From e3ad859786a8a7f04247cee63026fc98281caf79 Mon Sep 17 00:00:00 2001 From: SDK Chan Date: Tue, 16 Sep 2025 19:00:23 +0000 Subject: [PATCH 1/9] [core/nvnflinger] Rewrite GetBufferHistory --- .../nvnflinger/buffer_queue_consumer.cpp | 17 +-- .../service/nvnflinger/buffer_queue_core.cpp | 18 ++- .../service/nvnflinger/buffer_queue_core.h | 26 ++-- .../nvnflinger/buffer_queue_producer.cpp | 125 ++++++++++-------- .../nvnflinger/buffer_queue_producer.h | 1 + src/core/hle/service/nvnflinger/buffer_slot.h | 2 +- 6 files changed, 109 insertions(+), 80 deletions(-) diff --git a/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp index a9b0f9d2f3..88eb279151 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp +++ b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp @@ -1,3 +1,4 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -97,18 +98,18 @@ Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer, slots[slot].needs_cleanup_on_release = false; slots[slot].buffer_state = BufferState::Acquired; + // Mark tracked buffer history records as acquired + for (auto& buffer_history_record : core->buffer_history) { + if (buffer_history_record.frame_number == core->frame_counter) { + buffer_history_record.state = BufferState::Acquired; + break; + } + } + // TODO: for now, avoid resetting the fence, so that when we next return this // slot to the producer, it will wait for the fence to pass. We should fix this // by properly waiting for the fence in the BufferItemConsumer. // slots[slot].fence = Fence::NoFence(); - - const auto target_frame_number = slots[slot].frame_number; - for (size_t i = 0; i < core->history.size(); i++) { - if (core->history[i].frame_number == target_frame_number) { - core->history[i].state = BufferState::Acquired; - break; - } - } } // If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to diff --git a/src/core/hle/service/nvnflinger/buffer_queue_core.cpp b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp index 27ac930f96..9975ca3ccd 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_core.cpp +++ b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp @@ -1,3 +1,4 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -10,12 +11,19 @@ namespace Service::android { -BufferQueueCore::BufferQueueCore() { - history.resize(8); -}; - +BufferQueueCore::BufferQueueCore() = default; BufferQueueCore::~BufferQueueCore() = default; +void BufferQueueCore::PushHistory(u64 frame_number, s64 queue_time, s64 presentation_time, BufferState state) { + buffer_history_pos = (buffer_history_pos + 1) % BUFFER_HISTORY_SIZE; + buffer_history[buffer_history_pos] = BufferHistoryInfo{ + .frame_number = frame_number, + .queue_time = queue_time, + .presentation_time = presentation_time, + .state = state, + }; +} + void BufferQueueCore::SignalDequeueCondition() { dequeue_possible.store(true); dequeue_condition.notify_all(); @@ -47,7 +55,7 @@ s32 BufferQueueCore::GetMinMaxBufferCountLocked(bool async) const { s32 BufferQueueCore::GetMaxBufferCountLocked(bool async) const { const auto min_buffer_count = GetMinMaxBufferCountLocked(async); - auto max_buffer_count = (std::max)(default_max_buffer_count, min_buffer_count); + auto max_buffer_count = std::max(default_max_buffer_count, min_buffer_count); if (override_max_buffer_count != 0) { ASSERT(override_max_buffer_count >= min_buffer_count); diff --git a/src/core/hle/service/nvnflinger/buffer_queue_core.h b/src/core/hle/service/nvnflinger/buffer_queue_core.h index 341634352b..feedfb0c93 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_core.h +++ b/src/core/hle/service/nvnflinger/buffer_queue_core.h @@ -1,3 +1,4 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -15,6 +16,7 @@ #include "core/hle/service/nvnflinger/buffer_item.h" #include "core/hle/service/nvnflinger/buffer_queue_defs.h" +#include "core/hle/service/nvnflinger/buffer_slot.h" #include "core/hle/service/nvnflinger/pixel_format.h" #include "core/hle/service/nvnflinger/status.h" #include "core/hle/service/nvnflinger/window.h" @@ -23,22 +25,19 @@ namespace Service::android { #ifdef _MSC_VER #pragma pack(push, 1) +struct BufferHistoryInfo { +#elif defined(__GNUC__) || defined(__clang__) +struct __attribute__((packed)) BufferHistoryInfo { #endif -struct BufferInfo { u64 frame_number; s64 queue_time; - s64 presentation_time{}; - BufferState state{BufferState::Free}; -} -#if defined(__GNUC__) || defined(__clang__) -__attribute__((packed)) -#endif -; + s64 presentation_time; + BufferState state; +}; #ifdef _MSC_VER #pragma pack(pop) #endif -static_assert(sizeof(BufferInfo) == 0x1C, - "BufferInfo is an invalid size"); +static_assert(sizeof(BufferHistoryInfo) == 0x1C, "BufferHistoryInfo must be 28 bytes"); class IConsumerListener; class IProducerListener; @@ -49,10 +48,13 @@ class BufferQueueCore final { public: static constexpr s32 INVALID_BUFFER_SLOT = BufferItem::INVALID_BUFFER_SLOT; + static constexpr u32 BUFFER_HISTORY_SIZE = 8; BufferQueueCore(); ~BufferQueueCore(); + void PushHistory(u64 frame_number, s64 queue_time, s64 presentation_time, BufferState state); + private: void SignalDequeueCondition(); bool WaitForDequeueCondition(std::unique_lock& lk); @@ -88,11 +90,11 @@ private: const s32 max_acquired_buffer_count{}; // This is always zero on HOS bool buffer_has_been_queued{}; u64 frame_counter{}; + std::array buffer_history{}; + u32 buffer_history_pos{BUFFER_HISTORY_SIZE-1}; u32 transform_hint{}; bool is_allocating{}; mutable std::condition_variable_any is_allocating_condition; - - std::vector history{8}; }; } // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp index f9e1dba965..57fc65ab52 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp +++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp @@ -1,3 +1,4 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -75,7 +76,7 @@ Status BufferQueueProducer::SetBufferCount(s32 buffer_count) { return Status::BadValue; } - // There must be no dequeued buffers when changing the buffer count. + // There must be no dequeued buffers when changing the buffer count. for (s32 s{}; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { if (slots[s].buffer_state == BufferState::Dequeued) { LOG_ERROR(Service_Nvnflinger, "buffer owned by producer"); @@ -96,8 +97,8 @@ Status BufferQueueProducer::SetBufferCount(s32 buffer_count) { return Status::BadValue; } - // Here we are guaranteed that the producer doesn't have any dequeued buffers and will - // release all of its buffer references. + // Here we are guaranteed that the producer doesn't have any dequeued buffers and will + // release all of its buffer references. if (core->GetPreallocatedBufferCountLocked() <= 0) { core->FreeAllBuffersLocked(); } @@ -108,7 +109,7 @@ Status BufferQueueProducer::SetBufferCount(s32 buffer_count) { listener = core->consumer_listener; } - // Call back without lock held + // Call back without lock held if (listener != nullptr) { listener->OnBuffersReleased(); } @@ -134,7 +135,7 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, St } } - // Free up any buffers that are in slots beyond the max buffer count + // Free up any buffers that are in slots beyond the max buffer count for (s32 s = max_buffer_count; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { ASSERT(slots[s].buffer_state == BufferState::Free); if (slots[s].graphic_buffer != nullptr && slots[s].buffer_state == BufferState::Free && @@ -144,7 +145,7 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, St } } - // Look for a free buffer to give to the client + // Look for a free buffer to give to the client *found = BufferQueueCore::INVALID_BUFFER_SLOT; s32 dequeued_count{}; s32 acquired_count{}; @@ -169,16 +170,16 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, St } } - // Producers are not allowed to dequeue more than one buffer if they did not set a buffer - // count + // Producers are not allowed to dequeue more than one buffer if they did not set a buffer + // count if (!core->override_max_buffer_count && dequeued_count) { LOG_ERROR(Service_Nvnflinger, "can't dequeue multiple buffers without setting the buffer count"); return Status::InvalidOperation; } - // See whether a buffer has been queued since the last SetBufferCount so we know whether to - // perform the min undequeued buffers check below + // See whether a buffer has been queued since the last SetBufferCount so we know whether to + // perform the min undequeued buffers check below if (core->buffer_has_been_queued) { // Make sure the producer is not trying to dequeue more buffers than allowed const s32 new_undequeued_count = max_buffer_count - (dequeued_count + 1); @@ -191,16 +192,16 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, St } } - // If we disconnect and reconnect quickly, we can be in a state where our slots are empty - // but we have many buffers in the queue. This can cause us to run out of memory if we - // outrun the consumer. Wait here if it looks like we have too many buffers queued up. + // If we disconnect and reconnect quickly, we can be in a state where our slots are empty + // but we have many buffers in the queue. This can cause us to run out of memory if we + // outrun the consumer. Wait here if it looks like we have too many buffers queued up. const bool too_many_buffers = core->queue.size() > static_cast(max_buffer_count); if (too_many_buffers) { LOG_ERROR(Service_Nvnflinger, "queue size is {}, waiting", core->queue.size()); } - // If no buffer is found, or if the queue has too many buffers outstanding, wait for a - // buffer to be acquired or released, or for the max buffer count to change. + // If no buffer is found, or if the queue has too many buffers outstanding, wait for a + // buffer to be acquired or released, or for the max buffer count to change. try_again = (*found == BufferQueueCore::INVALID_BUFFER_SLOT) || too_many_buffers; if (try_again) { // Return an error if we're in non-blocking mode (producer and consumer are controlled @@ -240,7 +241,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool format = core->default_buffer_format; } - // Enable the usage bits the consumer requested + // Enable the usage bits the consumer requested usage |= core->consumer_usage_bit; s32 found{}; @@ -249,7 +250,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool return status; } - // This should not happen + // This should not happen if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { LOG_ERROR(Service_Nvnflinger, "no available buffer slots"); return Status::Busy; @@ -361,7 +362,7 @@ Status BufferQueueProducer::DetachNextBuffer(std::shared_ptr* out return Status::NoInit; } - // Find the oldest valid slot + // Find the oldest valid slot int found = BufferQueueCore::INVALID_BUFFER_SLOT; for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { if (slots[s].buffer_state == BufferState::Free && slots[s].graphic_buffer != nullptr) { @@ -407,7 +408,7 @@ Status BufferQueueProducer::AttachBuffer(s32* out_slot, return status; } - // This should not happen + // This should not happen if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { LOG_ERROR(Service_Nvnflinger, "No available buffer slots"); return Status::Busy; @@ -530,11 +531,6 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, item.is_droppable = core->dequeue_buffer_cannot_block || async; item.swap_interval = swap_interval; - position = (position + 1) % 8; - core->history[position] = {.frame_number = core->frame_counter, - .queue_time = slots[slot].queue_time, - .state = BufferState::Queued}; - sticky_transform = sticky_transform_; if (core->queue.empty()) { @@ -551,8 +547,17 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, // mark it as freed if (core->StillTracking(*front)) { slots[front->slot].buffer_state = BufferState::Free; - // Reset the frame number of the freed buffer so that it is the first in line to - // be dequeued again + + // Mark tracked buffer history records as free + for (auto& buffer_history_record : core->buffer_history) { + if (buffer_history_record.frame_number == front->frame_number) { + buffer_history_record.state = BufferState::Free; + break; + } + } + + // Reset the frame number of the freed buffer so that it is the first in line to + // be dequeued again slots[front->slot].frame_number = 0; } // Overwrite the droppable buffer with the incoming one @@ -564,22 +569,23 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, } } + core->PushHistory(core->frame_counter, slots[slot].queue_time, slots[slot].presentation_time, BufferState::Queued); core->buffer_has_been_queued = true; core->SignalDequeueCondition(); output->Inflate(core->default_width, core->default_height, core->transform_hint, static_cast(core->queue.size())); - // Take a ticket for the callback functions + // Take a ticket for the callback functions callback_ticket = next_callback_ticket++; } - // Don't send the GraphicBuffer through the callback, and don't send the slot number, since the - // consumer shouldn't need it + // Don't send the GraphicBuffer through the callback, and don't send the slot number, since the + // consumer shouldn't need it item.graphic_buffer.reset(); item.slot = BufferItem::INVALID_BUFFER_SLOT; - // Call back without the main BufferQueue lock held, but with the callback lock held so we can - // ensure that callbacks occur in order + // Call back without the main BufferQueue lock held, but with the callback lock held so we can + // ensure that callbacks occur in order { std::scoped_lock lock{callback_mutex}; while (callback_ticket != current_callback_ticket) { @@ -797,8 +803,8 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot, slots[slot].graphic_buffer = std::make_shared(nvmap, buffer); slots[slot].frame_number = 0; - // Most games preallocate a buffer and pass a valid buffer here. However, it is possible for - // this to be called with an empty buffer, Naruto Ultimate Ninja Storm is a game that does this. + // Most games preallocate a buffer and pass a valid buffer here. However, it is possible for + // this to be called with an empty buffer, Naruto Ultimate Ninja Storm is a game that does this. if (buffer) { slots[slot].is_preallocated = true; @@ -938,33 +944,46 @@ void BufferQueueProducer::Transact(u32 code, std::span parcel_data, break; } case TransactionId::GetBufferHistory: { - LOG_WARNING(Service_Nvnflinger, "called, transaction=GetBufferHistory"); + LOG_DEBUG(Service_Nvnflinger, "called, transaction=GetBufferHistory"); - std::scoped_lock lock{core->mutex}; - - auto buffer_history_count = (std::min)(parcel_in.Read(), (s32)core->history.size()); - - if (buffer_history_count <= 0) { + const s32 request = parcel_in.Read(); + if (request <= 0) { parcel_out.Write(Status::BadValue); parcel_out.Write(0); - status = Status::None; break; } - auto info = new BufferInfo[buffer_history_count]; - auto pos = position; - for (int i = 0; i < buffer_history_count; i++) { - info[i] = core->history[(pos - i) % core->history.size()]; - LOG_WARNING(Service_Nvnflinger, "frame_number={}, state={}", - core->history[(pos - i) % core->history.size()].frame_number, - (u32)core->history[(pos - i) % core->history.size()].state); - pos--; + constexpr u32 history_max = BufferQueueCore::BUFFER_HISTORY_SIZE; + std::array buffer_history_snapshot{}; + s32 valid_index{}; + { + std::scoped_lock lk(core->mutex); + + const u32 current_history_pos = core->buffer_history_pos; + u32 index_reversed{}; + for (u32 i = 0; i < history_max; ++i) { + // Wrap values backwards e.g. 7, 6, 5, etc. in the range of 0-7 + index_reversed = (current_history_pos + history_max - i) % history_max; + const auto& current_history_buffer = core->buffer_history[index_reversed]; + + // Here we use the frame number as a terminator. + // Because a buffer without frame_number is not considered complete + if (current_history_buffer.frame_number == 0) { + break; + } + + buffer_history_snapshot[valid_index] = current_history_buffer; + ++valid_index; + } } + const s32 limit = std::min(request, valid_index); parcel_out.Write(Status::NoError); - parcel_out.Write(buffer_history_count); - parcel_out.WriteFlattenedObject(info); - status = Status::None; + parcel_out.Write(limit); + for (s32 i = 0; i < limit; ++i) { + parcel_out.Write(buffer_history_snapshot[i]); + } + break; } default: @@ -972,9 +991,7 @@ void BufferQueueProducer::Transact(u32 code, std::span parcel_data, break; } - if (status != Status::None) { - parcel_out.Write(status); - } + parcel_out.Write(status); const auto serialized = parcel_out.Serialize(); std::memcpy(parcel_reply.data(), serialized.data(), diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.h b/src/core/hle/service/nvnflinger/buffer_queue_producer.h index 28195cd3c5..5908a119a2 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_producer.h +++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.h @@ -1,3 +1,4 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/nvnflinger/buffer_slot.h b/src/core/hle/service/nvnflinger/buffer_slot.h index 5b5cbb6fbd..02a696f7da 100644 --- a/src/core/hle/service/nvnflinger/buffer_slot.h +++ b/src/core/hle/service/nvnflinger/buffer_slot.h @@ -15,7 +15,7 @@ namespace Service::android { class GraphicBuffer; -enum class BufferState : s32 { +enum class BufferState : u32 { Free = 0, Dequeued = 1, Queued = 2, -- 2.39.5 From 505bce69b581422bc0844777749dac1015f579ee Mon Sep 17 00:00:00 2001 From: SDK-Chan Date: Tue, 16 Sep 2025 19:16:47 +0200 Subject: [PATCH 2/9] Update src/core/hle/service/nvnflinger/buffer_slot.h --- src/core/hle/service/nvnflinger/buffer_slot.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/hle/service/nvnflinger/buffer_slot.h b/src/core/hle/service/nvnflinger/buffer_slot.h index 02a696f7da..f815532563 100644 --- a/src/core/hle/service/nvnflinger/buffer_slot.h +++ b/src/core/hle/service/nvnflinger/buffer_slot.h @@ -1,3 +1,4 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project // SPDX-License-Identifier: GPL-3.0-or-later -- 2.39.5 From 9e9bac1cb66dcdd1128d2f6fd85daff892cf4813 Mon Sep 17 00:00:00 2001 From: SDK-Chan Date: Tue, 16 Sep 2025 19:29:12 +0200 Subject: [PATCH 3/9] Update src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp --- src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp index 88eb279151..2913d25819 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp +++ b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp @@ -1,4 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project // SPDX-License-Identifier: GPL-3.0-or-later -- 2.39.5 From db774e6cef7dca0bd647ef8c98dc53f247af17e0 Mon Sep 17 00:00:00 2001 From: SDK-Chan Date: Tue, 16 Sep 2025 19:29:46 +0200 Subject: [PATCH 4/9] Update src/core/hle/service/nvnflinger/buffer_queue_core.cpp --- src/core/hle/service/nvnflinger/buffer_queue_core.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/hle/service/nvnflinger/buffer_queue_core.cpp b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp index 9975ca3ccd..6120d8eae1 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_core.cpp +++ b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp @@ -1,4 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project // SPDX-License-Identifier: GPL-3.0-or-later -- 2.39.5 From 22fef3a4873108974e2da865465835a49016d6b0 Mon Sep 17 00:00:00 2001 From: SDK-Chan Date: Tue, 16 Sep 2025 19:30:06 +0200 Subject: [PATCH 5/9] Update src/core/hle/service/nvnflinger/buffer_queue_core.h --- src/core/hle/service/nvnflinger/buffer_queue_core.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/hle/service/nvnflinger/buffer_queue_core.h b/src/core/hle/service/nvnflinger/buffer_queue_core.h index feedfb0c93..ed7d4b4069 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_core.h +++ b/src/core/hle/service/nvnflinger/buffer_queue_core.h @@ -1,4 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project // SPDX-License-Identifier: GPL-3.0-or-later -- 2.39.5 From 9b697d118a697754b2176f64751fa764e695f7c6 Mon Sep 17 00:00:00 2001 From: SDK-Chan Date: Tue, 16 Sep 2025 19:30:22 +0200 Subject: [PATCH 6/9] Update src/core/hle/service/nvnflinger/buffer_queue_producer.cpp --- src/core/hle/service/nvnflinger/buffer_queue_producer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp index 57fc65ab52..b15bf66035 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp +++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp @@ -1,4 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project // SPDX-License-Identifier: GPL-3.0-or-later -- 2.39.5 From 43d2d4e67a745bd6de1431dc622c0ba952319926 Mon Sep 17 00:00:00 2001 From: SDK-Chan Date: Tue, 16 Sep 2025 19:30:43 +0200 Subject: [PATCH 7/9] Update src/core/hle/service/nvnflinger/buffer_queue_producer.h --- src/core/hle/service/nvnflinger/buffer_queue_producer.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.h b/src/core/hle/service/nvnflinger/buffer_queue_producer.h index 5908a119a2..6610e0853a 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_producer.h +++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.h @@ -1,4 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project // SPDX-License-Identifier: GPL-3.0-or-later -- 2.39.5 From 5cb8040a564ed32c9dae901ad3e250fad3a115a4 Mon Sep 17 00:00:00 2001 From: SDK-Chan Date: Tue, 16 Sep 2025 19:31:01 +0200 Subject: [PATCH 8/9] Update src/core/hle/service/nvnflinger/buffer_slot.h --- src/core/hle/service/nvnflinger/buffer_slot.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/hle/service/nvnflinger/buffer_slot.h b/src/core/hle/service/nvnflinger/buffer_slot.h index f815532563..d348b331cb 100644 --- a/src/core/hle/service/nvnflinger/buffer_slot.h +++ b/src/core/hle/service/nvnflinger/buffer_slot.h @@ -1,4 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project // SPDX-License-Identifier: GPL-3.0-or-later -- 2.39.5 From 35f081a34c1a7a70cba17b36bcc4d58a6f907c59 Mon Sep 17 00:00:00 2001 From: SDK-Chan Date: Tue, 16 Sep 2025 19:33:48 +0200 Subject: [PATCH 9/9] Update src/core/hle/service/nvnflinger/buffer_queue_producer.cpp --- .../nvnflinger/buffer_queue_producer.cpp | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp index b15bf66035..bc3076d20b 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp +++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp @@ -78,7 +78,7 @@ Status BufferQueueProducer::SetBufferCount(s32 buffer_count) { return Status::BadValue; } - // There must be no dequeued buffers when changing the buffer count. + // There must be no dequeued buffers when changing the buffer count. for (s32 s{}; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { if (slots[s].buffer_state == BufferState::Dequeued) { LOG_ERROR(Service_Nvnflinger, "buffer owned by producer"); @@ -99,8 +99,8 @@ Status BufferQueueProducer::SetBufferCount(s32 buffer_count) { return Status::BadValue; } - // Here we are guaranteed that the producer doesn't have any dequeued buffers and will - // release all of its buffer references. + // Here we are guaranteed that the producer doesn't have any dequeued buffers and will + // release all of its buffer references. if (core->GetPreallocatedBufferCountLocked() <= 0) { core->FreeAllBuffersLocked(); } @@ -111,7 +111,7 @@ Status BufferQueueProducer::SetBufferCount(s32 buffer_count) { listener = core->consumer_listener; } - // Call back without lock held + // Call back without lock held if (listener != nullptr) { listener->OnBuffersReleased(); } @@ -137,7 +137,7 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, St } } - // Free up any buffers that are in slots beyond the max buffer count + // Free up any buffers that are in slots beyond the max buffer count for (s32 s = max_buffer_count; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { ASSERT(slots[s].buffer_state == BufferState::Free); if (slots[s].graphic_buffer != nullptr && slots[s].buffer_state == BufferState::Free && @@ -147,7 +147,7 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, St } } - // Look for a free buffer to give to the client + // Look for a free buffer to give to the client *found = BufferQueueCore::INVALID_BUFFER_SLOT; s32 dequeued_count{}; s32 acquired_count{}; @@ -172,16 +172,16 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, St } } - // Producers are not allowed to dequeue more than one buffer if they did not set a buffer - // count + // Producers are not allowed to dequeue more than one buffer if they did not set a buffer + // count if (!core->override_max_buffer_count && dequeued_count) { LOG_ERROR(Service_Nvnflinger, "can't dequeue multiple buffers without setting the buffer count"); return Status::InvalidOperation; } - // See whether a buffer has been queued since the last SetBufferCount so we know whether to - // perform the min undequeued buffers check below + // See whether a buffer has been queued since the last SetBufferCount so we know whether to + // perform the min undequeued buffers check below if (core->buffer_has_been_queued) { // Make sure the producer is not trying to dequeue more buffers than allowed const s32 new_undequeued_count = max_buffer_count - (dequeued_count + 1); @@ -194,16 +194,16 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, St } } - // If we disconnect and reconnect quickly, we can be in a state where our slots are empty - // but we have many buffers in the queue. This can cause us to run out of memory if we - // outrun the consumer. Wait here if it looks like we have too many buffers queued up. + // If we disconnect and reconnect quickly, we can be in a state where our slots are empty + // but we have many buffers in the queue. This can cause us to run out of memory if we + // outrun the consumer. Wait here if it looks like we have too many buffers queued up. const bool too_many_buffers = core->queue.size() > static_cast(max_buffer_count); if (too_many_buffers) { LOG_ERROR(Service_Nvnflinger, "queue size is {}, waiting", core->queue.size()); } - // If no buffer is found, or if the queue has too many buffers outstanding, wait for a - // buffer to be acquired or released, or for the max buffer count to change. + // If no buffer is found, or if the queue has too many buffers outstanding, wait for a + // buffer to be acquired or released, or for the max buffer count to change. try_again = (*found == BufferQueueCore::INVALID_BUFFER_SLOT) || too_many_buffers; if (try_again) { // Return an error if we're in non-blocking mode (producer and consumer are controlled @@ -243,7 +243,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool format = core->default_buffer_format; } - // Enable the usage bits the consumer requested + // Enable the usage bits the consumer requested usage |= core->consumer_usage_bit; s32 found{}; @@ -252,7 +252,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool return status; } - // This should not happen + // This should not happen if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { LOG_ERROR(Service_Nvnflinger, "no available buffer slots"); return Status::Busy; @@ -364,7 +364,7 @@ Status BufferQueueProducer::DetachNextBuffer(std::shared_ptr* out return Status::NoInit; } - // Find the oldest valid slot + // Find the oldest valid slot int found = BufferQueueCore::INVALID_BUFFER_SLOT; for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { if (slots[s].buffer_state == BufferState::Free && slots[s].graphic_buffer != nullptr) { @@ -410,7 +410,7 @@ Status BufferQueueProducer::AttachBuffer(s32* out_slot, return status; } - // This should not happen + // This should not happen if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { LOG_ERROR(Service_Nvnflinger, "No available buffer slots"); return Status::Busy; @@ -550,7 +550,7 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, if (core->StillTracking(*front)) { slots[front->slot].buffer_state = BufferState::Free; - // Mark tracked buffer history records as free + // Mark tracked buffer history records as free for (auto& buffer_history_record : core->buffer_history) { if (buffer_history_record.frame_number == front->frame_number) { buffer_history_record.state = BufferState::Free; @@ -558,8 +558,8 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, } } - // Reset the frame number of the freed buffer so that it is the first in line to - // be dequeued again + // Reset the frame number of the freed buffer so that it is the first in line to + // be dequeued again slots[front->slot].frame_number = 0; } // Overwrite the droppable buffer with the incoming one @@ -577,17 +577,17 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, output->Inflate(core->default_width, core->default_height, core->transform_hint, static_cast(core->queue.size())); - // Take a ticket for the callback functions + // Take a ticket for the callback functions callback_ticket = next_callback_ticket++; } - // Don't send the GraphicBuffer through the callback, and don't send the slot number, since the - // consumer shouldn't need it + // Don't send the GraphicBuffer through the callback, and don't send the slot number, since the + // consumer shouldn't need it item.graphic_buffer.reset(); item.slot = BufferItem::INVALID_BUFFER_SLOT; - // Call back without the main BufferQueue lock held, but with the callback lock held so we can - // ensure that callbacks occur in order + // Call back without the main BufferQueue lock held, but with the callback lock held so we can + // ensure that callbacks occur in order { std::scoped_lock lock{callback_mutex}; while (callback_ticket != current_callback_ticket) { @@ -805,8 +805,8 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot, slots[slot].graphic_buffer = std::make_shared(nvmap, buffer); slots[slot].frame_number = 0; - // Most games preallocate a buffer and pass a valid buffer here. However, it is possible for - // this to be called with an empty buffer, Naruto Ultimate Ninja Storm is a game that does this. + // Most games preallocate a buffer and pass a valid buffer here. However, it is possible for + // this to be called with an empty buffer, Naruto Ultimate Ninja Storm is a game that does this. if (buffer) { slots[slot].is_preallocated = true; @@ -968,8 +968,8 @@ void BufferQueueProducer::Transact(u32 code, std::span parcel_data, index_reversed = (current_history_pos + history_max - i) % history_max; const auto& current_history_buffer = core->buffer_history[index_reversed]; - // Here we use the frame number as a terminator. - // Because a buffer without frame_number is not considered complete + // Here we use the frame number as a terminator. + // Because a buffer without frame_number is not considered complete if (current_history_buffer.frame_number == 0) { break; } -- 2.39.5