diff options
Diffstat (limited to '')
-rw-r--r-- | src/core/hle/service/nvflinger/buffer_queue_producer.cpp | 933 |
1 files changed, 0 insertions, 933 deletions
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp deleted file mode 100644 index e601b5da1..000000000 --- a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp +++ /dev/null @@ -1,933 +0,0 @@ -// 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/BufferQueueProducer.cpp - -#include "common/assert.h" -#include "common/logging/log.h" -#include "common/settings.h" -#include "core/core.h" -#include "core/hle/kernel/hle_ipc.h" -#include "core/hle/kernel/k_event.h" -#include "core/hle/kernel/k_readable_event.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/service/kernel_helpers.h" -#include "core/hle/service/nvdrv/core/nvmap.h" -#include "core/hle/service/nvflinger/buffer_queue_core.h" -#include "core/hle/service/nvflinger/buffer_queue_producer.h" -#include "core/hle/service/nvflinger/consumer_listener.h" -#include "core/hle/service/nvflinger/parcel.h" -#include "core/hle/service/nvflinger/ui/graphic_buffer.h" -#include "core/hle/service/nvflinger/window.h" -#include "core/hle/service/vi/vi.h" - -namespace Service::android { - -BufferQueueProducer::BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_, - std::shared_ptr<BufferQueueCore> buffer_queue_core_, - Service::Nvidia::NvCore::NvMap& nvmap_) - : service_context{service_context_}, core{std::move(buffer_queue_core_)}, slots(core->slots), - nvmap(nvmap_) { - buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent"); -} - -BufferQueueProducer::~BufferQueueProducer() { - service_context.CloseEvent(buffer_wait_event); -} - -Status BufferQueueProducer::RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf) { - LOG_DEBUG(Service_NVFlinger, "slot {}", slot); - - std::scoped_lock lock{core->mutex}; - - if (core->is_abandoned) { - LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); - return Status::NoInit; - } - if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { - LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot, - BufferQueueDefs::NUM_BUFFER_SLOTS); - return Status::BadValue; - } else if (slots[slot].buffer_state != BufferState::Dequeued) { - LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot, - slots[slot].buffer_state); - return Status::BadValue; - } - - slots[slot].request_buffer_called = true; - *buf = slots[slot].graphic_buffer; - - return Status::NoError; -} - -Status BufferQueueProducer::SetBufferCount(s32 buffer_count) { - LOG_DEBUG(Service_NVFlinger, "count = {}", buffer_count); - - std::shared_ptr<IConsumerListener> listener; - { - std::scoped_lock lock{core->mutex}; - core->WaitWhileAllocatingLocked(); - - if (core->is_abandoned) { - LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); - return Status::NoInit; - } - - if (buffer_count > BufferQueueDefs::NUM_BUFFER_SLOTS) { - LOG_ERROR(Service_NVFlinger, "buffer_count {} too large (max {})", buffer_count, - BufferQueueDefs::NUM_BUFFER_SLOTS); - return Status::BadValue; - } - - // 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_NVFlinger, "buffer owned by producer"); - return Status::BadValue; - } - } - - if (buffer_count == 0) { - core->override_max_buffer_count = 0; - core->SignalDequeueCondition(); - return Status::NoError; - } - - const s32 min_buffer_slots = core->GetMinMaxBufferCountLocked(false); - if (buffer_count < min_buffer_slots) { - LOG_ERROR(Service_NVFlinger, "requested buffer count {} is less than minimum {}", - buffer_count, min_buffer_slots); - return Status::BadValue; - } - - // 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(); - } - - core->override_max_buffer_count = buffer_count; - core->SignalDequeueCondition(); - buffer_wait_event->Signal(); - listener = core->consumer_listener; - } - - // Call back without lock held - if (listener != nullptr) { - listener->OnBuffersReleased(); - } - - return Status::NoError; -} - -Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags, - std::unique_lock<std::mutex>& lk) const { - bool try_again = true; - - while (try_again) { - if (core->is_abandoned) { - LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); - return Status::NoInit; - } - - const s32 max_buffer_count = core->GetMaxBufferCountLocked(async); - if (async && core->override_max_buffer_count) { - if (core->override_max_buffer_count < max_buffer_count) { - LOG_ERROR(Service_NVFlinger, "async mode is invalid with buffer count override"); - return Status::BadValue; - } - } - - // 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) { - core->FreeBufferLocked(s); - *return_flags |= Status::ReleaseAllBuffers; - } - } - - // Look for a free buffer to give to the client - *found = BufferQueueCore::INVALID_BUFFER_SLOT; - s32 dequeued_count{}; - s32 acquired_count{}; - for (s32 s{}; s < max_buffer_count; ++s) { - switch (slots[s].buffer_state) { - case BufferState::Dequeued: - ++dequeued_count; - break; - case BufferState::Acquired: - ++acquired_count; - break; - case BufferState::Free: - // We return the oldest of the free buffers to avoid stalling the producer if - // possible, since the consumer may still have pending reads of in-flight buffers - if (*found == BufferQueueCore::INVALID_BUFFER_SLOT || - slots[s].frame_number < slots[*found].frame_number) { - *found = s; - } - break; - default: - break; - } - } - - // 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_NVFlinger, - "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 - 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); - const s32 min_undequeued_count = core->GetMinUndequeuedBufferCountLocked(async); - if (new_undequeued_count < min_undequeued_count) { - LOG_ERROR(Service_NVFlinger, - "min undequeued buffer count({}) exceeded (dequeued={} undequeued={})", - min_undequeued_count, dequeued_count, new_undequeued_count); - return Status::InvalidOperation; - } - } - - // 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<size_t>(max_buffer_count); - if (too_many_buffers) { - LOG_ERROR(Service_NVFlinger, "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. - 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 - // by the application). - if (core->dequeue_buffer_cannot_block && - (acquired_count <= core->max_acquired_buffer_count)) { - return Status::WouldBlock; - } - - if (!core->WaitForDequeueCondition(lk)) { - // We are no longer running - return Status::NoError; - } - } - } - - return Status::NoError; -} - -Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool async, u32 width, - u32 height, PixelFormat format, u32 usage) { - LOG_DEBUG(Service_NVFlinger, "async={} w={} h={} format={}, usage={}", async ? "true" : "false", - width, height, format, usage); - - if ((width != 0 && height == 0) || (width == 0 && height != 0)) { - LOG_ERROR(Service_NVFlinger, "invalid size: w={} h={}", width, height); - return Status::BadValue; - } - - Status return_flags = Status::NoError; - bool attached_by_consumer = false; - { - std::unique_lock lock{core->mutex}; - core->WaitWhileAllocatingLocked(); - - if (format == PixelFormat::NoFormat) { - format = core->default_buffer_format; - } - - // Enable the usage bits the consumer requested - usage |= core->consumer_usage_bit; - - s32 found{}; - Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags, lock); - if (status != Status::NoError) { - return status; - } - - // This should not happen - if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { - LOG_ERROR(Service_NVFlinger, "no available buffer slots"); - return Status::Busy; - } - - *out_slot = found; - - attached_by_consumer = slots[found].attached_by_consumer; - - const bool use_default_size = !width && !height; - if (use_default_size) { - width = core->default_width; - height = core->default_height; - } - - slots[found].buffer_state = BufferState::Dequeued; - - const std::shared_ptr<GraphicBuffer>& buffer(slots[found].graphic_buffer); - if ((buffer == nullptr) || (buffer->Width() != width) || (buffer->Height() != height) || - (buffer->Format() != format) || ((buffer->Usage() & usage) != usage)) { - slots[found].acquire_called = false; - slots[found].graphic_buffer = nullptr; - slots[found].request_buffer_called = false; - slots[found].fence = Fence::NoFence(); - - return_flags |= Status::BufferNeedsReallocation; - } - - *out_fence = slots[found].fence; - slots[found].fence = Fence::NoFence(); - } - - if ((return_flags & Status::BufferNeedsReallocation) != Status::None) { - LOG_DEBUG(Service_NVFlinger, "allocating a new buffer for slot {}", *out_slot); - - auto graphic_buffer = std::make_shared<GraphicBuffer>(width, height, format, usage); - if (graphic_buffer == nullptr) { - LOG_ERROR(Service_NVFlinger, "creating GraphicBuffer failed"); - return Status::NoMemory; - } - - { - std::scoped_lock lock{core->mutex}; - - if (core->is_abandoned) { - LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); - return Status::NoInit; - } - - slots[*out_slot].frame_number = UINT32_MAX; - slots[*out_slot].graphic_buffer = graphic_buffer; - } - } - - if (attached_by_consumer) { - return_flags |= Status::BufferNeedsReallocation; - } - - LOG_DEBUG(Service_NVFlinger, "returning slot={} frame={}, flags={}", *out_slot, - slots[*out_slot].frame_number, return_flags); - - return return_flags; -} - -Status BufferQueueProducer::DetachBuffer(s32 slot) { - LOG_DEBUG(Service_NVFlinger, "slot {}", slot); - - std::scoped_lock lock{core->mutex}; - - if (core->is_abandoned) { - LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); - return Status::NoInit; - } - - if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { - LOG_ERROR(Service_NVFlinger, "slot {} out of range [0, {})", slot, - BufferQueueDefs::NUM_BUFFER_SLOTS); - return Status::BadValue; - } else if (slots[slot].buffer_state != BufferState::Dequeued) { - LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot, - slots[slot].buffer_state); - return Status::BadValue; - } else if (!slots[slot].request_buffer_called) { - LOG_ERROR(Service_NVFlinger, "buffer in slot {} has not been requested", slot); - return Status::BadValue; - } - - core->FreeBufferLocked(slot); - core->SignalDequeueCondition(); - - return Status::NoError; -} - -Status BufferQueueProducer::DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out_buffer, - Fence* out_fence) { - if (out_buffer == nullptr) { - LOG_ERROR(Service_NVFlinger, "out_buffer must not be nullptr"); - return Status::BadValue; - } else if (out_fence == nullptr) { - LOG_ERROR(Service_NVFlinger, "out_fence must not be nullptr"); - return Status::BadValue; - } - - std::scoped_lock lock{core->mutex}; - core->WaitWhileAllocatingLocked(); - - if (core->is_abandoned) { - LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); - return Status::NoInit; - } - - // 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) { - if (found == BufferQueueCore::INVALID_BUFFER_SLOT || - slots[s].frame_number < slots[found].frame_number) { - found = s; - } - } - } - - if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { - return Status::NoMemory; - } - - LOG_DEBUG(Service_NVFlinger, "Detached slot {}", found); - - *out_buffer = slots[found].graphic_buffer; - *out_fence = slots[found].fence; - - core->FreeBufferLocked(found); - - return Status::NoError; -} - -Status BufferQueueProducer::AttachBuffer(s32* out_slot, - const std::shared_ptr<GraphicBuffer>& buffer) { - if (out_slot == nullptr) { - LOG_ERROR(Service_NVFlinger, "out_slot must not be nullptr"); - return Status::BadValue; - } else if (buffer == nullptr) { - LOG_ERROR(Service_NVFlinger, "Cannot attach nullptr buffer"); - return Status::BadValue; - } - - std::unique_lock lock{core->mutex}; - core->WaitWhileAllocatingLocked(); - - Status return_flags = Status::NoError; - s32 found{}; - - const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags, lock); - if (status != Status::NoError) { - return status; - } - - // This should not happen - if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { - LOG_ERROR(Service_NVFlinger, "No available buffer slots"); - return Status::Busy; - } - - *out_slot = found; - - LOG_DEBUG(Service_NVFlinger, "Returning slot {} flags={}", *out_slot, return_flags); - - slots[*out_slot].graphic_buffer = buffer; - slots[*out_slot].buffer_state = BufferState::Dequeued; - slots[*out_slot].fence = Fence::NoFence(); - slots[*out_slot].request_buffer_called = true; - - return return_flags; -} - -Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, - QueueBufferOutput* output) { - s64 timestamp{}; - bool is_auto_timestamp{}; - Common::Rectangle<s32> crop; - NativeWindowScalingMode scaling_mode{}; - NativeWindowTransform transform; - u32 sticky_transform_{}; - bool async{}; - s32 swap_interval{}; - Fence fence{}; - - input.Deflate(×tamp, &is_auto_timestamp, &crop, &scaling_mode, &transform, - &sticky_transform_, &async, &swap_interval, &fence); - - switch (scaling_mode) { - case NativeWindowScalingMode::Freeze: - case NativeWindowScalingMode::ScaleToWindow: - case NativeWindowScalingMode::ScaleCrop: - case NativeWindowScalingMode::NoScaleCrop: - break; - default: - LOG_ERROR(Service_NVFlinger, "unknown scaling mode {}", scaling_mode); - return Status::BadValue; - } - - std::shared_ptr<IConsumerListener> frame_available_listener; - std::shared_ptr<IConsumerListener> frame_replaced_listener; - s32 callback_ticket{}; - BufferItem item; - - { - std::scoped_lock lock{core->mutex}; - - if (core->is_abandoned) { - LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); - return Status::NoInit; - } - - const s32 max_buffer_count = core->GetMaxBufferCountLocked(async); - if (async && core->override_max_buffer_count) { - if (core->override_max_buffer_count < max_buffer_count) { - LOG_ERROR(Service_NVFlinger, "async mode is invalid with " - "buffer count override"); - return Status::BadValue; - } - } - - if (slot < 0 || slot >= max_buffer_count) { - LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot, - max_buffer_count); - return Status::BadValue; - } else if (slots[slot].buffer_state != BufferState::Dequeued) { - LOG_ERROR(Service_NVFlinger, - "slot {} is not owned by the producer " - "(state = {})", - slot, slots[slot].buffer_state); - return Status::BadValue; - } else if (!slots[slot].request_buffer_called) { - LOG_ERROR(Service_NVFlinger, - "slot {} was queued without requesting " - "a buffer", - slot); - return Status::BadValue; - } - - LOG_DEBUG(Service_NVFlinger, - "slot={} frame={} time={} crop=[{},{},{},{}] transform={} scale={}", slot, - core->frame_counter + 1, timestamp, crop.Left(), crop.Top(), crop.Right(), - crop.Bottom(), transform, scaling_mode); - - const std::shared_ptr<GraphicBuffer>& graphic_buffer(slots[slot].graphic_buffer); - Common::Rectangle<s32> buffer_rect(graphic_buffer->Width(), graphic_buffer->Height()); - Common::Rectangle<s32> cropped_rect; - [[maybe_unused]] const bool unused = crop.Intersect(buffer_rect, &cropped_rect); - - if (cropped_rect != crop) { - LOG_ERROR(Service_NVFlinger, "crop rect is not contained within the buffer in slot {}", - slot); - return Status::BadValue; - } - - slots[slot].fence = fence; - slots[slot].buffer_state = BufferState::Queued; - ++core->frame_counter; - slots[slot].frame_number = core->frame_counter; - - item.acquire_called = slots[slot].acquire_called; - item.graphic_buffer = slots[slot].graphic_buffer; - item.crop = crop; - item.transform = transform & ~NativeWindowTransform::InverseDisplay; - item.transform_to_display_inverse = - (transform & NativeWindowTransform::InverseDisplay) != NativeWindowTransform::None; - item.scaling_mode = static_cast<u32>(scaling_mode); - item.timestamp = timestamp; - item.is_auto_timestamp = is_auto_timestamp; - item.frame_number = core->frame_counter; - item.slot = slot; - item.fence = fence; - item.is_droppable = core->dequeue_buffer_cannot_block || async; - item.swap_interval = swap_interval; - - nvmap.DuplicateHandle(item.graphic_buffer->BufferId(), true); - - sticky_transform = sticky_transform_; - - if (core->queue.empty()) { - // When the queue is empty, we can simply queue this buffer - core->queue.push_back(item); - frame_available_listener = core->consumer_listener; - } else { - // When the queue is not empty, we need to look at the front buffer - // state to see if we need to replace it - auto front(core->queue.begin()); - - if (front->is_droppable) { - // If the front queued buffer is still being tracked, we first - // 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 - slots[front->slot].frame_number = 0; - } - // Overwrite the droppable buffer with the incoming one - *front = item; - frame_replaced_listener = core->consumer_listener; - } else { - core->queue.push_back(item); - frame_available_listener = core->consumer_listener; - } - } - - core->buffer_has_been_queued = true; - core->SignalDequeueCondition(); - output->Inflate(core->default_width, core->default_height, core->transform_hint, - static_cast<u32>(core->queue.size())); - - // 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 - 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 - { - std::scoped_lock lock{callback_mutex}; - while (callback_ticket != current_callback_ticket) { - callback_condition.wait(callback_mutex); - } - - if (frame_available_listener != nullptr) { - frame_available_listener->OnFrameAvailable(item); - } else if (frame_replaced_listener != nullptr) { - frame_replaced_listener->OnFrameReplaced(item); - } - - ++current_callback_ticket; - callback_condition.notify_all(); - } - - return Status::NoError; -} - -void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) { - LOG_DEBUG(Service_NVFlinger, "slot {}", slot); - - std::scoped_lock lock{core->mutex}; - - if (core->is_abandoned) { - LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); - return; - } - - if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { - LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot, - BufferQueueDefs::NUM_BUFFER_SLOTS); - return; - } else if (slots[slot].buffer_state != BufferState::Dequeued) { - LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot, - slots[slot].buffer_state); - return; - } - - slots[slot].buffer_state = BufferState::Free; - slots[slot].frame_number = 0; - slots[slot].fence = fence; - - core->SignalDequeueCondition(); - buffer_wait_event->Signal(); -} - -Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) { - std::scoped_lock lock{core->mutex}; - - if (out_value == nullptr) { - LOG_ERROR(Service_NVFlinger, "outValue was nullptr"); - return Status::BadValue; - } - - if (core->is_abandoned) { - LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); - return Status::NoInit; - } - - u32 value{}; - switch (what) { - case NativeWindow::Width: - value = core->default_width; - break; - case NativeWindow::Height: - value = core->default_height; - break; - case NativeWindow::Format: - value = static_cast<u32>(core->default_buffer_format); - break; - case NativeWindow::MinUndequeedBuffers: - value = core->GetMinUndequeuedBufferCountLocked(false); - break; - case NativeWindow::StickyTransform: - value = sticky_transform; - break; - case NativeWindow::ConsumerRunningBehind: - value = (core->queue.size() > 1); - break; - case NativeWindow::ConsumerUsageBits: - value = core->consumer_usage_bit; - break; - default: - ASSERT(false); - return Status::BadValue; - } - - LOG_DEBUG(Service_NVFlinger, "what = {}, value = {}", what, value); - - *out_value = static_cast<s32>(value); - - return Status::NoError; -} - -Status BufferQueueProducer::Connect(const std::shared_ptr<IProducerListener>& listener, - NativeWindowApi api, bool producer_controlled_by_app, - QueueBufferOutput* output) { - std::scoped_lock lock{core->mutex}; - - LOG_DEBUG(Service_NVFlinger, "api = {} producer_controlled_by_app = {}", api, - producer_controlled_by_app); - - if (core->is_abandoned) { - LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); - return Status::NoInit; - } - - if (core->consumer_listener == nullptr) { - LOG_ERROR(Service_NVFlinger, "BufferQueue has no consumer"); - return Status::NoInit; - } - - if (output == nullptr) { - LOG_ERROR(Service_NVFlinger, "output was nullptr"); - return Status::BadValue; - } - - if (core->connected_api != NativeWindowApi::NoConnectedApi) { - LOG_ERROR(Service_NVFlinger, "already connected (cur = {} req = {})", core->connected_api, - api); - return Status::BadValue; - } - - Status status = Status::NoError; - switch (api) { - case NativeWindowApi::Egl: - case NativeWindowApi::Cpu: - case NativeWindowApi::Media: - case NativeWindowApi::Camera: - core->connected_api = api; - output->Inflate(core->default_width, core->default_height, core->transform_hint, - static_cast<u32>(core->queue.size())); - core->connected_producer_listener = listener; - break; - default: - LOG_ERROR(Service_NVFlinger, "unknown api = {}", api); - status = Status::BadValue; - break; - } - - core->buffer_has_been_queued = false; - core->dequeue_buffer_cannot_block = - core->consumer_controlled_by_app && producer_controlled_by_app; - - return status; -} - -Status BufferQueueProducer::Disconnect(NativeWindowApi api) { - LOG_DEBUG(Service_NVFlinger, "api = {}", api); - - Status status = Status::NoError; - std::shared_ptr<IConsumerListener> listener; - - { - std::scoped_lock lock{core->mutex}; - - core->WaitWhileAllocatingLocked(); - - if (core->is_abandoned) { - // Disconnecting after the surface has been abandoned is a no-op. - return Status::NoError; - } - - // HACK: We are not Android. Remove handle for items in queue, and clear queue. - // Allows synchronous destruction of nvmap handles. - for (auto& item : core->queue) { - nvmap.FreeHandle(item.graphic_buffer->BufferId(), true); - } - core->queue.clear(); - - switch (api) { - case NativeWindowApi::Egl: - case NativeWindowApi::Cpu: - case NativeWindowApi::Media: - case NativeWindowApi::Camera: - if (core->connected_api == api) { - core->FreeAllBuffersLocked(); - core->connected_producer_listener = nullptr; - core->connected_api = NativeWindowApi::NoConnectedApi; - core->SignalDequeueCondition(); - buffer_wait_event->Signal(); - listener = core->consumer_listener; - } else { - LOG_ERROR(Service_NVFlinger, "still connected to another api (cur = {} req = {})", - core->connected_api, api); - status = Status::BadValue; - } - break; - default: - LOG_ERROR(Service_NVFlinger, "unknown api = {}", api); - status = Status::BadValue; - break; - } - } - - // Call back without lock held - if (listener != nullptr) { - listener->OnBuffersReleased(); - } - - return status; -} - -Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot, - const std::shared_ptr<GraphicBuffer>& buffer) { - LOG_DEBUG(Service_NVFlinger, "slot {}", slot); - - if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { - return Status::BadValue; - } - - std::scoped_lock lock{core->mutex}; - - slots[slot] = {}; - slots[slot].graphic_buffer = 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. - if (buffer) { - slots[slot].is_preallocated = true; - - core->override_max_buffer_count = core->GetPreallocatedBufferCountLocked(); - core->default_width = buffer->Width(); - core->default_height = buffer->Height(); - core->default_buffer_format = buffer->Format(); - } - - core->SignalDequeueCondition(); - buffer_wait_event->Signal(); - - return Status::NoError; -} - -void BufferQueueProducer::Transact(Kernel::HLERequestContext& ctx, TransactionId code, u32 flags) { - Status status{Status::NoError}; - Parcel parcel_in{ctx.ReadBuffer()}; - Parcel parcel_out{}; - - switch (code) { - case TransactionId::Connect: { - const auto enable_listener = parcel_in.Read<bool>(); - const auto api = parcel_in.Read<NativeWindowApi>(); - const auto producer_controlled_by_app = parcel_in.Read<bool>(); - - UNIMPLEMENTED_IF_MSG(enable_listener, "Listener is unimplemented!"); - - std::shared_ptr<IProducerListener> listener; - QueueBufferOutput output{}; - - status = Connect(listener, api, producer_controlled_by_app, &output); - - parcel_out.Write(output); - break; - } - case TransactionId::SetPreallocatedBuffer: { - const auto slot = parcel_in.Read<s32>(); - const auto buffer = parcel_in.ReadObject<GraphicBuffer>(); - - status = SetPreallocatedBuffer(slot, buffer); - break; - } - case TransactionId::DequeueBuffer: { - const auto is_async = parcel_in.Read<bool>(); - const auto width = parcel_in.Read<u32>(); - const auto height = parcel_in.Read<u32>(); - const auto pixel_format = parcel_in.Read<PixelFormat>(); - const auto usage = parcel_in.Read<u32>(); - - s32 slot{}; - Fence fence{}; - - status = DequeueBuffer(&slot, &fence, is_async, width, height, pixel_format, usage); - - parcel_out.Write(slot); - parcel_out.WriteObject(&fence); - break; - } - case TransactionId::RequestBuffer: { - const auto slot = parcel_in.Read<s32>(); - - std::shared_ptr<GraphicBuffer> buf; - - status = RequestBuffer(slot, &buf); - - parcel_out.WriteObject(buf); - break; - } - case TransactionId::QueueBuffer: { - const auto slot = parcel_in.Read<s32>(); - - QueueBufferInput input{parcel_in}; - QueueBufferOutput output; - - status = QueueBuffer(slot, input, &output); - - parcel_out.Write(output); - break; - } - case TransactionId::Query: { - const auto what = parcel_in.Read<NativeWindow>(); - - s32 value{}; - - status = Query(what, &value); - - parcel_out.Write(value); - break; - } - case TransactionId::CancelBuffer: { - const auto slot = parcel_in.Read<s32>(); - const auto fence = parcel_in.ReadFlattened<Fence>(); - - CancelBuffer(slot, fence); - break; - } - case TransactionId::Disconnect: { - const auto api = parcel_in.Read<NativeWindowApi>(); - - status = Disconnect(api); - break; - } - case TransactionId::DetachBuffer: { - const auto slot = parcel_in.Read<s32>(); - - status = DetachBuffer(slot); - break; - } - case TransactionId::SetBufferCount: { - const auto buffer_count = parcel_in.Read<s32>(); - - status = SetBufferCount(buffer_count); - break; - } - case TransactionId::GetBufferHistory: - LOG_WARNING(Service_NVFlinger, "(STUBBED) called, transaction=GetBufferHistory"); - break; - default: - ASSERT_MSG(false, "Unimplemented TransactionId {}", code); - break; - } - - parcel_out.Write(status); - - ctx.WriteBuffer(parcel_out.Serialize()); -} - -Kernel::KReadableEvent& BufferQueueProducer::GetNativeHandle() { - return buffer_wait_event->GetReadableEvent(); -} - -} // namespace Service::android |