// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include "common/debug.h" #include "common/polyfill_thread.h" #include "core/libraries/videoout/video_out.h" #include #include #include namespace Vulkan { struct Frame; } namespace Libraries::VideoOut { struct VideoOutPort { bool is_open = false; SceVideoOutResolutionStatus resolution; std::array buffer_slots; std::array buffer_labels; // should be contiguous in memory static_assert(sizeof(buffer_labels[0]) == 8u); std::array groups; FlipStatus flip_status; SceVideoOutVblankStatus vblank_status; std::vector flip_events; std::vector vblank_events; std::mutex vo_mutex; std::mutex port_mutex; std::condition_variable vo_cv; std::condition_variable vblank_cv; int flip_rate = 0; s32 FindFreeGroup() const { s32 index = 0; while (index < groups.size() && groups[index].is_occupied) { index++; } return index; } bool IsVoLabel(const u64* address) const { const u64* start = &buffer_labels[0]; const u64* end = &buffer_labels[MaxDisplayBuffers - 1]; return address >= start && address <= end; } void WaitVoLabel(auto&& pred) { std::unique_lock lk{vo_mutex}; vo_cv.wait(lk, pred); } void SignalVoLabel() { std::scoped_lock lk{vo_mutex}; vo_cv.notify_one(); } [[nodiscard]] int NumRegisteredBuffers() const { return std::count_if(buffer_slots.cbegin(), buffer_slots.cend(), [](auto& buffer) { return buffer.group_index != -1; }); } }; struct ServiceThreadParams { u32 unknown; bool set_priority; u32 priority; bool set_affinity; u64 affinity; }; class VideoOutDriver { public: explicit VideoOutDriver(u32 width, u32 height); ~VideoOutDriver(); int Open(const ServiceThreadParams* params); void Close(s32 handle); VideoOutPort* GetPort(s32 handle); int RegisterBuffers(VideoOutPort* port, s32 startIndex, void* const* addresses, s32 bufferNum, const BufferAttribute* attribute); int UnregisterBuffers(VideoOutPort* port, s32 attributeIndex); bool SubmitFlip(VideoOutPort* port, s32 index, s64 flip_arg, bool is_eop = false); private: struct Request { Vulkan::Frame* frame; VideoOutPort* port; s32 index; s64 flip_arg; bool eop; operator bool() const noexcept { return frame != nullptr; } }; std::chrono::microseconds Flip(const Request& req); void SubmitFlipInternal(VideoOutPort* port, s32 index, s64 flip_arg, bool is_eop = false); void PresentThread(std::stop_token token); std::mutex mutex; VideoOutPort main_port{}; std::jthread present_thread; std::queue requests; }; } // namespace Libraries::VideoOut