diff options
Diffstat (limited to '')
-rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/core/hle/service/nvflinger/buffer_queue_core.cpp | 135 | ||||
-rw-r--r-- | src/core/hle/service/nvflinger/buffer_queue_core.h | 98 |
3 files changed, 235 insertions, 0 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c7ce6c1a6..5ba9b6c09 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -543,6 +543,8 @@ add_library(core STATIC hle/service/nvflinger/buffer_item_consumer.h hle/service/nvflinger/buffer_queue_consumer.cpp hle/service/nvflinger/buffer_queue_consumer.h + hle/service/nvflinger/buffer_queue_core.cpp + hle/service/nvflinger/buffer_queue_core.h hle/service/nvflinger/buffer_queue_defs.h hle/service/nvflinger/buffer_slot.h hle/service/nvflinger/buffer_transform_flags.h diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.cpp b/src/core/hle/service/nvflinger/buffer_queue_core.cpp new file mode 100644 index 000000000..a6ff46aa9 --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_queue_core.cpp @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2021 yuzu Emulator Project +// Copyright 2014 The Android Open Source Project +// Parts of this implementation were base 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/nvflinger/buffer_queue_core.h" + +namespace android { + +BufferQueueCore::BufferQueueCore() : lock{mutex} { + // This is locked on creation, so unlock. + lock.unlock(); + + for (s32 slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) { + free_slots.insert(slot); + } +} + +void BufferQueueCore::NotifyShutdown() { + std::unique_lock<std::mutex> lk(mutex); + + is_shutting_down = true; + + SignalDequeueCondition(); +} + +void BufferQueueCore::SignalDequeueCondition() { + dequeue_condition.notify_all(); +} + +bool BufferQueueCore::WaitForDequeueCondition() { + if (is_shutting_down) { + return false; + } + + dequeue_condition.wait(lock); + + return true; +} + +s32 BufferQueueCore::GetMinUndequeuedBufferCountLocked(bool async) const { + // If DequeueBuffer is allowed to error out, we don't have to add an extra buffer. + if (!use_async_buffer) { + return max_acquired_buffer_count; + } + + 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) + 1; +} + +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); + max_buffer_count = override_max_buffer_count; + } + + // Any buffers that are dequeued by the producer or sitting in the queue waiting to be consumed + // need to have their slots preserved. + 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<s32>(std::count_if(slots.begin(), slots.end(), + [](const auto& slot) { return slot.is_preallocated; })); +} + +void BufferQueueCore::FreeBufferLocked(s32 slot) { + LOG_DEBUG(Service_NVFlinger, "slot {}", slot); + + const auto had_buffer = slots[slot].graphic_buffer != nullptr; + + slots[slot].graphic_buffer.reset(); + + if (slots[slot].buffer_state == BufferState::Acquired) { + slots[slot].needs_cleanup_on_release = true; + } + + if (slots[slot].buffer_state != BufferState::Free) { + free_slots.insert(slot); + } else if (had_buffer) { + // If the slot was FREE, but we had a buffer, we need to move this slot from the free + // buffers list to the the free slots list. + free_buffers.remove(slot); + free_slots.insert(slot); + } + + slots[slot].buffer_state = BufferState::Free; + slots[slot].acquire_called = false; + slots[slot].frame_number = 0; + slots[slot].fence = Fence::NoFence(); +} + +void BufferQueueCore::FreeAllBuffersLocked() { + queue.clear(); + 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) { + std::unique_lock<std::mutex> lk(mutex); + is_allocating_condition.wait(lk); + } +} + +} // namespace android diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.h b/src/core/hle/service/nvflinger/buffer_queue_core.h new file mode 100644 index 000000000..8019b7024 --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_queue_core.h @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2021 yuzu Emulator Project +// Copyright 2014 The Android Open Source Project +// Parts of this implementation were base on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueCore.h + +#pragma once + +#include <condition_variable> +#include <list> +#include <memory> +#include <mutex> +#include <set> +#include <vector> + +#include "core/hle/service/nvflinger/buffer_item.h" +#include "core/hle/service/nvflinger/buffer_queue_defs.h" +#include "core/hle/service/nvflinger/pixel_format.h" +#include "core/hle/service/nvflinger/status.h" +#include "core/hle/service/nvflinger/window.h" + +namespace android { + +class IConsumerListener; +class IProducerListener; + +class BufferQueueCore final { + friend class BufferQueueProducer; + friend class BufferQueueConsumer; + +public: + static constexpr s32 INVALID_BUFFER_SLOT = BufferItem::INVALID_BUFFER_SLOT; + + BufferQueueCore(); + + void NotifyShutdown(); + +private: + void SignalDequeueCondition(); + bool WaitForDequeueCondition(); + + s32 GetMinUndequeuedBufferCountLocked(bool async) const; + s32 GetMinMaxBufferCountLocked(bool async) const; + s32 GetMaxBufferCountLocked(bool async) const; + s32 GetPreallocatedBufferCountLocked() const; + void FreeBufferLocked(s32 slot); + void FreeAllBuffersLocked(); + bool StillTracking(const BufferItem* item) const; + void WaitWhileAllocatingLocked() const; + +private: + class AutoLock final { + public: + AutoLock(std::shared_ptr<BufferQueueCore>& core_) : core{core_} { + core->lock.lock(); + } + + ~AutoLock() { + core->lock.unlock(); + } + + private: + std::shared_ptr<BufferQueueCore>& core; + }; + +private: + mutable std::mutex mutex; + mutable std::unique_lock<std::mutex> lock; + bool is_abandoned{}; + bool consumer_controlled_by_app{}; + std::shared_ptr<IConsumerListener> consumer_listener; + u32 consumer_usage_bit{}; + NativeWindowApi connected_api{NativeWindowApi::NoConnectedApi}; + std::shared_ptr<IProducerListener> connected_producer_listener; + BufferQueueDefs::SlotsType slots{}; + std::vector<BufferItem> queue; + std::set<s32> free_slots; + std::list<s32> free_buffers; + s32 override_max_buffer_count{}; + mutable std::condition_variable dequeue_condition; + const bool use_async_buffer{}; // This is always disabled on HOS + bool dequeue_buffer_cannot_block{}; + PixelFormat default_buffer_format{PixelFormat::Rgba8888}; + u32 default_width{1}; + u32 default_height{1}; + s32 default_max_buffer_count{2}; + const s32 max_acquired_buffer_count{}; // This is always zero on HOS + bool buffer_has_been_queued{}; + u64 frame_counter{}; + u32 transform_hint{}; + bool is_allocating{}; + mutable std::condition_variable is_allocating_condition; + bool allow_allocation{true}; + u64 buffer_age{}; + bool is_shutting_down{}; +}; + +} // namespace android |