| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- // Copyright 2018 yuzu emulator team
- // Licensed under GPLv2 or any later version
- // Refer to the license.txt file included.
- #include <algorithm>
- #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) {
- ASSERT(slot < buffer_slots);
- LOG_WARNING(Service, "Adding graphics buffer {}", slot);
- {
- std::unique_lock lock{queue_mutex};
- free_buffers.push_back(slot);
- }
- condition.notify_one();
- buffers[slot] = {
- .slot = slot,
- .status = Buffer::Status::Free,
- .igbp_buffer = igbp_buffer,
- .transform = {},
- .crop_rect = {},
- .swap_interval = 0,
- .multi_fence = {},
- };
- buffer_wait_event.writable->Signal();
- }
- std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width,
- u32 height) {
- // Wait for first request before trying to dequeue
- {
- std::unique_lock lock{queue_mutex};
- condition.wait(lock, [this] { return !free_buffers.empty() || !is_connect; });
- }
- if (!is_connect) {
- // Buffer was disconnected while the thread was blocked, this is most likely due to
- // emulation being stopped
- return std::nullopt;
- }
- std::unique_lock lock{queue_mutex};
- auto f_itr = free_buffers.begin();
- auto slot = buffers.size();
- while (f_itr != free_buffers.end()) {
- const Buffer& buffer = buffers[*f_itr];
- if (buffer.status == Buffer::Status::Free && buffer.igbp_buffer.width == width &&
- buffer.igbp_buffer.height == height) {
- slot = *f_itr;
- free_buffers.erase(f_itr);
- break;
- }
- ++f_itr;
- }
- if (slot == buffers.size()) {
- return std::nullopt;
- }
- buffers[slot].status = Buffer::Status::Dequeued;
- return {{buffers[slot].slot, &buffers[slot].multi_fence}};
- }
- const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const {
- ASSERT(slot < buffers.size());
- ASSERT(buffers[slot].status == Buffer::Status::Dequeued);
- ASSERT(buffers[slot].slot == slot);
- return buffers[slot].igbp_buffer;
- }
- void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
- const Common::Rectangle<int>& crop_rect, u32 swap_interval,
- Service::Nvidia::MultiFence& multi_fence) {
- ASSERT(slot < buffers.size());
- ASSERT(buffers[slot].status == Buffer::Status::Dequeued);
- ASSERT(buffers[slot].slot == slot);
- buffers[slot].status = Buffer::Status::Queued;
- buffers[slot].transform = transform;
- buffers[slot].crop_rect = crop_rect;
- buffers[slot].swap_interval = swap_interval;
- buffers[slot].multi_fence = multi_fence;
- queue_sequence.push_back(slot);
- }
- void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence) {
- ASSERT(slot < buffers.size());
- ASSERT(buffers[slot].status != Buffer::Status::Free);
- ASSERT(buffers[slot].slot == slot);
- buffers[slot].status = Buffer::Status::Free;
- buffers[slot].multi_fence = multi_fence;
- buffers[slot].swap_interval = 0;
- {
- std::unique_lock lock{queue_mutex};
- free_buffers.push_back(slot);
- }
- condition.notify_one();
- buffer_wait_event.writable->Signal();
- }
- std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
- std::size_t buffer_slot = buffers.size();
- // Iterate to find a queued buffer matching the requested slot.
- while (buffer_slot == buffers.size() && !queue_sequence.empty()) {
- const auto slot = static_cast<std::size_t>(queue_sequence.front());
- ASSERT(slot < buffers.size());
- if (buffers[slot].status == Buffer::Status::Queued) {
- ASSERT(buffers[slot].slot == slot);
- buffer_slot = slot;
- }
- queue_sequence.pop_front();
- }
- if (buffer_slot == buffers.size()) {
- return std::nullopt;
- }
- buffers[buffer_slot].status = Buffer::Status::Acquired;
- return {{buffers[buffer_slot]}};
- }
- void BufferQueue::ReleaseBuffer(u32 slot) {
- ASSERT(slot < buffers.size());
- ASSERT(buffers[slot].status == Buffer::Status::Acquired);
- ASSERT(buffers[slot].slot == slot);
- buffers[slot].status = Buffer::Status::Free;
- {
- std::unique_lock lock{queue_mutex};
- free_buffers.push_back(slot);
- }
- condition.notify_one();
- buffer_wait_event.writable->Signal();
- }
- void BufferQueue::Connect() {
- queue_sequence.clear();
- is_connect = true;
- }
- void BufferQueue::Disconnect() {
- buffers.fill({});
- queue_sequence.clear();
- buffer_wait_event.writable->Signal();
- is_connect = false;
- condition.notify_one();
- }
- u32 BufferQueue::Query(QueryType type) {
- LOG_WARNING(Service, "(STUBBED) called type={}", type);
- switch (type) {
- case QueryType::NativeWindowFormat:
- return static_cast<u32>(PixelFormat::RGBA8888);
- }
- UNIMPLEMENTED();
- return 0;
- }
- std::shared_ptr<Kernel::WritableEvent> BufferQueue::GetWritableBufferWaitEvent() const {
- return buffer_wait_event.writable;
- }
- std::shared_ptr<Kernel::ReadableEvent> BufferQueue::GetBufferWaitEvent() const {
- return buffer_wait_event.readable;
- }
- } // namespace Service::NVFlinger
|