9 changed files with 452 additions and 363 deletions
-
4src/core/CMakeLists.txt
-
96src/core/hle/service/nvflinger/buffer_queue.cpp
-
82src/core/hle/service/nvflinger/buffer_queue.h
-
161src/core/hle/service/nvflinger/nvflinger.cpp
-
84src/core/hle/service/nvflinger/nvflinger.h
-
250src/core/hle/service/vi/vi.cpp
-
130src/core/hle/service/vi/vi.h
-
2src/core/hle/service/vi/vi_m.cpp
-
6src/core/hle/service/vi/vi_m.h
@ -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 |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue