[gpu] Adjustments on the frame composer

This commit is contained in:
CamilleLaVey
2026-08-05 18:18:29 -04:00
committed by crueter
parent 41cd784835
commit 63e33f37f6
6 changed files with 67 additions and 58 deletions
@@ -94,6 +94,10 @@ void nvdisp_disp0::Composite(std::span<const Nvnflinger::HwcLayer> sorted_layers
Common::ADPF::BeginFrameWork(); Common::ADPF::BeginFrameWork();
} }
void nvdisp_disp0::WaitForComposite() {
system.GPU().WaitForComposite();
}
Kernel::KEvent* nvdisp_disp0::QueryEvent(u32 event_id) { Kernel::KEvent* nvdisp_disp0::QueryEvent(u32 event_id) {
LOG_CRITICAL(Service_NVDRV, "Unknown DISP Event {}", event_id); LOG_CRITICAL(Service_NVDRV, "Unknown DISP Event {}", event_id);
return nullptr; return nullptr;
@@ -37,6 +37,8 @@ public:
/// Performs a screen flip, compositing each buffer. /// Performs a screen flip, compositing each buffer.
void Composite(std::span<const Nvnflinger::HwcLayer> sorted_layers); void Composite(std::span<const Nvnflinger::HwcLayer> sorted_layers);
void WaitForComposite();
Kernel::KEvent* QueryEvent(u32 event_id) override; Kernel::KEvent* QueryEvent(u32 event_id) override;
private: private:
@@ -57,6 +57,11 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display,
// Set default speed limit to 100%. // Set default speed limit to 100%.
*out_speed_scale = 1.0f; *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. // If no layers are available, skip the logic.
bool any_visible = false; bool any_visible = false;
for (auto& layer : display.stack.layers) { for (auto& layer : display.stack.layers) {
@@ -158,55 +163,30 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display,
nvdisp.Composite(composition_stack); nvdisp.Composite(composition_stack);
} }
// Batch framebuffer releases, instead of one-into-one.
std::vector<std::pair<Layer*, Framebuffer*>> 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) // Advance by 1 frame (60 FPS compositing)
m_frame_number += 1; 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) { for (auto& [layer_id, framebuffer] : m_framebuffers) {
if (!framebuffer.is_acquired) { if (!framebuffer.is_acquired) {
// Already released.
continue; continue;
} }
if (framebuffer.release_frame_number > m_frame_number) { const auto layer = display.stack.FindLayer(layer_id);
if (!layer) {
continue; continue;
} }
if (const auto layer = display.stack.FindLayer(layer_id); layer != nullptr) { if (!layer->is_overlay && framebuffer.release_frame_number > m_frame_number) {
// Skip overlay layers as they were already released above continue;
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;
} }
layer->buffer_item_consumer->ReleaseBuffer(framebuffer.item, android::Fence::NoFence());
framebuffer.is_acquired = false;
} }
return 1;
} }
void HardwareComposer::RemoveLayerLocked(Display& display, ConsumerId consumer_id) { void HardwareComposer::RemoveLayerLocked(Display& display, ConsumerId consumer_id) {
@@ -52,6 +52,7 @@ private:
private: private:
bool TryAcquireFramebufferLocked(Layer& layer, Framebuffer& framebuffer); bool TryAcquireFramebufferLocked(Layer& layer, Framebuffer& framebuffer);
CacheStatus CacheFramebufferLocked(Layer& layer, ConsumerId consumer_id); CacheStatus CacheFramebufferLocked(Layer& layer, ConsumerId consumer_id);
void ReleaseFramebuffersLocked(Display& display);
}; };
} // namespace Service::Nvnflinger } // namespace Service::Nvnflinger
+42 -22
View File
@@ -10,6 +10,7 @@
#include <condition_variable> #include <condition_variable>
#include <list> #include <list>
#include <memory> #include <memory>
#include <utility>
#include "common/assert.h" #include "common/assert.h"
#include "common/settings.h" #include "common/settings.h"
@@ -129,7 +130,7 @@ struct GPU::Impl {
[[nodiscard]] u64 RequestSyncOperation(Func&& action) { [[nodiscard]] u64 RequestSyncOperation(Func&& action) {
std::unique_lock lck{sync_request_mutex}; std::unique_lock lck{sync_request_mutex};
const u64 fence = ++last_sync_fence; const u64 fence = ++last_sync_fence;
sync_requests.emplace_back(action); sync_requests.emplace_back(std::forward<Func>(action));
return fence; return fence;
} }
@@ -232,9 +233,9 @@ struct GPU::Impl {
} }
void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers, std::vector<Service::Nvidia::NvFence>&& fences) { void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers, std::vector<Service::Nvidia::NvFence>&& fences) {
size_t num_fences{fences.size()}; const size_t num_fences{fences.size()};
size_t current_request_counter{}; size_t current_request_counter{};
{ if (num_fences != 0) {
std::unique_lock<std::mutex> lk(request_swap_mutex); std::unique_lock<std::mutex> lk(request_swap_mutex);
if (free_swap_counters.empty()) { if (free_swap_counters.empty()) {
current_request_counter = request_swap_counters.size(); current_request_counter = request_swap_counters.size();
@@ -245,27 +246,41 @@ struct GPU::Impl {
free_swap_counters.pop_front(); free_swap_counters.pop_front();
} }
} }
const auto wait_fence = RequestSyncOperation([this, current_request_counter, &layers, &fences, num_fences] { pending_composite_fence = RequestSyncOperation(
auto& syncpoint_manager = system.Host1x().GetSyncpointManager(); [this, current_request_counter, num_fences, layers = std::move(layers),
if (num_fences == 0) { fences = std::move(fences)] {
renderer->Composite(layers); if (num_fences == 0) {
} renderer->Composite(layers);
const auto executer = [this, current_request_counter, layers_copy = layers]() { return;
{
std::unique_lock<std::mutex> lk(request_swap_mutex);
if (--request_swap_counters[current_request_counter] != 0) {
return;
}
free_swap_counters.push_back(current_request_counter);
} }
renderer->Composite(layers_copy); auto& syncpoint_manager = system.Host1x().GetSyncpointManager();
}; const auto executer = [this, current_request_counter, layers]() {
for (size_t i = 0; i < num_fences; i++) { {
syncpoint_manager.RegisterGuestAction(fences[i].id, fences[i].value, executer); std::unique_lock<std::mutex> 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); 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<u8> GetAppletCaptureBuffer() { std::vector<u8> GetAppletCaptureBuffer() {
@@ -318,6 +333,7 @@ struct GPU::Impl {
std::deque<size_t> free_swap_counters; std::deque<size_t> free_swap_counters;
std::deque<size_t> request_swap_counters; std::deque<size_t> request_swap_counters;
std::mutex request_swap_mutex; std::mutex request_swap_mutex;
u64 pending_composite_fence{};
}; };
GPU::GPU(Core::System& system, bool is_async, bool use_nvdec) GPU::GPU(Core::System& system, bool is_async, bool use_nvdec)
@@ -435,6 +451,10 @@ void GPU::RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers,
impl->RequestComposite(std::move(layers), std::move(fences)); impl->RequestComposite(std::move(layers), std::move(fences));
} }
void GPU::WaitForComposite() {
impl->WaitForComposite();
}
std::vector<u8> GPU::GetAppletCaptureBuffer() { std::vector<u8> GPU::GetAppletCaptureBuffer() {
return impl->GetAppletCaptureBuffer(); return impl->GetAppletCaptureBuffer();
} }
+2
View File
@@ -218,6 +218,8 @@ public:
void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers, void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers,
std::vector<Service::Nvidia::NvFence>&& fences); std::vector<Service::Nvidia::NvFence>&& fences);
void WaitForComposite();
std::vector<u8> GetAppletCaptureBuffer(); std::vector<u8> GetAppletCaptureBuffer();
/// Performs any additional setup necessary in order to begin GPU emulation. /// Performs any additional setup necessary in order to begin GPU emulation.