mirror of https://git.suyu.dev/suyu/suyu
Merge pull request #132 from Subv/nvflinger
VI: Move BufferQueue and NVFlinger to their own folder/namespace.merge-requests/60/head
commit
7f19a7d305
@ -0,0 +1,96 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue.h"
|
||||
|
||||
namespace Service {
|
||||
namespace NVFlinger {
|
||||
|
||||
BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) {
|
||||
native_handle = Kernel::Event::Create(Kernel::ResetType::OneShot, "BufferQueue NativeHandle");
|
||||
}
|
||||
|
||||
void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) {
|
||||
Buffer buffer{};
|
||||
buffer.slot = slot;
|
||||
buffer.igbp_buffer = igbp_buffer;
|
||||
buffer.status = Buffer::Status::Free;
|
||||
|
||||
LOG_WARNING(Service, "Adding graphics buffer %u", slot);
|
||||
|
||||
queue.emplace_back(buffer);
|
||||
}
|
||||
|
||||
u32 BufferQueue::DequeueBuffer(u32 pixel_format, u32 width, u32 height) {
|
||||
auto 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;
|
||||
|
||||
// Make sure that the parameters match.
|
||||
auto& igbp_buffer = buffer.igbp_buffer;
|
||||
return igbp_buffer.format == pixel_format && igbp_buffer.width == width &&
|
||||
igbp_buffer.height == height;
|
||||
});
|
||||
ASSERT(itr != queue.end());
|
||||
|
||||
itr->status = Buffer::Status::Dequeued;
|
||||
return itr->slot;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void BufferQueue::QueueBuffer(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::Dequeued);
|
||||
itr->status = Buffer::Status::Queued;
|
||||
}
|
||||
|
||||
boost::optional<const BufferQueue::Buffer&> BufferQueue::AcquireBuffer() {
|
||||
auto itr = std::find_if(queue.begin(), queue.end(), [](const Buffer& buffer) {
|
||||
return buffer.status == Buffer::Status::Queued;
|
||||
});
|
||||
if (itr == queue.end())
|
||||
return boost::none;
|
||||
itr->status = Buffer::Status::Acquired;
|
||||
return *itr;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
u32 BufferQueue::Query(QueryType type) {
|
||||
LOG_WARNING(Service, "(STUBBED) called type=%u", static_cast<u32>(type));
|
||||
switch (type) {
|
||||
case QueryType::NativeWindowFormat:
|
||||
// TODO(Subv): Use an enum for this
|
||||
static constexpr u32 FormatABGR8 = 1;
|
||||
return FormatABGR8;
|
||||
}
|
||||
|
||||
UNIMPLEMENTED();
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace NVFlinger
|
||||
} // namespace Service
|
@ -0,0 +1,82 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <boost/optional.hpp>
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
|
||||
namespace CoreTiming {
|
||||
struct EventType;
|
||||
}
|
||||
|
||||
namespace Service {
|
||||
namespace NVFlinger {
|
||||
|
||||
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(17);
|
||||
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,
|
||||
};
|
||||
|
||||
BufferQueue(u32 id, u64 layer_id);
|
||||
~BufferQueue() = default;
|
||||
|
||||
struct Buffer {
|
||||
enum class Status { Free = 0, Queued = 1, Dequeued = 2, Acquired = 3 };
|
||||
|
||||
u32 slot;
|
||||
Status status = Status::Free;
|
||||
IGBPBuffer igbp_buffer;
|
||||
};
|
||||
|
||||
void SetPreallocatedBuffer(u32 slot, IGBPBuffer& buffer);
|
||||
u32 DequeueBuffer(u32 pixel_format, u32 width, u32 height);
|
||||
const IGBPBuffer& RequestBuffer(u32 slot) const;
|
||||
void QueueBuffer(u32 slot);
|
||||
boost::optional<const Buffer&> AcquireBuffer();
|
||||
void ReleaseBuffer(u32 slot);
|
||||
u32 Query(QueryType type);
|
||||
|
||||
u32 GetId() const {
|
||||
return id;
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event> GetNativeHandle() const {
|
||||
return native_handle;
|
||||
}
|
||||
|
||||
private:
|
||||
u32 id;
|
||||
u64 layer_id;
|
||||
|
||||
std::vector<Buffer> queue;
|
||||
Kernel::SharedPtr<Kernel::Event> native_handle;
|
||||
};
|
||||
|
||||
} // namespace NVFlinger
|
||||
} // namespace Service
|
@ -0,0 +1,161 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
|
||||
#include "core/hle/service/nvdrv/nvdrv.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue.h"
|
||||
#include "core/hle/service/nvflinger/nvflinger.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
namespace Service {
|
||||
namespace NVFlinger {
|
||||
|
||||
constexpr size_t SCREEN_REFRESH_RATE = 60;
|
||||
constexpr u64 frame_ticks = static_cast<u64>(BASE_CLOCK_RATE / SCREEN_REFRESH_RATE);
|
||||
|
||||
NVFlinger::NVFlinger() {
|
||||
// Add the different displays to the list of displays.
|
||||
Display default_{0, "Default"};
|
||||
Display external{1, "External"};
|
||||
Display edid{2, "Edid"};
|
||||
Display internal{3, "Internal"};
|
||||
|
||||
displays.emplace_back(default_);
|
||||
displays.emplace_back(external);
|
||||
displays.emplace_back(edid);
|
||||
displays.emplace_back(internal);
|
||||
|
||||
// Schedule the screen composition events
|
||||
composition_event =
|
||||
CoreTiming::RegisterEvent("ScreenCompositioin", [this](u64 userdata, int cycles_late) {
|
||||
Compose();
|
||||
CoreTiming::ScheduleEvent(frame_ticks - cycles_late, composition_event);
|
||||
});
|
||||
|
||||
CoreTiming::ScheduleEvent(frame_ticks, composition_event);
|
||||
}
|
||||
|
||||
NVFlinger::~NVFlinger() {
|
||||
CoreTiming::UnscheduleEvent(composition_event, 0);
|
||||
}
|
||||
|
||||
u64 NVFlinger::OpenDisplay(const std::string& name) {
|
||||
LOG_WARNING(Service, "Opening display %s", name.c_str());
|
||||
|
||||
// TODO(Subv): Currently we only support the Default display.
|
||||
ASSERT(name == "Default");
|
||||
|
||||
auto itr = std::find_if(displays.begin(), displays.end(),
|
||||
[&](const Display& display) { return display.name == name; });
|
||||
|
||||
ASSERT(itr != displays.end());
|
||||
|
||||
return itr->id;
|
||||
}
|
||||
|
||||
u64 NVFlinger::CreateLayer(u64 display_id) {
|
||||
auto& display = GetDisplay(display_id);
|
||||
|
||||
ASSERT_MSG(display.layers.empty(), "Only one layer is supported per display at the moment");
|
||||
|
||||
u64 layer_id = next_layer_id++;
|
||||
u32 buffer_queue_id = next_buffer_queue_id++;
|
||||
auto buffer_queue = std::make_shared<BufferQueue>(buffer_queue_id, layer_id);
|
||||
display.layers.emplace_back(layer_id, buffer_queue);
|
||||
buffer_queues.emplace_back(std::move(buffer_queue));
|
||||
return layer_id;
|
||||
}
|
||||
|
||||
u32 NVFlinger::GetBufferQueueId(u64 display_id, u64 layer_id) {
|
||||
const auto& layer = GetLayer(display_id, layer_id);
|
||||
return layer.buffer_queue->GetId();
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event> NVFlinger::GetVsyncEvent(u64 display_id) {
|
||||
const auto& display = GetDisplay(display_id);
|
||||
return display.vsync_event;
|
||||
}
|
||||
|
||||
std::shared_ptr<BufferQueue> NVFlinger::GetBufferQueue(u32 id) const {
|
||||
auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
|
||||
[&](const auto& queue) { return queue->GetId() == id; });
|
||||
|
||||
ASSERT(itr != buffer_queues.end());
|
||||
return *itr;
|
||||
}
|
||||
|
||||
Display& NVFlinger::GetDisplay(u64 display_id) {
|
||||
auto itr = std::find_if(displays.begin(), displays.end(),
|
||||
[&](const Display& display) { return display.id == display_id; });
|
||||
|
||||
ASSERT(itr != displays.end());
|
||||
return *itr;
|
||||
}
|
||||
|
||||
Layer& NVFlinger::GetLayer(u64 display_id, u64 layer_id) {
|
||||
auto& display = GetDisplay(display_id);
|
||||
|
||||
auto itr = std::find_if(display.layers.begin(), display.layers.end(),
|
||||
[&](const Layer& layer) { return layer.id == layer_id; });
|
||||
|
||||
ASSERT(itr != display.layers.end());
|
||||
return *itr;
|
||||
}
|
||||
|
||||
void NVFlinger::Compose() {
|
||||
for (auto& display : displays) {
|
||||
// Trigger vsync for this display at the end of drawing
|
||||
SCOPE_EXIT({ display.vsync_event->Signal(); });
|
||||
|
||||
// Don't do anything for displays without layers.
|
||||
if (display.layers.empty())
|
||||
continue;
|
||||
|
||||
// TODO(Subv): Support more than 1 layer.
|
||||
ASSERT_MSG(display.layers.size() == 1, "Max 1 layer per display is supported");
|
||||
|
||||
Layer& layer = display.layers[0];
|
||||
auto& buffer_queue = layer.buffer_queue;
|
||||
|
||||
// Search for a queued buffer and acquire it
|
||||
auto buffer = buffer_queue->AcquireBuffer();
|
||||
|
||||
if (buffer == boost::none) {
|
||||
// There was no queued buffer to draw, render previous frame
|
||||
VideoCore::g_renderer->SwapBuffers({});
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& igbp_buffer = buffer->igbp_buffer;
|
||||
|
||||
// Now send the buffer to the GPU for drawing.
|
||||
auto nvdrv = Nvidia::nvdrv.lock();
|
||||
ASSERT(nvdrv);
|
||||
|
||||
// TODO(Subv): Support more than just disp0. The display device selection is probably based
|
||||
// on which display we're drawing (Default, Internal, External, etc)
|
||||
auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0");
|
||||
ASSERT(nvdisp);
|
||||
|
||||
nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.format,
|
||||
igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride);
|
||||
|
||||
buffer_queue->ReleaseBuffer(buffer->slot);
|
||||
}
|
||||
}
|
||||
|
||||
Layer::Layer(u64 id, std::shared_ptr<BufferQueue> queue) : id(id), buffer_queue(std::move(queue)) {}
|
||||
|
||||
Display::Display(u64 id, std::string name) : id(id), name(std::move(name)) {
|
||||
vsync_event = Kernel::Event::Create(Kernel::ResetType::Pulse, "Display VSync Event");
|
||||
}
|
||||
|
||||
} // namespace NVFlinger
|
||||
} // namespace Service
|
@ -0,0 +1,84 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <boost/optional.hpp>
|
||||
#include "core/hle/kernel/event.h"
|
||||
|
||||
namespace CoreTiming {
|
||||
struct EventType;
|
||||
}
|
||||
|
||||
namespace Service {
|
||||
namespace NVFlinger {
|
||||
|
||||
class BufferQueue;
|
||||
|
||||
struct Layer {
|
||||
Layer(u64 id, std::shared_ptr<BufferQueue> queue);
|
||||
~Layer() = default;
|
||||
|
||||
u64 id;
|
||||
std::shared_ptr<BufferQueue> buffer_queue;
|
||||
};
|
||||
|
||||
struct Display {
|
||||
Display(u64 id, std::string name);
|
||||
~Display() = default;
|
||||
|
||||
u64 id;
|
||||
std::string name;
|
||||
|
||||
std::vector<Layer> layers;
|
||||
Kernel::SharedPtr<Kernel::Event> vsync_event;
|
||||
};
|
||||
|
||||
class NVFlinger final {
|
||||
public:
|
||||
NVFlinger();
|
||||
~NVFlinger();
|
||||
|
||||
/// Opens the specified display and returns the id.
|
||||
u64 OpenDisplay(const std::string& name);
|
||||
|
||||
/// Creates a layer on the specified display and returns the layer id.
|
||||
u64 CreateLayer(u64 display_id);
|
||||
|
||||
/// Gets the buffer queue id of the specified layer in the specified display.
|
||||
u32 GetBufferQueueId(u64 display_id, u64 layer_id);
|
||||
|
||||
/// Gets the vsync event for the specified display.
|
||||
Kernel::SharedPtr<Kernel::Event> GetVsyncEvent(u64 display_id);
|
||||
|
||||
/// Obtains a buffer queue identified by the id.
|
||||
std::shared_ptr<BufferQueue> GetBufferQueue(u32 id) const;
|
||||
|
||||
/// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when
|
||||
/// finished.
|
||||
void Compose();
|
||||
|
||||
private:
|
||||
/// Returns the display identified by the specified id.
|
||||
Display& GetDisplay(u64 display_id);
|
||||
|
||||
/// Returns the layer identified by the specified id in the desired display.
|
||||
Layer& GetLayer(u64 display_id, u64 layer_id);
|
||||
|
||||
std::vector<Display> displays;
|
||||
std::vector<std::shared_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;
|
||||
/// Id to use for the next buffer queue that is created, this counter is shared among all
|
||||
/// layers.
|
||||
u32 next_buffer_queue_id = 1;
|
||||
|
||||
/// CoreTiming event that handles screen composition.
|
||||
CoreTiming::EventType* composition_event;
|
||||
};
|
||||
|
||||
} // namespace NVFlinger
|
||||
} // namespace Service
|
Loading…
Reference in New Issue