diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index 866a96989..39fcc8125 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -2131,6 +2131,7 @@ int PS4_SYSV_ABI sceGnmSubmitDone() { if (!liverpool->IsGpuIdle()) { submission_lock = true; } + liverpool->SubmitDone(); send_init_packet = true; ++frames_submitted; return ORBIS_OK; diff --git a/src/core/libraries/videoout/driver.cpp b/src/core/libraries/videoout/driver.cpp index e74fb10f2..bc4031036 100644 --- a/src/core/libraries/videoout/driver.cpp +++ b/src/core/libraries/videoout/driver.cpp @@ -3,6 +3,7 @@ #include #include "common/assert.h" +#include "common/debug.h" #include "core/libraries/error_codes.h" #include "core/libraries/kernel/time_management.h" #include "core/libraries/videoout/driver.h" @@ -41,6 +42,7 @@ VideoOutDriver::VideoOutDriver(u32 width, u32 height) { main_port.resolution.fullHeight = height; main_port.resolution.paneWidth = width; main_port.resolution.paneHeight = height; + present_thread = std::jthread([&](std::stop_token token) { PresentThread(token); }); } VideoOutDriver::~VideoOutDriver() = default; @@ -158,27 +160,7 @@ int VideoOutDriver::UnregisterBuffers(VideoOutPort* port, s32 attributeIndex) { return ORBIS_OK; } -void VideoOutDriver::Flip(std::chrono::microseconds timeout) { - Request req; - { - std::unique_lock lock{mutex}; - submit_cond.wait_for(lock, timeout, [&] { return !requests.empty(); }); - if (requests.empty()) { - renderer->ShowSplash(); - return; - } - - // Retrieve the request. - req = requests.front(); - requests.pop(); - } - - // Whatever the game is rendering show splash if it is active - if (!renderer->ShowSplash(req.frame)) { - // Present the frame. - renderer->Present(req.frame); - } - +void VideoOutDriver::Flip(const Request& req) { std::scoped_lock lock{mutex}; // Update flip status. @@ -256,4 +238,33 @@ void VideoOutDriver::Vblank() { } } +void VideoOutDriver::PresentThread(std::stop_token token) { + static constexpr std::chrono::milliseconds FlipPeriod{16}; + while (!token.stop_requested()) { + Request req; + { + std::unique_lock lock{mutex}; + submit_cond.wait_for(lock, FlipPeriod, [&] { return !requests.empty(); }); + if (requests.empty()) { + renderer->ShowSplash(); + continue; + } + + // Retrieve the request. + req = requests.front(); + requests.pop(); + } + + // Whatever the game is rendering show splash if it is active + if (!renderer->ShowSplash(req.frame)) { + // Present the frame. + renderer->Present(req.frame); + } + + Flip(req); + Vblank(); + FRAME_END; + } +} + } // namespace Libraries::VideoOut diff --git a/src/core/libraries/videoout/driver.h b/src/core/libraries/videoout/driver.h index d98e62eea..72f2be153 100644 --- a/src/core/libraries/videoout/driver.h +++ b/src/core/libraries/videoout/driver.h @@ -6,6 +6,7 @@ #include #include #include +#include "common/polyfill_thread.h" #include "core/libraries/videoout/video_out.h" namespace Vulkan { @@ -63,7 +64,6 @@ public: const BufferAttribute* attribute); int UnregisterBuffers(VideoOutPort* port, s32 attributeIndex); - void Flip(std::chrono::microseconds timeout); bool SubmitFlip(VideoOutPort* port, s32 index, s64 flip_arg, bool is_eop = false); void Vblank(); @@ -78,10 +78,14 @@ private: bool eop; }; + void Flip(const Request& req); + void PresentThread(std::stop_token token); + std::mutex mutex; VideoOutPort main_port{}; std::condition_variable_any submit_cond; std::condition_variable done_cond; + std::jthread present_thread; std::queue requests; bool is_neo{}; }; diff --git a/src/core/libraries/videoout/video_out.cpp b/src/core/libraries/videoout/video_out.cpp index 8fbd69c4d..2f552367d 100644 --- a/src/core/libraries/videoout/video_out.cpp +++ b/src/core/libraries/videoout/video_out.cpp @@ -141,10 +141,12 @@ s32 PS4_SYSV_ABI sceVideoOutSubmitFlip(s32 handle, s32 bufferIndex, s32 flipMode LOG_INFO(Lib_VideoOut, "bufferIndex = {}, flipMode = {}, flipArg = {}", bufferIndex, flipMode, flipArg); - if (!driver->SubmitFlip(port, bufferIndex, flipArg)) { - LOG_ERROR(Lib_VideoOut, "Flip queue is full"); - return ORBIS_VIDEO_OUT_ERROR_FLIP_QUEUE_FULL; - } + // Next time the gpu enters idle state, submit the flip + Platform::IrqC::Instance()->RegisterOnce( + Platform::InterruptId::GpuIdle, [=](Platform::InterruptId irq) { + const auto result = driver->SubmitFlip(port, bufferIndex, flipArg); + ASSERT_MSG(result, "Flip submission failed"); + }); return ORBIS_OK; } @@ -229,14 +231,6 @@ s32 PS4_SYSV_ABI sceVideoOutUnregisterBuffers(s32 handle, s32 attributeIndex) { return driver->UnregisterBuffers(port, attributeIndex); } -void Flip(std::chrono::microseconds micros) { - return driver->Flip(micros); -} - -void Vblank() { - return driver->Vblank(); -} - void sceVideoOutGetBufferLabelAddress(s32 handle, uintptr_t* label_addr) { auto* port = driver->GetPort(handle); ASSERT(port); diff --git a/src/core/libraries/videoout/video_out.h b/src/core/libraries/videoout/video_out.h index 52426eccb..843a09603 100644 --- a/src/core/libraries/videoout/video_out.h +++ b/src/core/libraries/videoout/video_out.h @@ -104,9 +104,6 @@ s32 PS4_SYSV_ABI sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 i const void* param); s32 PS4_SYSV_ABI sceVideoOutClose(s32 handle); -void Flip(std::chrono::microseconds micros); -void Vblank(); - // Internal system functions void sceVideoOutGetBufferLabelAddress(s32 handle, uintptr_t* label_addr); s32 sceVideoOutSubmitEopFlip(s32 handle, u32 buf_id, u32 mode, u32 arg, void** unk); diff --git a/src/emulator.cpp b/src/emulator.cpp index 5e584eee9..5dd125df0 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -136,14 +136,8 @@ void Emulator::Run(const std::filesystem::path& file) { std::jthread mainthread = std::jthread([this](std::stop_token stop_token) { linker->Execute(); }); - // Begin main window loop until the application exits - static constexpr std::chrono::milliseconds FlipPeriod{16}; - while (window->isOpen()) { window->waitEvent(); - Libraries::VideoOut::Flip(FlipPeriod); - Libraries::VideoOut::Vblank(); - FRAME_END; } std::exit(0); diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index c4fcbcfae..76dca9e2a 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -71,7 +71,7 @@ void WindowSDL::waitEvent() { // Called on main thread SDL_Event event; - if (!SDL_PollEvent(&event)) { + if (!SDL_WaitEvent(&event)) { return; } diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index ab7ad2415..1801b7d3d 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -32,7 +32,7 @@ void Liverpool::Process(std::stop_token stoken) { while (!stoken.stop_requested()) { { std::unique_lock lk{submit_mutex}; - Common::CondvarWait(submit_cv, lk, stoken, [this] { return num_submits != 0; }); + Common::CondvarWait(submit_cv, lk, stoken, [this] { return num_submits != 0 || submit_done; }); } if (stoken.stop_requested()) { break; @@ -67,6 +67,13 @@ void Liverpool::Process(std::stop_token stoken) { } } + if (submit_done) { + if (rasterizer) { + rasterizer->Flush(); + } + submit_done = false; + } + Platform::IrqC::Instance()->Signal(Platform::InterruptId::GpuIdle); } } diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index b87c80ed9..d834a6f43 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -991,6 +991,12 @@ public: void SubmitGfx(std::span dcb, std::span ccb); void SubmitAsc(u32 vqid, std::span acb); + void SubmitDone() noexcept { + std::scoped_lock lk{submit_mutex}; + submit_done = true; + submit_cv.notify_one(); + } + bool IsGpuIdle() const { return num_submits == 0; } @@ -1061,6 +1067,7 @@ private: Vulkan::Rasterizer* rasterizer{}; std::jthread process_thread{}; std::atomic num_submits{}; + std::atomic submit_done{}; std::mutex submit_mutex; std::condition_variable_any submit_cv; }; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index fe52d0741..ff800facc 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -96,6 +96,10 @@ void Rasterizer::DispatchDirect() { cmdbuf.dispatch(cs_program.dim_x, cs_program.dim_y, cs_program.dim_z); } +void Rasterizer::Flush() { + scheduler.Flush(); +} + void Rasterizer::BeginRendering() { const auto& regs = liverpool->regs; RenderState state; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index aead5955d..c5c6e3a7f 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -35,6 +35,8 @@ public: void ScopeMarkerBegin(const std::string& str); void ScopeMarkerEnd(); + + void Flush(); private: u32 SetupIndexBuffer(bool& is_indexed, u32 index_offset);