diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index 9c321fba82..115efd40bb 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -94,6 +94,10 @@ void nvdisp_disp0::Composite(std::span sorted_layers Common::ADPF::BeginFrameWork(); } +void nvdisp_disp0::WaitForComposite() { + system.GPU().WaitForComposite(); +} + Kernel::KEvent* nvdisp_disp0::QueryEvent(u32 event_id) { LOG_CRITICAL(Service_NVDRV, "Unknown DISP Event {}", event_id); return nullptr; diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h index 1082b85c2b..13d2aaa445 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h @@ -37,6 +37,8 @@ public: /// Performs a screen flip, compositing each buffer. void Composite(std::span sorted_layers); + void WaitForComposite(); + Kernel::KEvent* QueryEvent(u32 event_id) override; private: diff --git a/src/core/hle/service/nvnflinger/hardware_composer.cpp b/src/core/hle/service/nvnflinger/hardware_composer.cpp index cfd999497b..0285b47137 100644 --- a/src/core/hle/service/nvnflinger/hardware_composer.cpp +++ b/src/core/hle/service/nvnflinger/hardware_composer.cpp @@ -57,6 +57,11 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display, // Set default speed limit to 100%. *out_speed_scale = 1.0f; + // The composition queued last frame reads guest memory on the GPU thread, so its framebuffers + // may only go back to the guest once it has run. + nvdisp.WaitForComposite(); + this->ReleaseFramebuffersLocked(display); + // If no layers are available, skip the logic. bool any_visible = false; for (auto& layer : display.stack.layers) { @@ -158,55 +163,30 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display, nvdisp.Composite(composition_stack); } - // Batch framebuffer releases, instead of one-into-one. - std::vector> to_release; - for (auto& [layer_id, framebuffer] : m_framebuffers) { - if (!framebuffer.is_acquired) - continue; - - auto layer = display.stack.FindLayer(layer_id); - if (!layer) - continue; - - // Overlay layers always release after every compose - // Non-overlay layers release based on their swap interval - if (layer->is_overlay || framebuffer.release_frame_number <= m_frame_number) { - to_release.emplace_back(layer.get(), &framebuffer); - } - } - for (auto& [layer, framebuffer] : to_release) { - layer->buffer_item_consumer->ReleaseBuffer(framebuffer->item, android::Fence::NoFence()); - framebuffer->is_acquired = false; - } - // Advance by 1 frame (60 FPS compositing) m_frame_number += 1; - // Release any necessary framebuffers (non-overlay layers only, as overlays are already released above). + return 1; +} + +void HardwareComposer::ReleaseFramebuffersLocked(Display& display) { for (auto& [layer_id, framebuffer] : m_framebuffers) { if (!framebuffer.is_acquired) { - // Already released. continue; } - if (framebuffer.release_frame_number > m_frame_number) { + const auto layer = display.stack.FindLayer(layer_id); + if (!layer) { continue; } - if (const auto layer = display.stack.FindLayer(layer_id); layer != nullptr) { - // Skip overlay layers as they were already released above - if (layer->is_overlay) { - continue; - } - - // TODO: support release fence - // This is needed to prevent screen tearing - layer->buffer_item_consumer->ReleaseBuffer(framebuffer.item, android::Fence::NoFence()); - framebuffer.is_acquired = false; + if (!layer->is_overlay && framebuffer.release_frame_number > m_frame_number) { + continue; } + + layer->buffer_item_consumer->ReleaseBuffer(framebuffer.item, android::Fence::NoFence()); + framebuffer.is_acquired = false; } - - return 1; } void HardwareComposer::RemoveLayerLocked(Display& display, ConsumerId consumer_id) { diff --git a/src/core/hle/service/nvnflinger/hardware_composer.h b/src/core/hle/service/nvnflinger/hardware_composer.h index e9b7194612..4727ab866a 100644 --- a/src/core/hle/service/nvnflinger/hardware_composer.h +++ b/src/core/hle/service/nvnflinger/hardware_composer.h @@ -52,6 +52,7 @@ private: private: bool TryAcquireFramebufferLocked(Layer& layer, Framebuffer& framebuffer); CacheStatus CacheFramebufferLocked(Layer& layer, ConsumerId consumer_id); + void ReleaseFramebuffersLocked(Display& display); }; } // namespace Service::Nvnflinger diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 12f37ced80..6973aa5b5c 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "common/assert.h" #include "common/settings.h" @@ -129,7 +130,7 @@ struct GPU::Impl { [[nodiscard]] u64 RequestSyncOperation(Func&& action) { std::unique_lock lck{sync_request_mutex}; const u64 fence = ++last_sync_fence; - sync_requests.emplace_back(action); + sync_requests.emplace_back(std::forward(action)); return fence; } @@ -232,9 +233,9 @@ struct GPU::Impl { } void RequestComposite(std::vector&& layers, std::vector&& fences) { - size_t num_fences{fences.size()}; + const size_t num_fences{fences.size()}; size_t current_request_counter{}; - { + if (num_fences != 0) { std::unique_lock lk(request_swap_mutex); if (free_swap_counters.empty()) { current_request_counter = request_swap_counters.size(); @@ -245,27 +246,41 @@ struct GPU::Impl { free_swap_counters.pop_front(); } } - const auto wait_fence = RequestSyncOperation([this, current_request_counter, &layers, &fences, num_fences] { - auto& syncpoint_manager = system.Host1x().GetSyncpointManager(); - if (num_fences == 0) { - renderer->Composite(layers); - } - const auto executer = [this, current_request_counter, layers_copy = layers]() { - { - std::unique_lock lk(request_swap_mutex); - if (--request_swap_counters[current_request_counter] != 0) { - return; - } - free_swap_counters.push_back(current_request_counter); + pending_composite_fence = RequestSyncOperation( + [this, current_request_counter, num_fences, layers = std::move(layers), + fences = std::move(fences)] { + if (num_fences == 0) { + renderer->Composite(layers); + return; } - renderer->Composite(layers_copy); - }; - for (size_t i = 0; i < num_fences; i++) { - syncpoint_manager.RegisterGuestAction(fences[i].id, fences[i].value, executer); - } - }); + auto& syncpoint_manager = system.Host1x().GetSyncpointManager(); + const auto executer = [this, current_request_counter, layers]() { + { + std::unique_lock lk(request_swap_mutex); + if (--request_swap_counters[current_request_counter] != 0) { + return; + } + free_swap_counters.push_back(current_request_counter); + } + renderer->Composite(layers); + }; + for (size_t i = 0; i < num_fences; i++) { + syncpoint_manager.RegisterGuestAction(fences[i].id, fences[i].value, executer); + } + }); gpu_thread.TickGPU(is_async); - WaitForSyncOperation(wait_fence); + } + + void WaitForComposite() { + const u64 fence = pending_composite_fence; + if (fence == 0) { + return; + } + pending_composite_fence = 0; + if (shutting_down.load(std::memory_order_relaxed)) { + return; + } + WaitForSyncOperation(fence); } std::vector GetAppletCaptureBuffer() { @@ -318,6 +333,7 @@ struct GPU::Impl { std::deque free_swap_counters; std::deque request_swap_counters; std::mutex request_swap_mutex; + u64 pending_composite_fence{}; }; GPU::GPU(Core::System& system, bool is_async, bool use_nvdec) @@ -435,6 +451,10 @@ void GPU::RequestComposite(std::vector&& layers, impl->RequestComposite(std::move(layers), std::move(fences)); } +void GPU::WaitForComposite() { + impl->WaitForComposite(); +} + std::vector GPU::GetAppletCaptureBuffer() { return impl->GetAppletCaptureBuffer(); } diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 538c4da85a..05bd720312 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -218,6 +218,8 @@ public: void RequestComposite(std::vector&& layers, std::vector&& fences); + void WaitForComposite(); + std::vector GetAppletCaptureBuffer(); /// Performs any additional setup necessary in order to begin GPU emulation.