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