diff options
| author | Levi <L3ehunin@gmail.com> | 2021-01-10 22:09:56 -0700 |
|---|---|---|
| committer | Levi <L3ehunin@gmail.com> | 2021-01-10 22:09:56 -0700 |
| commit | 7a3c884e39fccfbb498b855080bffabc9ce2e7f1 (patch) | |
| tree | 5056f9406dec188439cb0deb87603498243a9412 /src/core/hle/service/nvflinger | |
| parent | bc69cc151192326f9b8e18bbda831f1589ba27e0 (diff) | |
| parent | 46cd71d1c773c29cce8b48e7e2b478bdf6d77085 (diff) | |
Merge remote-tracking branch 'upstream/master' into int-flags
Diffstat (limited to 'src/core/hle/service/nvflinger')
| -rw-r--r-- | src/core/hle/service/nvflinger/buffer_queue.cpp | 168 | ||||
| -rw-r--r-- | src/core/hle/service/nvflinger/buffer_queue.h | 23 | ||||
| -rw-r--r-- | src/core/hle/service/nvflinger/nvflinger.cpp | 38 | ||||
| -rw-r--r-- | src/core/hle/service/nvflinger/nvflinger.h | 9 |
4 files changed, 153 insertions, 85 deletions
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp index 637b310d7..5578181a4 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue.cpp @@ -22,127 +22,169 @@ BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id, u64 layer_id) BufferQueue::~BufferQueue() = default; void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) { + ASSERT(slot < buffer_slots); LOG_WARNING(Service, "Adding graphics buffer {}", slot); - free_buffers.push_back(slot); - queue.push_back({ + { + std::unique_lock lock{free_buffers_mutex}; + free_buffers.push_back(slot); + } + free_buffers_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{free_buffers_mutex}; + free_buffers_condition.wait(lock, [this] { return !free_buffers.empty() || !is_connect; }); + } - if (free_buffers.empty()) { + 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{free_buffers_mutex}; + auto f_itr = free_buffers.begin(); - auto itr = queue.end(); + auto slot = buffers.size(); 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()) { + 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 (itr == queue.end()) { + if (slot == buffers.size()) { return std::nullopt; } - - itr->status = Buffer::Status::Dequeued; - return {{itr->slot, &itr->multi_fence}}; + buffers[slot].status = Buffer::Status::Dequeued; + return {{buffers[slot].slot, &buffers[slot].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; + 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) { - 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; + 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; + std::unique_lock lock{queue_sequence_mutex}; 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{free_buffers_mutex}; + free_buffers.push_back(slot); + } + free_buffers_condition.notify_one(); + + buffer_wait_event.writable->Signal(); +} + std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() { - auto itr = queue.end(); + std::unique_lock lock{queue_sequence_mutex}; + std::size_t buffer_slot = buffers.size(); // Iterate to find a queued buffer matching the requested slot. - while (itr == queue.end() && !queue_sequence.empty()) { - const 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; - }); + 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 (itr == queue.end()) { + if (buffer_slot == buffers.size()) { return std::nullopt; } - itr->status = Buffer::Status::Acquired; - return *itr; + buffers[buffer_slot].status = Buffer::Status::Acquired; + return {{buffers[buffer_slot]}}; } 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); + 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{free_buffers_mutex}; + free_buffers.push_back(slot); + } + free_buffers_condition.notify_one(); buffer_wait_event.writable->Signal(); } -void BufferQueue::Disconnect() { - queue.clear(); +void BufferQueue::Connect() { + std::unique_lock lock{queue_sequence_mutex}; queue_sequence.clear(); - id = 1; - layer_id = 1; + is_connect = true; +} + +void BufferQueue::Disconnect() { + buffers.fill({}); + { + std::unique_lock lock{queue_sequence_mutex}; + queue_sequence.clear(); + } + buffer_wait_event.writable->Signal(); + is_connect = false; + free_buffers_condition.notify_one(); } u32 BufferQueue::Query(QueryType type) { - LOG_WARNING(Service, "(STUBBED) called type={}", static_cast<u32>(type)); + LOG_WARNING(Service, "(STUBBED) called type={}", type); switch (type) { case QueryType::NativeWindowFormat: return static_cast<u32>(PixelFormat::RGBA8888); + case QueryType::NativeWindowWidth: + case QueryType::NativeWindowHeight: + break; } - - UNIMPLEMENTED(); + UNIMPLEMENTED_MSG("Unimplemented query type={}", type); return 0; } diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h index 8a837e5aa..ad7469277 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.h +++ b/src/core/hle/service/nvflinger/buffer_queue.h @@ -4,7 +4,9 @@ #pragma once +#include <condition_variable> #include <list> +#include <mutex> #include <optional> #include <vector> @@ -21,6 +23,7 @@ class KernelCore; namespace Service::NVFlinger { +constexpr u32 buffer_slots = 0x40; struct IGBPBuffer { u32_le magic; u32_le width; @@ -95,8 +98,10 @@ public: void QueueBuffer(u32 slot, BufferTransformFlags transform, const Common::Rectangle<int>& crop_rect, u32 swap_interval, Service::Nvidia::MultiFence& multi_fence); + void CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence); std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer(); void ReleaseBuffer(u32 slot); + void Connect(); void Disconnect(); u32 Query(QueryType type); @@ -104,18 +109,30 @@ public: return id; } + bool IsConnected() const { + return is_connect; + } + std::shared_ptr<Kernel::WritableEvent> GetWritableBufferWaitEvent() const; std::shared_ptr<Kernel::ReadableEvent> GetBufferWaitEvent() const; private: - u32 id; - u64 layer_id; + BufferQueue(const BufferQueue&) = delete; + + u32 id{}; + u64 layer_id{}; + std::atomic_bool is_connect{}; std::list<u32> free_buffers; - std::vector<Buffer> queue; + std::array<Buffer, buffer_slots> buffers; std::list<u32> queue_sequence; Kernel::EventPair buffer_wait_event; + + std::mutex free_buffers_mutex; + std::condition_variable free_buffers_condition; + + std::mutex queue_sequence_mutex; }; } // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index c64673dba..4b3581949 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -88,6 +88,10 @@ NVFlinger::NVFlinger(Core::System& system) : system(system) { } NVFlinger::~NVFlinger() { + for (auto& buffer_queue : buffer_queues) { + buffer_queue->Disconnect(); + } + if (system.IsMulticore()) { is_running = false; wait_event->Set(); @@ -104,6 +108,8 @@ void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { } std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) { + const auto guard = Lock(); + LOG_DEBUG(Service, "Opening \"{}\" display", name); // TODO(Subv): Currently we only support the Default display. @@ -121,6 +127,7 @@ std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) { } std::optional<u64> NVFlinger::CreateLayer(u64 display_id) { + const auto guard = Lock(); auto* const display = FindDisplay(display_id); if (display == nullptr) { @@ -129,18 +136,22 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) { const u64 layer_id = next_layer_id++; const u32 buffer_queue_id = next_buffer_queue_id++; - buffer_queues.emplace_back(system.Kernel(), buffer_queue_id, layer_id); - display->CreateLayer(layer_id, buffer_queues.back()); + buffer_queues.emplace_back( + std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id)); + display->CreateLayer(layer_id, *buffer_queues.back()); return layer_id; } void NVFlinger::CloseLayer(u64 layer_id) { + const auto guard = Lock(); + for (auto& display : displays) { display.CloseLayer(layer_id); } } std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) const { + const auto guard = Lock(); const auto* const layer = FindLayer(display_id, layer_id); if (layer == nullptr) { @@ -151,6 +162,7 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) co } std::shared_ptr<Kernel::ReadableEvent> NVFlinger::FindVsyncEvent(u64 display_id) const { + const auto guard = Lock(); auto* const display = FindDisplay(display_id); if (display == nullptr) { @@ -160,20 +172,16 @@ std::shared_ptr<Kernel::ReadableEvent> NVFlinger::FindVsyncEvent(u64 display_id) return display->GetVSyncEvent(); } -BufferQueue& NVFlinger::FindBufferQueue(u32 id) { +BufferQueue* NVFlinger::FindBufferQueue(u32 id) { + const auto guard = Lock(); const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(), - [id](const auto& queue) { return queue.GetId() == id; }); - - ASSERT(itr != buffer_queues.end()); - return *itr; -} + [id](const auto& queue) { return queue->GetId() == id; }); -const BufferQueue& NVFlinger::FindBufferQueue(u32 id) const { - const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(), - [id](const auto& queue) { return queue.GetId() == id; }); + if (itr == buffer_queues.end()) { + return nullptr; + } - ASSERT(itr != buffer_queues.end()); - return *itr; + return itr->get(); } VI::Display* NVFlinger::FindDisplay(u64 display_id) { @@ -242,6 +250,10 @@ void NVFlinger::Compose() { const auto& igbp_buffer = buffer->get().igbp_buffer; + if (!system.IsPoweredOn()) { + return; // We are likely shutting down + } + auto& gpu = system.GPU(); const auto& multi_fence = buffer->get().multi_fence; guard->unlock(); diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 1ebe949c0..c6765259f 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -75,10 +75,7 @@ public: [[nodiscard]] std::shared_ptr<Kernel::ReadableEvent> FindVsyncEvent(u64 display_id) const; /// Obtains a buffer queue identified by the ID. - [[nodiscard]] BufferQueue& FindBufferQueue(u32 id); - - /// Obtains a buffer queue identified by the ID. - [[nodiscard]] const BufferQueue& FindBufferQueue(u32 id) const; + [[nodiscard]] BufferQueue* FindBufferQueue(u32 id); /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when /// finished. @@ -86,11 +83,11 @@ public: [[nodiscard]] s64 GetNextTicks() const; +private: [[nodiscard]] std::unique_lock<std::mutex> Lock() const { return std::unique_lock{*guard}; } -private: /// Finds the display identified by the specified ID. [[nodiscard]] VI::Display* FindDisplay(u64 display_id); @@ -110,7 +107,7 @@ private: std::shared_ptr<Nvidia::Module> nvdrv; std::vector<VI::Display> displays; - std::vector<BufferQueue> buffer_queues; + std::vector<std::unique_ptr<BufferQueue>> buffer_queues; /// Id to use for the next layer that is created, this counter is shared among all displays. u64 next_layer_id = 1; |
