hle: nvflinger: Add implementation for BufferQueueConsumer class.
This commit is contained in:
		| @@ -541,6 +541,8 @@ add_library(core STATIC | |||||||
|     hle/service/nvflinger/buffer_item.h |     hle/service/nvflinger/buffer_item.h | ||||||
|     hle/service/nvflinger/buffer_item_consumer.cpp |     hle/service/nvflinger/buffer_item_consumer.cpp | ||||||
|     hle/service/nvflinger/buffer_item_consumer.h |     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_defs.h |     hle/service/nvflinger/buffer_queue_defs.h | ||||||
|     hle/service/nvflinger/buffer_slot.h |     hle/service/nvflinger/buffer_slot.h | ||||||
|     hle/service/nvflinger/buffer_transform_flags.h |     hle/service/nvflinger/buffer_transform_flags.h | ||||||
|   | |||||||
							
								
								
									
										225
									
								
								src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										225
									
								
								src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,225 @@ | |||||||
|  | // 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/BufferQueueConsumer.cpp | ||||||
|  |  | ||||||
|  | #include "common/logging/log.h" | ||||||
|  | #include "core/hle/service/nvflinger/buffer_item.h" | ||||||
|  | #include "core/hle/service/nvflinger/buffer_queue_consumer.h" | ||||||
|  | #include "core/hle/service/nvflinger/buffer_queue_core.h" | ||||||
|  | #include "core/hle/service/nvflinger/producer_listener.h" | ||||||
|  |  | ||||||
|  | namespace android { | ||||||
|  |  | ||||||
|  | BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_) | ||||||
|  |     : core{std::move(core_)}, slots{core->slots} {} | ||||||
|  |  | ||||||
|  | BufferQueueConsumer::~BufferQueueConsumer() = default; | ||||||
|  |  | ||||||
|  | Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer, s64 expected_presenst_ns, | ||||||
|  |                                           u64 max_frame_number) { | ||||||
|  |     s32 num_dropped_buffers{}; | ||||||
|  |  | ||||||
|  |     std::shared_ptr<IProducerListener> listener; | ||||||
|  |     { | ||||||
|  |         std::unique_lock lock(core->mutex); | ||||||
|  |  | ||||||
|  |         // Check that the consumer doesn't currently have the maximum number of buffers acquired. | ||||||
|  |         s32 num_acquired_buffers{}; | ||||||
|  |         for (const auto& slot : slots) { | ||||||
|  |             if (slot.buffer_state == BufferState::Acquired) { | ||||||
|  |                 ++num_acquired_buffers; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (num_acquired_buffers >= core->max_acquired_buffer_count + 1) { | ||||||
|  |             LOG_ERROR(Service_NVFlinger, "max acquired buffer count reached: {} (max {})", | ||||||
|  |                       num_acquired_buffers, core->max_acquired_buffer_count); | ||||||
|  |             return Status::InvalidOperation; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Check if the queue is empty. | ||||||
|  |         if (core->queue.empty()) { | ||||||
|  |             return Status::NoBufferAvailable; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         auto front(core->queue.begin()); | ||||||
|  |  | ||||||
|  |         // If expected_presenst_ns is specified, we may not want to return a buffer yet. | ||||||
|  |         if (expected_presenst_ns != 0) { | ||||||
|  |             constexpr auto MAX_REASONABLE_NSEC = 1000000000LL; // 1 second | ||||||
|  |  | ||||||
|  |             // The expected_presenst_ns argument indicates when the buffer is expected to be | ||||||
|  |             // presented on-screen. | ||||||
|  |             while (core->queue.size() > 1 && !core->queue[0].is_auto_timestamp) { | ||||||
|  |                 const auto& buffer_item{core->queue[1]}; | ||||||
|  |  | ||||||
|  |                 // If dropping entry[0] would leave us with a buffer that the consumer is not yet | ||||||
|  |                 // ready for, don't drop it. | ||||||
|  |                 if (max_frame_number && buffer_item.frame_number > max_frame_number) { | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 // If entry[1] is timely, drop entry[0] (and repeat). | ||||||
|  |                 const auto desired_present = buffer_item.timestamp; | ||||||
|  |                 if (desired_present < expected_presenst_ns - MAX_REASONABLE_NSEC || | ||||||
|  |                     desired_present > expected_presenst_ns) { | ||||||
|  |                     // This buffer is set to display in the near future, or desired_present is | ||||||
|  |                     // garbage. | ||||||
|  |                     LOG_DEBUG(Service_NVFlinger, "nodrop desire={} expect={}", desired_present, | ||||||
|  |                               expected_presenst_ns); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 LOG_DEBUG(Service_NVFlinger, "drop desire={} expect={} size={}", desired_present, | ||||||
|  |                           expected_presenst_ns, core->queue.size()); | ||||||
|  |  | ||||||
|  |                 if (core->StillTracking(&*front)) { | ||||||
|  |                     // Front buffer is still in mSlots, so mark the slot as free | ||||||
|  |                     slots[front->slot].buffer_state = BufferState::Free; | ||||||
|  |                     core->free_buffers.push_back(front->slot); | ||||||
|  |                     listener = core->connected_producer_listener; | ||||||
|  |                     ++num_dropped_buffers; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 core->queue.erase(front); | ||||||
|  |                 front = core->queue.begin(); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // See if the front buffer is ready to be acquired. | ||||||
|  |             const auto desired_present = front->timestamp; | ||||||
|  |             const auto buffer_is_due = desired_present <= expected_presenst_ns || | ||||||
|  |                                        desired_present > expected_presenst_ns + MAX_REASONABLE_NSEC; | ||||||
|  |             const auto consumer_is_ready = | ||||||
|  |                 max_frame_number > 0 ? front->frame_number <= max_frame_number : true; | ||||||
|  |  | ||||||
|  |             if (!buffer_is_due || !consumer_is_ready) { | ||||||
|  |                 LOG_DEBUG(Service_NVFlinger, "defer desire={} expect={}", desired_present, | ||||||
|  |                           expected_presenst_ns); | ||||||
|  |                 return Status::PresentLater; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             LOG_DEBUG(Service_NVFlinger, "accept desire={} expect={}", desired_present, | ||||||
|  |                       expected_presenst_ns); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         const auto slot = front->slot; | ||||||
|  |         *out_buffer = *front; | ||||||
|  |  | ||||||
|  |         LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot); | ||||||
|  |  | ||||||
|  |         // If the front buffer is still being tracked, update its slot state | ||||||
|  |         if (core->StillTracking(&*front)) { | ||||||
|  |             slots[slot].acquire_called = true; | ||||||
|  |             slots[slot].needs_cleanup_on_release = false; | ||||||
|  |             slots[slot].buffer_state = BufferState::Acquired; | ||||||
|  |             slots[slot].fence = Fence::NoFence(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr | ||||||
|  |         // to avoid unnecessarily remapping this buffer on the consumer side. | ||||||
|  |         if (out_buffer->acquire_called) { | ||||||
|  |             out_buffer->graphic_buffer = nullptr; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         core->queue.erase(front); | ||||||
|  |  | ||||||
|  |         // We might have freed a slot while dropping old buffers, or the producer  may be blocked | ||||||
|  |         // waiting for the number of buffers in the queue to decrease. | ||||||
|  |         core->SignalDequeueCondition(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (listener != nullptr) { | ||||||
|  |         for (s32 i = 0; i < num_dropped_buffers; ++i) { | ||||||
|  |             listener->OnBufferReleased(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return Status::NoError; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence) { | ||||||
|  |     if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { | ||||||
|  |         LOG_ERROR(Service_NVFlinger, "slot {} out of range", slot); | ||||||
|  |         return Status::BadValue; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     std::shared_ptr<IProducerListener> listener; | ||||||
|  |     { | ||||||
|  |         std::unique_lock lock(core->mutex); | ||||||
|  |  | ||||||
|  |         // If the frame number has changed because the buffer has been reallocated, we can ignore | ||||||
|  |         // this ReleaseBuffer for the old buffer. | ||||||
|  |         if (frame_number != slots[slot].frame_number) { | ||||||
|  |             return Status::StaleBufferSlot; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Make sure this buffer hasn't been queued while acquired by the consumer. | ||||||
|  |         auto current(core->queue.begin()); | ||||||
|  |         while (current != core->queue.end()) { | ||||||
|  |             if (current->slot == slot) { | ||||||
|  |                 LOG_ERROR(Service_NVFlinger, "buffer slot {} pending release is currently queued", | ||||||
|  |                           slot); | ||||||
|  |                 return Status::BadValue; | ||||||
|  |             } | ||||||
|  |             ++current; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (slots[slot].buffer_state == BufferState::Acquired) { | ||||||
|  |             slots[slot].fence = release_fence; | ||||||
|  |             slots[slot].buffer_state = BufferState::Free; | ||||||
|  |  | ||||||
|  |             core->free_buffers.push_back(slot); | ||||||
|  |  | ||||||
|  |             listener = core->connected_producer_listener; | ||||||
|  |  | ||||||
|  |             LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot); | ||||||
|  |         } else if (slots[slot].needs_cleanup_on_release) { | ||||||
|  |             LOG_DEBUG(Service_NVFlinger, "releasing a stale buffer slot {} (state = {})", slot, | ||||||
|  |                       slots[slot].buffer_state); | ||||||
|  |  | ||||||
|  |             slots[slot].needs_cleanup_on_release = false; | ||||||
|  |  | ||||||
|  |             return Status::StaleBufferSlot; | ||||||
|  |         } else { | ||||||
|  |             LOG_ERROR(Service_NVFlinger, "attempted to release buffer slot {} but its state was {}", | ||||||
|  |                       slot, slots[slot].buffer_state); | ||||||
|  |  | ||||||
|  |             return Status::BadValue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         core->dequeue_condition.notify_all(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Call back without lock held | ||||||
|  |     if (listener != nullptr) { | ||||||
|  |         listener->OnBufferReleased(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return Status::NoError; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_listener, | ||||||
|  |                                     bool controlled_by_app) { | ||||||
|  |     if (consumer_listener == nullptr) { | ||||||
|  |         LOG_ERROR(Service_NVFlinger, "consumer_listener may not be nullptr"); | ||||||
|  |         return Status::BadValue; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     LOG_DEBUG(Service_NVFlinger, "controlled_by_app={}", controlled_by_app); | ||||||
|  |  | ||||||
|  |     BufferQueueCore::AutoLock lock(core); | ||||||
|  |  | ||||||
|  |     if (core->is_abandoned) { | ||||||
|  |         LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); | ||||||
|  |         return Status::NoInit; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     core->consumer_listener = consumer_listener; | ||||||
|  |     core->consumer_controlled_by_app = controlled_by_app; | ||||||
|  |  | ||||||
|  |     return Status::NoError; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace android | ||||||
							
								
								
									
										36
									
								
								src/core/hle/service/nvflinger/buffer_queue_consumer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/core/hle/service/nvflinger/buffer_queue_consumer.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | |||||||
|  | // 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/BufferQueueConsumer.h | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <memory> | ||||||
|  |  | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "core/hle/service/nvflinger/buffer_queue_defs.h" | ||||||
|  | #include "core/hle/service/nvflinger/status.h" | ||||||
|  |  | ||||||
|  | namespace android { | ||||||
|  |  | ||||||
|  | class BufferItem; | ||||||
|  | class BufferQueueCore; | ||||||
|  | class IConsumerListener; | ||||||
|  |  | ||||||
|  | class BufferQueueConsumer final { | ||||||
|  | public: | ||||||
|  |     explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_); | ||||||
|  |     ~BufferQueueConsumer(); | ||||||
|  |  | ||||||
|  |     Status AcquireBuffer(BufferItem* out_buffer, s64 expected_presenst_ns, | ||||||
|  |                          u64 max_frame_number = 0); | ||||||
|  |     Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence); | ||||||
|  |     Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app); | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     std::shared_ptr<BufferQueueCore> core; | ||||||
|  |     BufferQueueDefs::SlotsType& slots; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | } // namespace android | ||||||
		Reference in New Issue
	
	Block a user