// Copyright 2018 yuzu emulator team // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/writable_event.h" #include "core/hle/service/nvflinger/buffer_queue.h" namespace Service::NVFlinger { BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id, u64 layer_id) : id(id), layer_id(layer_id) { buffer_wait_event = Kernel::WritableEvent::CreateEventPair(kernel, "BufferQueue NativeHandle"); } BufferQueue::~BufferQueue() = default; void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) { LOG_WARNING(Service, "Adding graphics buffer {}", slot); Buffer buffer{}; buffer.slot = slot; buffer.igbp_buffer = igbp_buffer; buffer.status = Buffer::Status::Free; free_buffers.push_back(slot); queue.emplace_back(buffer); buffer_wait_event.writable->Signal(); } std::optional> BufferQueue::DequeueBuffer(u32 width, u32 height) { if (free_buffers.empty()) { return {}; } auto f_itr = free_buffers.begin(); auto itr = queue.end(); while (f_itr != free_buffers.end()) { auto slot = *f_itr; itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) { // Only consider free buffers. Buffers become free once again after they've been // Acquired and Released by the compositor, see the NVFlinger::Compose method. if (buffer.status != Buffer::Status::Free) { return false; } if (buffer.slot != slot) { return false; } // Make sure that the parameters match. return buffer.igbp_buffer.width == width && buffer.igbp_buffer.height == height; }); if (itr != queue.end()) { free_buffers.erase(f_itr); break; } ++f_itr; } if (itr == queue.end()) { return {}; } itr->status = Buffer::Status::Dequeued; return {{itr->slot, &itr->multi_fence}}; } const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const { auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) { return buffer.slot == slot; }); ASSERT(itr != queue.end()); ASSERT(itr->status == Buffer::Status::Dequeued); return itr->igbp_buffer; } void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform, const Common::Rectangle& crop_rect, u32 swap_interval, Service::Nvidia::MultiFence& multi_fence) { auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) { return buffer.slot == slot; }); ASSERT(itr != queue.end()); ASSERT(itr->status == Buffer::Status::Dequeued); itr->status = Buffer::Status::Queued; itr->transform = transform; itr->crop_rect = crop_rect; itr->swap_interval = swap_interval; itr->multi_fence = multi_fence; queue_sequence.push_back(slot); } std::optional> BufferQueue::AcquireBuffer() { auto itr = queue.end(); // Iterate to find a queued buffer matching the requested slot. while (itr == queue.end() && !queue_sequence.empty()) { u32 slot = queue_sequence.front(); itr = std::find_if(queue.begin(), queue.end(), [&slot](const Buffer& buffer) { return buffer.status == Buffer::Status::Queued && buffer.slot == slot; }); queue_sequence.pop_front(); } if (itr == queue.end()) return {}; itr->status = Buffer::Status::Acquired; return *itr; } void BufferQueue::ReleaseBuffer(u32 slot) { auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) { return buffer.slot == slot; }); ASSERT(itr != queue.end()); ASSERT(itr->status == Buffer::Status::Acquired); itr->status = Buffer::Status::Free; free_buffers.push_back(slot); buffer_wait_event.writable->Signal(); } void BufferQueue::Disconnect() { queue.clear(); queue_sequence.clear(); id = 1; layer_id = 1; } u32 BufferQueue::Query(QueryType type) { LOG_WARNING(Service, "(STUBBED) called type={}", static_cast(type)); switch (type) { case QueryType::NativeWindowFormat: // TODO(Subv): Use an enum for this static constexpr u32 FormatABGR8 = 1; return FormatABGR8; } UNIMPLEMENTED(); return 0; } std::shared_ptr BufferQueue::GetWritableBufferWaitEvent() const { return buffer_wait_event.writable; } std::shared_ptr BufferQueue::GetBufferWaitEvent() const { return buffer_wait_event.readable; } } // namespace Service::NVFlinger