// SPDX-FileCopyrightText: Copyright 2026 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 // Parts of this implementation were based on: // https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueCore.cpp #include "common/assert.h" #include "core/hle/service/nvnflinger/buffer_queue_core.h" namespace Service::android { BufferQueueCore::BufferQueueCore() = default; BufferQueueCore::~BufferQueueCore() = default; void BufferQueueCore::PushHistory(u64 frame_number, s64 queue_time, s64 presentation_time, BufferState state) { std::lock_guard lk(buffer_history_mutex); auto it = buffer_history_map.find(frame_number); if (it != buffer_history_map.end()) { it->second.state = state; return; } buffer_history_map.emplace(frame_number, BufferHistoryInfo{ frame_number, queue_time, presentation_time, state }); buffer_history_order.push_back(frame_number); if (buffer_history_order.size() > BUFFER_HISTORY_SIZE) { u64 oldest_frame = buffer_history_order.front(); buffer_history_order.pop_front(); buffer_history_map.erase(oldest_frame); } } void BufferQueueCore::UpdateHistory(u64 frame_number, BufferState state) { std::lock_guard lk(buffer_history_mutex); auto it = buffer_history_map.find(frame_number); if (it != buffer_history_map.end()) { it->second.state = state; } } void BufferQueueCore::SignalDequeueCondition() { dequeue_possible.store(true); dequeue_condition.notify_all(); } bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock& lk) { dequeue_condition.wait(lk, [&] { return dequeue_possible.load(); }); dequeue_possible.store(false); return true; } s32 BufferQueueCore::GetMinUndequeuedBufferCountLocked(bool async) const { if (!use_async_buffer) { return 0; } if (dequeue_buffer_cannot_block || async) { return max_acquired_buffer_count + 1; } return max_acquired_buffer_count; } s32 BufferQueueCore::GetMinMaxBufferCountLocked(bool async) const { return GetMinUndequeuedBufferCountLocked(async); } 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); if (override_max_buffer_count != 0) { ASSERT(override_max_buffer_count >= min_buffer_count); return override_max_buffer_count; } for (s32 slot = max_buffer_count; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) { const auto state = slots[slot].buffer_state; if (state == BufferState::Queued || state == BufferState::Dequeued) { max_buffer_count = slot + 1; } } return max_buffer_count; } s32 BufferQueueCore::GetPreallocatedBufferCountLocked() const { return static_cast(std::count_if(slots.begin(), slots.end(), [](const auto& slot) { return slot.is_preallocated; })); } void BufferQueueCore::FreeBufferLocked(s32 slot) { LOG_DEBUG(Service_Nvnflinger, "slot {}", slot); slots[slot].graphic_buffer.reset(); if (slots[slot].buffer_state == BufferState::Acquired) { slots[slot].needs_cleanup_on_release = true; } slots[slot].buffer_state = BufferState::Free; slots[slot].frame_number = UINT32_MAX; slots[slot].acquire_called = false; slots[slot].fence = Fence::NoFence(); } void BufferQueueCore::FreeAllBuffersLocked() { buffer_has_been_queued = false; for (s32 slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) { FreeBufferLocked(slot); } } bool BufferQueueCore::StillTracking(const BufferItem& item) const { const BufferSlot& slot = slots[item.slot]; return (slot.graphic_buffer != nullptr) && (item.graphic_buffer == slot.graphic_buffer); } void BufferQueueCore::WaitWhileAllocatingLocked() const { while (is_allocating) { is_allocating_condition.wait(mutex); } } } // namespace Service::android