3 changed files with 235 additions and 0 deletions
-
2src/core/CMakeLists.txt
-
135src/core/hle/service/nvflinger/buffer_queue_core.cpp
-
98src/core/hle/service/nvflinger/buffer_queue_core.h
@ -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
|
||||
@ -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 |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue