mirror of
https://github.com/yuzu-emu/yuzu.git
synced 2024-07-04 23:31:19 +01:00
service: nvflinger: Improve synchronization for BufferQueue.
- Use proper mechanisms for blocking on DequeueBuffer. - Ensure service thread terminates on emulation Shutdown.
This commit is contained in:
parent
bea51d948d
commit
6433b1dfd6
5 changed files with 72 additions and 19 deletions
|
@ -25,7 +25,12 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)
|
||||||
ASSERT(slot < buffer_slots);
|
ASSERT(slot < buffer_slots);
|
||||||
LOG_WARNING(Service, "Adding graphics buffer {}", slot);
|
LOG_WARNING(Service, "Adding graphics buffer {}", slot);
|
||||||
|
|
||||||
|
{
|
||||||
|
std::unique_lock lock{queue_mutex};
|
||||||
free_buffers.push_back(slot);
|
free_buffers.push_back(slot);
|
||||||
|
}
|
||||||
|
condition.notify_one();
|
||||||
|
|
||||||
buffers[slot] = {
|
buffers[slot] = {
|
||||||
.slot = slot,
|
.slot = slot,
|
||||||
.status = Buffer::Status::Free,
|
.status = Buffer::Status::Free,
|
||||||
|
@ -41,10 +46,20 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)
|
||||||
|
|
||||||
std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width,
|
std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width,
|
||||||
u32 height) {
|
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 (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;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_lock lock{queue_mutex};
|
||||||
|
|
||||||
auto f_itr = free_buffers.begin();
|
auto f_itr = free_buffers.begin();
|
||||||
auto slot = buffers.size();
|
auto slot = buffers.size();
|
||||||
|
|
||||||
|
@ -97,7 +112,11 @@ void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& mult
|
||||||
buffers[slot].multi_fence = multi_fence;
|
buffers[slot].multi_fence = multi_fence;
|
||||||
buffers[slot].swap_interval = 0;
|
buffers[slot].swap_interval = 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::unique_lock lock{queue_mutex};
|
||||||
free_buffers.push_back(slot);
|
free_buffers.push_back(slot);
|
||||||
|
}
|
||||||
|
condition.notify_one();
|
||||||
|
|
||||||
buffer_wait_event.writable->Signal();
|
buffer_wait_event.writable->Signal();
|
||||||
}
|
}
|
||||||
|
@ -127,15 +146,28 @@ void BufferQueue::ReleaseBuffer(u32 slot) {
|
||||||
ASSERT(buffers[slot].slot == slot);
|
ASSERT(buffers[slot].slot == slot);
|
||||||
|
|
||||||
buffers[slot].status = Buffer::Status::Free;
|
buffers[slot].status = Buffer::Status::Free;
|
||||||
|
{
|
||||||
|
std::unique_lock lock{queue_mutex};
|
||||||
free_buffers.push_back(slot);
|
free_buffers.push_back(slot);
|
||||||
|
}
|
||||||
|
condition.notify_one();
|
||||||
|
|
||||||
buffer_wait_event.writable->Signal();
|
buffer_wait_event.writable->Signal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BufferQueue::Connect() {
|
||||||
|
queue_sequence.clear();
|
||||||
|
id = 1;
|
||||||
|
layer_id = 1;
|
||||||
|
is_connect = true;
|
||||||
|
}
|
||||||
|
|
||||||
void BufferQueue::Disconnect() {
|
void BufferQueue::Disconnect() {
|
||||||
buffers.fill({});
|
buffers.fill({});
|
||||||
queue_sequence.clear();
|
queue_sequence.clear();
|
||||||
buffer_wait_event.writable->Signal();
|
buffer_wait_event.writable->Signal();
|
||||||
|
is_connect = false;
|
||||||
|
condition.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 BufferQueue::Query(QueryType type) {
|
u32 BufferQueue::Query(QueryType type) {
|
||||||
|
|
|
@ -4,7 +4,9 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <condition_variable>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
#include <mutex>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -99,6 +101,7 @@ public:
|
||||||
void CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence);
|
void CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence);
|
||||||
std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer();
|
std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer();
|
||||||
void ReleaseBuffer(u32 slot);
|
void ReleaseBuffer(u32 slot);
|
||||||
|
void Connect();
|
||||||
void Disconnect();
|
void Disconnect();
|
||||||
u32 Query(QueryType type);
|
u32 Query(QueryType type);
|
||||||
|
|
||||||
|
@ -106,18 +109,28 @@ public:
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsConnected() const {
|
||||||
|
return is_connect;
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<Kernel::WritableEvent> GetWritableBufferWaitEvent() const;
|
std::shared_ptr<Kernel::WritableEvent> GetWritableBufferWaitEvent() const;
|
||||||
|
|
||||||
std::shared_ptr<Kernel::ReadableEvent> GetBufferWaitEvent() const;
|
std::shared_ptr<Kernel::ReadableEvent> GetBufferWaitEvent() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
u32 id;
|
BufferQueue(const BufferQueue&) = delete;
|
||||||
u64 layer_id;
|
|
||||||
|
u32 id{};
|
||||||
|
u64 layer_id{};
|
||||||
|
std::atomic_bool is_connect{};
|
||||||
|
|
||||||
std::list<u32> free_buffers;
|
std::list<u32> free_buffers;
|
||||||
std::array<Buffer, buffer_slots> buffers;
|
std::array<Buffer, buffer_slots> buffers;
|
||||||
std::list<u32> queue_sequence;
|
std::list<u32> queue_sequence;
|
||||||
Kernel::EventPair buffer_wait_event;
|
Kernel::EventPair buffer_wait_event;
|
||||||
|
|
||||||
|
std::mutex queue_mutex;
|
||||||
|
std::condition_variable condition;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::NVFlinger
|
} // namespace Service::NVFlinger
|
||||||
|
|
|
@ -88,6 +88,10 @@ NVFlinger::NVFlinger(Core::System& system) : system(system) {
|
||||||
}
|
}
|
||||||
|
|
||||||
NVFlinger::~NVFlinger() {
|
NVFlinger::~NVFlinger() {
|
||||||
|
for (auto& buffer_queue : buffer_queues) {
|
||||||
|
buffer_queue->Disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
if (system.IsMulticore()) {
|
if (system.IsMulticore()) {
|
||||||
is_running = false;
|
is_running = false;
|
||||||
wait_event->Set();
|
wait_event->Set();
|
||||||
|
@ -132,8 +136,9 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
|
||||||
|
|
||||||
const u64 layer_id = next_layer_id++;
|
const u64 layer_id = next_layer_id++;
|
||||||
const u32 buffer_queue_id = next_buffer_queue_id++;
|
const u32 buffer_queue_id = next_buffer_queue_id++;
|
||||||
buffer_queues.emplace_back(system.Kernel(), buffer_queue_id, layer_id);
|
buffer_queues.emplace_back(
|
||||||
display->CreateLayer(layer_id, buffer_queues.back());
|
std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id));
|
||||||
|
display->CreateLayer(layer_id, *buffer_queues.back());
|
||||||
return layer_id;
|
return layer_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,13 +175,13 @@ std::shared_ptr<Kernel::ReadableEvent> NVFlinger::FindVsyncEvent(u64 display_id)
|
||||||
BufferQueue* NVFlinger::FindBufferQueue(u32 id) {
|
BufferQueue* NVFlinger::FindBufferQueue(u32 id) {
|
||||||
const auto guard = Lock();
|
const auto guard = Lock();
|
||||||
const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
|
const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
|
||||||
[id](const auto& queue) { return queue.GetId() == id; });
|
[id](const auto& queue) { return queue->GetId() == id; });
|
||||||
|
|
||||||
if (itr == buffer_queues.end()) {
|
if (itr == buffer_queues.end()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return &*itr;
|
return itr->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
VI::Display* NVFlinger::FindDisplay(u64 display_id) {
|
VI::Display* NVFlinger::FindDisplay(u64 display_id) {
|
||||||
|
|
|
@ -107,7 +107,7 @@ private:
|
||||||
std::shared_ptr<Nvidia::Module> nvdrv;
|
std::shared_ptr<Nvidia::Module> nvdrv;
|
||||||
|
|
||||||
std::vector<VI::Display> displays;
|
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.
|
/// Id to use for the next layer that is created, this counter is shared among all displays.
|
||||||
u64 next_layer_id = 1;
|
u64 next_layer_id = 1;
|
||||||
|
|
|
@ -544,6 +544,12 @@ private:
|
||||||
Settings::values.resolution_factor.GetValue()),
|
Settings::values.resolution_factor.GetValue()),
|
||||||
static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedHeight) *
|
static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedHeight) *
|
||||||
Settings::values.resolution_factor.GetValue())};
|
Settings::values.resolution_factor.GetValue())};
|
||||||
|
|
||||||
|
{
|
||||||
|
auto& buffer_queue = *nv_flinger.FindBufferQueue(id);
|
||||||
|
buffer_queue.Connect();
|
||||||
|
}
|
||||||
|
|
||||||
ctx.WriteBuffer(response.Serialize());
|
ctx.WriteBuffer(response.Serialize());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -565,18 +571,15 @@ private:
|
||||||
const u32 width{request.data.width};
|
const u32 width{request.data.width};
|
||||||
const u32 height{request.data.height};
|
const u32 height{request.data.height};
|
||||||
|
|
||||||
std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> result;
|
|
||||||
|
|
||||||
while (!result) {
|
|
||||||
auto& buffer_queue = *nv_flinger.FindBufferQueue(id);
|
auto& buffer_queue = *nv_flinger.FindBufferQueue(id);
|
||||||
result = buffer_queue.DequeueBuffer(width, height);
|
do {
|
||||||
|
if (auto result = buffer_queue.DequeueBuffer(width, height); result) {
|
||||||
if (result) {
|
|
||||||
// Buffer is available
|
// Buffer is available
|
||||||
IGBPDequeueBufferResponseParcel response{result->first, *result->second};
|
IGBPDequeueBufferResponseParcel response{result->first, *result->second};
|
||||||
ctx.WriteBuffer(response.Serialize());
|
ctx.WriteBuffer(response.Serialize());
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
} while (buffer_queue.IsConnected());
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue