mirror of
				https://git.suyu.dev/suyu/suyu.git
				synced 2025-11-04 12:34:39 +08:00 
			
		
		
		
	hle: nvflinger: Add implementation for BufferQueueProducer class.
This commit is contained in:
		
							parent
							
								
									bfff7b58fd
								
							
						
					
					
						commit
						56284bff6c
					
				@ -535,8 +535,6 @@ add_library(core STATIC
 | 
			
		||||
    hle/service/nvdrv/nvmemp.h
 | 
			
		||||
    hle/service/nvdrv/syncpoint_manager.cpp
 | 
			
		||||
    hle/service/nvdrv/syncpoint_manager.h
 | 
			
		||||
    hle/service/nvflinger/buffer_queue.cpp
 | 
			
		||||
    hle/service/nvflinger/buffer_queue.h
 | 
			
		||||
    hle/service/nvflinger/binder.h
 | 
			
		||||
    hle/service/nvflinger/buffer_item.h
 | 
			
		||||
    hle/service/nvflinger/buffer_item_consumer.cpp
 | 
			
		||||
@ -546,6 +544,8 @@ add_library(core STATIC
 | 
			
		||||
    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_queue_producer.cpp
 | 
			
		||||
    hle/service/nvflinger/buffer_queue_producer.h
 | 
			
		||||
    hle/service/nvflinger/buffer_slot.h
 | 
			
		||||
    hle/service/nvflinger/buffer_transform_flags.h
 | 
			
		||||
    hle/service/nvflinger/consumer_base.cpp
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										936
									
								
								src/core/hle/service/nvflinger/buffer_queue_producer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										936
									
								
								src/core/hle/service/nvflinger/buffer_queue_producer.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,936 @@
 | 
			
		||||
// 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/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/k_writable_event.h"
 | 
			
		||||
#include "core/hle/kernel/kernel.h"
 | 
			
		||||
#include "core/hle/service/kernel_helpers.h"
 | 
			
		||||
#include "core/hle/service/nvdrv/nvdrv.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 android {
 | 
			
		||||
 | 
			
		||||
BufferQueueProducer::BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_,
 | 
			
		||||
                                         std::shared_ptr<BufferQueueCore> buffer_queue_core_)
 | 
			
		||||
    : service_context{service_context_}, core{std::move(buffer_queue_core_)}, slots(core->slots) {
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    BufferQueueCore::AutoLock lock(core);
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        BufferQueueCore::AutoLock lock(core);
 | 
			
		||||
        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->GetWritableEvent().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* returnFlags) 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);
 | 
			
		||||
                *returnFlags |= Status::ReleaseAllBuffers;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
            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;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        *found = BufferQueueCore::INVALID_BUFFER_SLOT;
 | 
			
		||||
 | 
			
		||||
        // 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());
 | 
			
		||||
        } else {
 | 
			
		||||
            if (!core->free_buffers.empty()) {
 | 
			
		||||
                auto slot = core->free_buffers.begin();
 | 
			
		||||
                *found = *slot;
 | 
			
		||||
                core->free_buffers.erase(slot);
 | 
			
		||||
            } else if (core->allow_allocation && !core->free_slots.empty()) {
 | 
			
		||||
                auto slot = core->free_slots.begin();
 | 
			
		||||
                // Only return free slots up to the max buffer count
 | 
			
		||||
                if (*slot < max_buffer_count) {
 | 
			
		||||
                    *found = *slot;
 | 
			
		||||
                    core->free_slots.erase(slot);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 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()) {
 | 
			
		||||
                // 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) {
 | 
			
		||||
    { BufferQueueCore::AutoLock lock(core); }
 | 
			
		||||
 | 
			
		||||
    LOG_DEBUG(Service_NVFlinger, "async={} w={} h={} format={}, usage={}", async ? "true" : "false",
 | 
			
		||||
              width, height, format, usage);
 | 
			
		||||
 | 
			
		||||
    if ((width && !height) || (!width && height)) {
 | 
			
		||||
        LOG_ERROR(Service_NVFlinger, "invalid size: w={} h={}", width, height);
 | 
			
		||||
        return Status::BadValue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Status return_flags = Status::NoError;
 | 
			
		||||
    bool attached_by_consumer = false;
 | 
			
		||||
    {
 | 
			
		||||
        BufferQueueCore::AutoLock lock(core);
 | 
			
		||||
        core->WaitWhileAllocatingLocked();
 | 
			
		||||
        if (format == PixelFormat::NoFormat) {
 | 
			
		||||
            format = core->default_buffer_format;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Enable the usage bits the consumer requested
 | 
			
		||||
        usage |= core->consumer_usage_bit;
 | 
			
		||||
        const bool use_default_size = !width && !height;
 | 
			
		||||
        if (use_default_size) {
 | 
			
		||||
            width = core->default_width;
 | 
			
		||||
            height = core->default_height;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        s32 found = BufferItem::INVALID_BUFFER_SLOT;
 | 
			
		||||
        while (found == BufferItem::INVALID_BUFFER_SLOT) {
 | 
			
		||||
            Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags);
 | 
			
		||||
            if (status != Status::NoError) {
 | 
			
		||||
                return status;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // This should not happen
 | 
			
		||||
            if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
 | 
			
		||||
                LOG_DEBUG(Service_NVFlinger, "no available buffer slots");
 | 
			
		||||
                return Status::Busy;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const std::shared_ptr<GraphicBuffer>& buffer(slots[found].graphic_buffer);
 | 
			
		||||
 | 
			
		||||
            // If we are not allowed to allocate new buffers, WaitForFreeSlotThenRelock must have
 | 
			
		||||
            // returned a slot containing a buffer. If this buffer would require reallocation to
 | 
			
		||||
            // meet the requested attributes, we free it and attempt to get another one.
 | 
			
		||||
            if (!core->allow_allocation) {
 | 
			
		||||
                if (buffer->NeedsReallocation(width, height, format, usage)) {
 | 
			
		||||
                    core->FreeBufferLocked(found);
 | 
			
		||||
                    found = BufferItem::INVALID_BUFFER_SLOT;
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        *out_slot = found;
 | 
			
		||||
        attached_by_consumer = slots[found].attached_by_consumer;
 | 
			
		||||
        slots[found].buffer_state = BufferState::Dequeued;
 | 
			
		||||
 | 
			
		||||
        const std::shared_ptr<GraphicBuffer>& buffer(slots[found].graphic_buffer);
 | 
			
		||||
 | 
			
		||||
        if ((buffer == nullptr) || buffer->NeedsReallocation(width, height, format, usage)) {
 | 
			
		||||
            slots[found].acquire_called = false;
 | 
			
		||||
            slots[found].graphic_buffer = nullptr;
 | 
			
		||||
            slots[found].request_buffer_called = false;
 | 
			
		||||
            slots[found].fence = Fence::NoFence();
 | 
			
		||||
            core->buffer_age = 0;
 | 
			
		||||
            return_flags |= Status::BufferNeedsReallocation;
 | 
			
		||||
        } else {
 | 
			
		||||
            // We add 1 because that will be the frame number when this buffer
 | 
			
		||||
            // is queued
 | 
			
		||||
            core->buffer_age = core->frame_counter + 1 - slots[found].frame_number;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        LOG_DEBUG(Service_NVFlinger, "setting buffer age to {}", core->buffer_age);
 | 
			
		||||
 | 
			
		||||
        *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;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            BufferQueueCore::AutoLock lock(core);
 | 
			
		||||
            if (core->is_abandoned) {
 | 
			
		||||
                LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
 | 
			
		||||
                return Status::NoInit;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            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);
 | 
			
		||||
 | 
			
		||||
    BufferQueueCore::AutoLock lock(core);
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    BufferQueueCore::AutoLock lock(core);
 | 
			
		||||
 | 
			
		||||
    core->WaitWhileAllocatingLocked();
 | 
			
		||||
 | 
			
		||||
    if (core->is_abandoned) {
 | 
			
		||||
        LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
 | 
			
		||||
        return Status::NoInit;
 | 
			
		||||
    }
 | 
			
		||||
    if (core->free_buffers.empty()) {
 | 
			
		||||
        return Status::NoMemory;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const s32 found = core->free_buffers.front();
 | 
			
		||||
    core->free_buffers.remove(found);
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    BufferQueueCore::AutoLock lock(core);
 | 
			
		||||
    core->WaitWhileAllocatingLocked();
 | 
			
		||||
 | 
			
		||||
    Status return_flags = Status::NoError;
 | 
			
		||||
    s32 found{};
 | 
			
		||||
 | 
			
		||||
    const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags);
 | 
			
		||||
    if (status != Status::NoError) {
 | 
			
		||||
        return status;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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{};
 | 
			
		||||
    Rect 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> frameAvailableListener;
 | 
			
		||||
    std::shared_ptr<IConsumerListener> frameReplacedListener;
 | 
			
		||||
    s32 callback_ticket{};
 | 
			
		||||
    BufferItem item;
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        BufferQueueCore::AutoLock lock(core);
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
        Rect buffer_rect(graphic_buffer->Width(), graphic_buffer->Height());
 | 
			
		||||
        Rect cropped_rect;
 | 
			
		||||
        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;
 | 
			
		||||
        sticky_transform = sticky_transform_;
 | 
			
		||||
 | 
			
		||||
        if (core->queue.empty()) {
 | 
			
		||||
            // When the queue is empty, we can simply queue this buffer
 | 
			
		||||
            core->queue.push_back(item);
 | 
			
		||||
            frameAvailableListener = 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;
 | 
			
		||||
                    core->free_buffers.push_front(front->slot);
 | 
			
		||||
                }
 | 
			
		||||
                // Overwrite the droppable buffer with the incoming one
 | 
			
		||||
                *front = item;
 | 
			
		||||
                frameReplacedListener = core->consumer_listener;
 | 
			
		||||
            } else {
 | 
			
		||||
                core->queue.push_back(item);
 | 
			
		||||
                frameAvailableListener = 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::unique_lock lock(callback_mutex);
 | 
			
		||||
        while (callback_ticket != current_callback_ticket) {
 | 
			
		||||
            std::unique_lock<std::mutex> lk(callback_mutex);
 | 
			
		||||
            callback_condition.wait(lk);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (frameAvailableListener != nullptr) {
 | 
			
		||||
            frameAvailableListener->OnFrameAvailable(item);
 | 
			
		||||
        } else if (frameReplacedListener != nullptr) {
 | 
			
		||||
            frameReplacedListener->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);
 | 
			
		||||
 | 
			
		||||
    BufferQueueCore::AutoLock lock(core);
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    core->free_buffers.push_front(slot);
 | 
			
		||||
    slots[slot].buffer_state = BufferState::Free;
 | 
			
		||||
    slots[slot].fence = fence;
 | 
			
		||||
 | 
			
		||||
    core->SignalDequeueCondition();
 | 
			
		||||
    buffer_wait_event->GetWritableEvent().Signal();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) {
 | 
			
		||||
    BufferQueueCore::AutoLock lock(core);
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    s32 value{};
 | 
			
		||||
    switch (what) {
 | 
			
		||||
    case NativeWindow::Width:
 | 
			
		||||
        value = static_cast<s32>(core->default_width);
 | 
			
		||||
        break;
 | 
			
		||||
    case NativeWindow::Height:
 | 
			
		||||
        value = static_cast<s32>(core->default_height);
 | 
			
		||||
        break;
 | 
			
		||||
    case NativeWindow::Format:
 | 
			
		||||
        value = static_cast<s32>(core->default_buffer_format);
 | 
			
		||||
        break;
 | 
			
		||||
    case NativeWindow::MinUndequeedBuffers:
 | 
			
		||||
        value = core->GetMinUndequeuedBufferCountLocked(false);
 | 
			
		||||
        break;
 | 
			
		||||
    case NativeWindow::StickyTransform:
 | 
			
		||||
        value = static_cast<s32>(sticky_transform);
 | 
			
		||||
        break;
 | 
			
		||||
    case NativeWindow::ConsumerRunningBehind:
 | 
			
		||||
        value = (core->queue.size() > 1);
 | 
			
		||||
        break;
 | 
			
		||||
    case NativeWindow::ConsumerUsageBits:
 | 
			
		||||
        value = static_cast<s32>(core->consumer_usage_bit);
 | 
			
		||||
        break;
 | 
			
		||||
    case NativeWindow::BufferAge:
 | 
			
		||||
        if (core->buffer_age > INT32_MAX) {
 | 
			
		||||
            value = 0;
 | 
			
		||||
        } else {
 | 
			
		||||
            value = static_cast<s32>(core->buffer_age);
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        UNREACHABLE();
 | 
			
		||||
        return Status::BadValue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    LOG_DEBUG(Service_NVFlinger, "what = {}, value = {}", what, value);
 | 
			
		||||
 | 
			
		||||
    *out_value = value;
 | 
			
		||||
 | 
			
		||||
    return Status::NoError;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Status BufferQueueProducer::Connect(const std::shared_ptr<IProducerListener>& listener,
 | 
			
		||||
                                    NativeWindowApi api, bool producer_controlled_by_app,
 | 
			
		||||
                                    QueueBufferOutput* output) {
 | 
			
		||||
    BufferQueueCore::AutoLock lock(core);
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
    core->allow_allocation = true;
 | 
			
		||||
 | 
			
		||||
    return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
 | 
			
		||||
    LOG_DEBUG(Service_NVFlinger, "api = {}", api);
 | 
			
		||||
 | 
			
		||||
    Status status = Status::NoError;
 | 
			
		||||
    std::shared_ptr<IConsumerListener> listener;
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        BufferQueueCore::AutoLock lock(core);
 | 
			
		||||
 | 
			
		||||
        core->WaitWhileAllocatingLocked();
 | 
			
		||||
 | 
			
		||||
        if (core->is_abandoned) {
 | 
			
		||||
            // Disconnecting after the surface has been abandoned is a no-op.
 | 
			
		||||
            return Status::NoError;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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->GetWritableEvent().Signal();
 | 
			
		||||
                listener = core->consumer_listener;
 | 
			
		||||
            } else if (core->connected_api != NativeWindowApi::NoConnectedApi) {
 | 
			
		||||
                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);
 | 
			
		||||
 | 
			
		||||
    UNIMPLEMENTED_IF_MSG(!buffer, "buffer must be valid!");
 | 
			
		||||
 | 
			
		||||
    if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
 | 
			
		||||
        return Status::BadValue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    BufferQueueCore::AutoLock lock(core);
 | 
			
		||||
 | 
			
		||||
    slots[slot] = {};
 | 
			
		||||
    slots[slot].is_preallocated = true;
 | 
			
		||||
    slots[slot].graphic_buffer = buffer;
 | 
			
		||||
 | 
			
		||||
    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->GetWritableEvent().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");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    parcel_out.Write(status);
 | 
			
		||||
 | 
			
		||||
    ctx.WriteBuffer(parcel_out.Serialize());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Kernel::KReadableEvent& BufferQueueProducer::GetNativeHandle() {
 | 
			
		||||
    return buffer_wait_event->GetReadableEvent();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace android
 | 
			
		||||
							
								
								
									
										83
									
								
								src/core/hle/service/nvflinger/buffer_queue_producer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								src/core/hle/service/nvflinger/buffer_queue_producer.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,83 @@
 | 
			
		||||
// 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/BufferQueueProducer.h
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <condition_variable>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
 | 
			
		||||
#include "common/common_funcs.h"
 | 
			
		||||
#include "core/hle/service/nvdrv/nvdata.h"
 | 
			
		||||
#include "core/hle/service/nvflinger/binder.h"
 | 
			
		||||
#include "core/hle/service/nvflinger/buffer_queue_defs.h"
 | 
			
		||||
#include "core/hle/service/nvflinger/buffer_slot.h"
 | 
			
		||||
#include "core/hle/service/nvflinger/graphic_buffer_producer.h"
 | 
			
		||||
#include "core/hle/service/nvflinger/pixel_format.h"
 | 
			
		||||
#include "core/hle/service/nvflinger/status.h"
 | 
			
		||||
#include "core/hle/service/nvflinger/window.h"
 | 
			
		||||
 | 
			
		||||
namespace Kernel {
 | 
			
		||||
class KernelCore;
 | 
			
		||||
class KEvent;
 | 
			
		||||
class KReadableEvent;
 | 
			
		||||
class KWritableEvent;
 | 
			
		||||
} // namespace Kernel
 | 
			
		||||
 | 
			
		||||
namespace Service::KernelHelpers {
 | 
			
		||||
class ServiceContext;
 | 
			
		||||
} // namespace Service::KernelHelpers
 | 
			
		||||
 | 
			
		||||
namespace android {
 | 
			
		||||
 | 
			
		||||
class BufferQueueCore;
 | 
			
		||||
class IProducerListener;
 | 
			
		||||
 | 
			
		||||
class BufferQueueProducer final : public IBinder {
 | 
			
		||||
public:
 | 
			
		||||
    explicit BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_,
 | 
			
		||||
                                 std::shared_ptr<BufferQueueCore> buffer_queue_core_);
 | 
			
		||||
    ~BufferQueueProducer();
 | 
			
		||||
 | 
			
		||||
    void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code, u32 flags) override;
 | 
			
		||||
 | 
			
		||||
    Kernel::KReadableEvent& GetNativeHandle() override;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    Status RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf);
 | 
			
		||||
    Status SetBufferCount(s32 buffer_count);
 | 
			
		||||
    Status DequeueBuffer(s32* out_slot, android::Fence* out_fence, bool async, u32 width,
 | 
			
		||||
                         u32 height, PixelFormat format, u32 usage);
 | 
			
		||||
    Status DetachBuffer(s32 slot);
 | 
			
		||||
    Status DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out_buffer, Fence* out_fence);
 | 
			
		||||
    Status AttachBuffer(s32* outSlot, const std::shared_ptr<GraphicBuffer>& buffer);
 | 
			
		||||
    Status QueueBuffer(s32 slot, const QueueBufferInput& input, QueueBufferOutput* output);
 | 
			
		||||
    void CancelBuffer(s32 slot, const Fence& fence);
 | 
			
		||||
    Status Query(NativeWindow what, s32* out_value);
 | 
			
		||||
    Status Connect(const std::shared_ptr<IProducerListener>& listener, NativeWindowApi api,
 | 
			
		||||
                   bool producer_controlled_by_app, QueueBufferOutput* output);
 | 
			
		||||
 | 
			
		||||
    Status Disconnect(NativeWindowApi api);
 | 
			
		||||
    Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr<GraphicBuffer>& buffer);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    BufferQueueProducer(const BufferQueueProducer&) = delete;
 | 
			
		||||
 | 
			
		||||
    Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* returnFlags) const;
 | 
			
		||||
 | 
			
		||||
    Kernel::KEvent* buffer_wait_event{};
 | 
			
		||||
    Service::KernelHelpers::ServiceContext& service_context;
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<BufferQueueCore> core;
 | 
			
		||||
    BufferQueueDefs::SlotsType& slots;
 | 
			
		||||
    u32 sticky_transform{};
 | 
			
		||||
    std::mutex callback_mutex;
 | 
			
		||||
    s32 next_callback_ticket{};
 | 
			
		||||
    s32 current_callback_ticket{};
 | 
			
		||||
    std::condition_variable callback_condition;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace android
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user