diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index a4e63f555..dba69d6eb 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -296,7 +296,7 @@ static void ResetSubmissionLock(Platform::InterruptId irq) { cv_lock.notify_all(); } -void WaitGpuIdle() { +static void WaitGpuIdle() { HLE_TRACE; std::unique_lock lock{m_submission}; cv_lock.wait(lock, [] { return submission_lock == 0; }); diff --git a/src/core/libraries/videoout/driver.cpp b/src/core/libraries/videoout/driver.cpp index 5e5f72254..0e832f728 100644 --- a/src/core/libraries/videoout/driver.cpp +++ b/src/core/libraries/videoout/driver.cpp @@ -4,6 +4,7 @@ #include #include "common/assert.h" #include "common/debug.h" +#include "common/thread.h" #include "core/libraries/error_codes.h" #include "core/libraries/kernel/time_management.h" #include "core/libraries/videoout/driver.h" @@ -12,10 +13,6 @@ extern std::unique_ptr renderer; extern std::unique_ptr liverpool; -namespace Libraries::GnmDriver { -void WaitGpuIdle(); -} - namespace Libraries::VideoOut { constexpr static bool Is32BppPixelFormat(PixelFormat format) { @@ -160,8 +157,18 @@ int VideoOutDriver::UnregisterBuffers(VideoOutPort* port, s32 attributeIndex) { return ORBIS_OK; } -void VideoOutDriver::Flip(const Request& req) { - std::scoped_lock lock{mutex}; +std::chrono::microseconds VideoOutDriver::Flip(const Request& req) { + if (!req) { + return std::chrono::microseconds{0}; + } + + const auto start = std::chrono::high_resolution_clock::now(); + + // Whatever the game is rendering show splash if it is active + if (!renderer->ShowSplash(req.frame)) { + // Present the frame. + renderer->Present(req.frame); + } // Update flip status. auto* port = req.port; @@ -187,6 +194,9 @@ void VideoOutDriver::Flip(const Request& req) { port->buffer_labels[req.index] = 0; port->SignalVoLabel(); } + + const auto end = std::chrono::high_resolution_clock::now(); + return std::chrono::duration_cast(end - start); } bool VideoOutDriver::SubmitFlip(VideoOutPort* port, s32 index, s64 flip_arg, @@ -195,7 +205,7 @@ bool VideoOutDriver::SubmitFlip(VideoOutPort* port, s32 index, s64 flip_arg, if (!is_eop) { liverpool->SubmitDone(); - GnmDriver::WaitGpuIdle(); + liverpool->WaitGpuIdle(); } Vulkan::Frame* frame; @@ -246,31 +256,42 @@ 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; - } + static constexpr std::chrono::microseconds VblankPeriod{16683}; + Common::SetCurrentThreadName("VblankThread"); - // Retrieve the request. - req = requests.front(); + const auto receive_request = [this] -> Request { + std::scoped_lock lk{mutex}; + if (!requests.empty()) { + const auto request = requests.front(); requests.pop(); + return request; } + return {}; + }; - // Whatever the game is rendering show splash if it is active - if (!renderer->ShowSplash(req.frame)) { - // Present the frame. - renderer->Present(req.frame); + auto delay = std::chrono::microseconds{0}; + while (!token.stop_requested()) { + // Sleep for most of the vblank duration. + std::this_thread::sleep_for(VblankPeriod - delay); + + // Check if it's time to take a request. + auto& vblank_status = main_port.vblank_status; + if (vblank_status.count % (main_port.flip_rate + 1) == 0) { + const auto request = receive_request(); + delay = Flip(request); + FRAME_END; } + vblank_status.count++; + vblank_status.processTime = Libraries::Kernel::sceKernelGetProcessTime(); + vblank_status.tsc = Libraries::Kernel::sceKernelReadTsc(); - Flip(req); - Vblank(); - FRAME_END; + // Trigger flip events for the port. + for (auto& event : main_port.vblank_events) { + if (event != nullptr) { + event->TriggerEvent(SCE_VIDEO_OUT_EVENT_VBLANK, + Kernel::SceKernelEvent::Filter::VideoOut, nullptr); + } + } } } diff --git a/src/core/libraries/videoout/driver.h b/src/core/libraries/videoout/driver.h index 07bed31f6..7d8e546d4 100644 --- a/src/core/libraries/videoout/driver.h +++ b/src/core/libraries/videoout/driver.h @@ -94,9 +94,13 @@ private: s64 flip_arg; u64 submit_tsc; bool eop; + + operator bool() const noexcept { + return frame != nullptr; + } }; - void Flip(const Request& req); + std::chrono::microseconds Flip(const Request& req); void PresentThread(std::stop_token token); std::mutex mutex; diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 7cdf9a4f4..53769a880 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -65,6 +65,8 @@ void Liverpool::Process(std::stop_token stoken) { queue.submits.pop(); --num_submits; + std::scoped_lock lock2{submit_mutex}; + submit_cv.notify_all(); } } diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index 6923562ec..71eeb5be6 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -1002,6 +1002,11 @@ public: submit_cv.notify_one(); } + void WaitGpuIdle() noexcept { + std::unique_lock lk{submit_mutex}; + submit_cv.wait(lk, [this] { return num_submits == 0; }); + } + bool IsGpuIdle() const { return num_submits == 0; } diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 9bc9006d6..5442ed070 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -182,10 +182,11 @@ Frame* RendererVulkan::PrepareFrameInternal(VideoCore::Image& image, bool is_eop // Request a free presentation frame. Frame* frame = GetRenderFrame(); - // EOP flips are triggered from GPU thread to use the drawing scheduler to record + // EOP flips are triggered from GPU thread so use the drawing scheduler to record // commands. Otherwise we are dealing with a CPU flip which could have arrived // from any guest thread. Use a separate scheduler for that. auto& scheduler = is_eop ? draw_scheduler : flip_scheduler; + scheduler.EndRendering(); const auto cmdbuf = scheduler.CommandBuffer(); image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits::eTransferRead, diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index 7fffdeb2c..20c99e302 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -55,7 +55,7 @@ void Swapchain::Create(u32 width_, u32 height_, vk::SurfaceKHR surface_) { .pQueueFamilyIndices = queue_family_indices.data(), .preTransform = transform, .compositeAlpha = composite_alpha, - .presentMode = vk::PresentModeKHR::eFifo, + .presentMode = vk::PresentModeKHR::eMailbox, .clipped = true, .oldSwapchain = nullptr, };