Browse Source
Merge pull request #8088 from bunnei/fixup-nvflinger
Merge pull request #8088 from bunnei/fixup-nvflinger
Follow-up fixes for NVFlinger rewritence_cpp
committed by
GitHub
9 changed files with 136 additions and 547 deletions
-
4src/core/hle/service/nvflinger/buffer_item_consumer.cpp
-
206src/core/hle/service/nvflinger/buffer_queue.cpp
-
154src/core/hle/service/nvflinger/buffer_queue.h
-
182src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
-
26src/core/hle/service/nvflinger/buffer_queue_core.cpp
-
22src/core/hle/service/nvflinger/buffer_queue_core.h
-
79src/core/hle/service/nvflinger/buffer_queue_producer.cpp
-
2src/core/hle/service/nvflinger/buffer_queue_producer.h
-
8src/core/hle/service/nvflinger/consumer_base.cpp
@ -1,206 +0,0 @@ |
|||||
// 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/k_writable_event.h"
|
|
||||
#include "core/hle/kernel/kernel.h"
|
|
||||
#include "core/hle/service/kernel_helpers.h"
|
|
||||
#include "core/hle/service/nvflinger/buffer_queue.h"
|
|
||||
|
|
||||
namespace Service::NVFlinger { |
|
||||
|
|
||||
BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_, |
|
||||
KernelHelpers::ServiceContext& service_context_) |
|
||||
: id(id_), layer_id(layer_id_), service_context{service_context_} { |
|
||||
buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent"); |
|
||||
} |
|
||||
|
|
||||
BufferQueue::~BufferQueue() { |
|
||||
service_context.CloseEvent(buffer_wait_event); |
|
||||
} |
|
||||
|
|
||||
void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) { |
|
||||
ASSERT(slot < buffer_slots); |
|
||||
LOG_WARNING(Service, "Adding graphics buffer {}", slot); |
|
||||
|
|
||||
{ |
|
||||
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->GetWritableEvent().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 (!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 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; |
|
||||
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->GetWritableEvent().Signal(); |
|
||||
} |
|
||||
|
|
||||
std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() { |
|
||||
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 (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{free_buffers_mutex}; |
|
||||
free_buffers.push_back(slot); |
|
||||
} |
|
||||
free_buffers_condition.notify_one(); |
|
||||
|
|
||||
buffer_wait_event->GetWritableEvent().Signal(); |
|
||||
} |
|
||||
|
|
||||
void BufferQueue::Connect() { |
|
||||
std::unique_lock lock{queue_sequence_mutex}; |
|
||||
queue_sequence.clear(); |
|
||||
is_connect = true; |
|
||||
} |
|
||||
|
|
||||
void BufferQueue::Disconnect() { |
|
||||
buffers.fill({}); |
|
||||
{ |
|
||||
std::unique_lock lock{queue_sequence_mutex}; |
|
||||
queue_sequence.clear(); |
|
||||
} |
|
||||
buffer_wait_event->GetWritableEvent().Signal(); |
|
||||
is_connect = false; |
|
||||
free_buffers_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); |
|
||||
case QueryType::NativeWindowWidth: |
|
||||
case QueryType::NativeWindowHeight: |
|
||||
break; |
|
||||
case QueryType::NativeWindowMinUndequeuedBuffers: |
|
||||
return 0; |
|
||||
case QueryType::NativeWindowConsumerUsageBits: |
|
||||
return 0; |
|
||||
} |
|
||||
UNIMPLEMENTED_MSG("Unimplemented query type={}", type); |
|
||||
return 0; |
|
||||
} |
|
||||
|
|
||||
Kernel::KWritableEvent& BufferQueue::GetWritableBufferWaitEvent() { |
|
||||
return buffer_wait_event->GetWritableEvent(); |
|
||||
} |
|
||||
|
|
||||
Kernel::KReadableEvent& BufferQueue::GetBufferWaitEvent() { |
|
||||
return buffer_wait_event->GetReadableEvent(); |
|
||||
} |
|
||||
|
|
||||
} // namespace Service::NVFlinger
|
|
||||
@ -1,154 +0,0 @@ |
|||||
// Copyright 2018 yuzu emulator team |
|
||||
// Licensed under GPLv2 or any later version |
|
||||
// Refer to the license.txt file included. |
|
||||
|
|
||||
#pragma once |
|
||||
|
|
||||
#include <condition_variable> |
|
||||
#include <list> |
|
||||
#include <mutex> |
|
||||
#include <optional> |
|
||||
|
|
||||
#include "common/common_funcs.h" |
|
||||
#include "common/math_util.h" |
|
||||
#include "common/swap.h" |
|
||||
#include "core/hle/kernel/k_event.h" |
|
||||
#include "core/hle/kernel/k_readable_event.h" |
|
||||
#include "core/hle/service/nvdrv/nvdata.h" |
|
||||
|
|
||||
namespace Kernel { |
|
||||
class KernelCore; |
|
||||
class KEvent; |
|
||||
class KReadableEvent; |
|
||||
class KWritableEvent; |
|
||||
} // namespace Kernel |
|
||||
|
|
||||
namespace Service::KernelHelpers { |
|
||||
class ServiceContext; |
|
||||
} // namespace Service::KernelHelpers |
|
||||
|
|
||||
namespace Service::NVFlinger { |
|
||||
|
|
||||
constexpr u32 buffer_slots = 0x40; |
|
||||
struct IGBPBuffer { |
|
||||
u32_le magic; |
|
||||
u32_le width; |
|
||||
u32_le height; |
|
||||
u32_le stride; |
|
||||
u32_le format; |
|
||||
u32_le usage; |
|
||||
INSERT_PADDING_WORDS(1); |
|
||||
u32_le index; |
|
||||
INSERT_PADDING_WORDS(3); |
|
||||
u32_le gpu_buffer_id; |
|
||||
INSERT_PADDING_WORDS(6); |
|
||||
u32_le external_format; |
|
||||
INSERT_PADDING_WORDS(10); |
|
||||
u32_le nvmap_handle; |
|
||||
u32_le offset; |
|
||||
INSERT_PADDING_WORDS(60); |
|
||||
}; |
|
||||
|
|
||||
static_assert(sizeof(IGBPBuffer) == 0x16C, "IGBPBuffer has wrong size"); |
|
||||
|
|
||||
class BufferQueue final { |
|
||||
public: |
|
||||
enum class QueryType { |
|
||||
NativeWindowWidth = 0, |
|
||||
NativeWindowHeight = 1, |
|
||||
NativeWindowFormat = 2, |
|
||||
/// The minimum number of buffers that must remain un-dequeued after a buffer has been |
|
||||
/// queued |
|
||||
NativeWindowMinUndequeuedBuffers = 3, |
|
||||
/// The consumer gralloc usage bits currently set by the consumer |
|
||||
NativeWindowConsumerUsageBits = 10, |
|
||||
}; |
|
||||
|
|
||||
explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_, |
|
||||
KernelHelpers::ServiceContext& service_context_); |
|
||||
~BufferQueue(); |
|
||||
|
|
||||
enum class BufferTransformFlags : u32 { |
|
||||
/// No transform flags are set |
|
||||
Unset = 0x00, |
|
||||
/// Flip source image horizontally (around the vertical axis) |
|
||||
FlipH = 0x01, |
|
||||
/// Flip source image vertically (around the horizontal axis) |
|
||||
FlipV = 0x02, |
|
||||
/// Rotate source image 90 degrees clockwise |
|
||||
Rotate90 = 0x04, |
|
||||
/// Rotate source image 180 degrees |
|
||||
Rotate180 = 0x03, |
|
||||
/// Rotate source image 270 degrees clockwise |
|
||||
Rotate270 = 0x07, |
|
||||
}; |
|
||||
|
|
||||
enum class PixelFormat : u32 { |
|
||||
RGBA8888 = 1, |
|
||||
RGBX8888 = 2, |
|
||||
RGB888 = 3, |
|
||||
RGB565 = 4, |
|
||||
BGRA8888 = 5, |
|
||||
RGBA5551 = 6, |
|
||||
RRGBA4444 = 7, |
|
||||
}; |
|
||||
|
|
||||
struct Buffer { |
|
||||
enum class Status { Free = 0, Queued = 1, Dequeued = 2, Acquired = 3 }; |
|
||||
|
|
||||
u32 slot; |
|
||||
Status status = Status::Free; |
|
||||
IGBPBuffer igbp_buffer; |
|
||||
BufferTransformFlags transform; |
|
||||
Common::Rectangle<int> crop_rect; |
|
||||
u32 swap_interval; |
|
||||
Service::Nvidia::MultiFence multi_fence; |
|
||||
}; |
|
||||
|
|
||||
void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer); |
|
||||
std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> DequeueBuffer(u32 width, |
|
||||
u32 height); |
|
||||
const IGBPBuffer& RequestBuffer(u32 slot) const; |
|
||||
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); |
|
||||
|
|
||||
u32 GetId() const { |
|
||||
return id; |
|
||||
} |
|
||||
|
|
||||
bool IsConnected() const { |
|
||||
return is_connect; |
|
||||
} |
|
||||
|
|
||||
Kernel::KWritableEvent& GetWritableBufferWaitEvent(); |
|
||||
|
|
||||
Kernel::KReadableEvent& GetBufferWaitEvent(); |
|
||||
|
|
||||
private: |
|
||||
BufferQueue(const BufferQueue&) = delete; |
|
||||
|
|
||||
u32 id{}; |
|
||||
u64 layer_id{}; |
|
||||
std::atomic_bool is_connect{}; |
|
||||
|
|
||||
std::list<u32> free_buffers; |
|
||||
std::array<Buffer, buffer_slots> buffers; |
|
||||
std::list<u32> queue_sequence; |
|
||||
Kernel::KEvent* buffer_wait_event{}; |
|
||||
|
|
||||
std::mutex free_buffers_mutex; |
|
||||
std::condition_variable free_buffers_condition; |
|
||||
|
|
||||
std::mutex queue_sequence_mutex; |
|
||||
|
|
||||
KernelHelpers::ServiceContext& service_context; |
|
||||
}; |
|
||||
|
|
||||
} // namespace Service::NVFlinger |
|
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue