summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Subv2018-01-22 11:54:58 -0500
committerGravatar Subv2018-01-22 11:54:58 -0500
commit8d7686ff8e5a5be74d4808af4311d3746e4072f7 (patch)
treeafa1c5c7761d167d59ed5de3fad7a28d1edf9d0f /src
parentAdded stubs for audio services. (#116) (diff)
downloadyuzu-8d7686ff8e5a5be74d4808af4311d3746e4072f7.tar.gz
yuzu-8d7686ff8e5a5be74d4808af4311d3746e4072f7.tar.xz
yuzu-8d7686ff8e5a5be74d4808af4311d3746e4072f7.zip
VI: Move BufferQueue and NVFlinger to their own folder/namespace.
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp96
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h82
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp161
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h84
-rw-r--r--src/core/hle/service/vi/vi.cpp250
-rw-r--r--src/core/hle/service/vi/vi.h130
-rw-r--r--src/core/hle/service/vi/vi_m.cpp2
-rw-r--r--src/core/hle/service/vi/vi_m.h6
9 files changed, 452 insertions, 363 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index bef0d7a1e..242c2db0c 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -130,6 +130,10 @@ add_library(core STATIC
130 hle/service/nvdrv/nvdrv.h 130 hle/service/nvdrv/nvdrv.h
131 hle/service/nvdrv/nvmemp.cpp 131 hle/service/nvdrv/nvmemp.cpp
132 hle/service/nvdrv/nvmemp.h 132 hle/service/nvdrv/nvmemp.h
133 hle/service/nvflinger/buffer_queue.cpp
134 hle/service/nvflinger/buffer_queue.h
135 hle/service/nvflinger/nvflinger.cpp
136 hle/service/nvflinger/nvflinger.h
133 hle/service/pctl/pctl.cpp 137 hle/service/pctl/pctl.cpp
134 hle/service/pctl/pctl.h 138 hle/service/pctl/pctl.h
135 hle/service/pctl/pctl_a.cpp 139 hle/service/pctl/pctl_a.cpp
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
new file mode 100644
index 000000000..705bdbe5d
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -0,0 +1,96 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6
7#include "common/alignment.h"
8#include "common/scope_exit.h"
9#include "core/core_timing.h"
10#include "core/hle/service/nvflinger/buffer_queue.h"
11
12namespace Service {
13namespace NVFlinger {
14
15BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) {
16 native_handle = Kernel::Event::Create(Kernel::ResetType::OneShot, "BufferQueue NativeHandle");
17}
18
19void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) {
20 Buffer buffer{};
21 buffer.slot = slot;
22 buffer.igbp_buffer = igbp_buffer;
23 buffer.status = Buffer::Status::Free;
24
25 LOG_WARNING(Service, "Adding graphics buffer %u", slot);
26
27 queue.emplace_back(buffer);
28}
29
30u32 BufferQueue::DequeueBuffer(u32 pixel_format, u32 width, u32 height) {
31 auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) {
32 // Only consider free buffers. Buffers become free once again after they've been Acquired
33 // and Released by the compositor, see the NVFlinger::Compose method.
34 if (buffer.status != Buffer::Status::Free)
35 return false;
36
37 // Make sure that the parameters match.
38 auto& igbp_buffer = buffer.igbp_buffer;
39 return igbp_buffer.format == pixel_format && igbp_buffer.width == width &&
40 igbp_buffer.height == height;
41 });
42 ASSERT(itr != queue.end());
43
44 itr->status = Buffer::Status::Dequeued;
45 return itr->slot;
46}
47
48const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const {
49 auto itr = std::find_if(queue.begin(), queue.end(),
50 [&](const Buffer& buffer) { return buffer.slot == slot; });
51 ASSERT(itr != queue.end());
52 ASSERT(itr->status == Buffer::Status::Dequeued);
53 return itr->igbp_buffer;
54}
55
56void BufferQueue::QueueBuffer(u32 slot) {
57 auto itr = std::find_if(queue.begin(), queue.end(),
58 [&](const Buffer& buffer) { return buffer.slot == slot; });
59 ASSERT(itr != queue.end());
60 ASSERT(itr->status == Buffer::Status::Dequeued);
61 itr->status = Buffer::Status::Queued;
62}
63
64boost::optional<const BufferQueue::Buffer&> BufferQueue::AcquireBuffer() {
65 auto itr = std::find_if(queue.begin(), queue.end(), [](const Buffer& buffer) {
66 return buffer.status == Buffer::Status::Queued;
67 });
68 if (itr == queue.end())
69 return boost::none;
70 itr->status = Buffer::Status::Acquired;
71 return *itr;
72}
73
74void BufferQueue::ReleaseBuffer(u32 slot) {
75 auto itr = std::find_if(queue.begin(), queue.end(),
76 [&](const Buffer& buffer) { return buffer.slot == slot; });
77 ASSERT(itr != queue.end());
78 ASSERT(itr->status == Buffer::Status::Acquired);
79 itr->status = Buffer::Status::Free;
80}
81
82u32 BufferQueue::Query(QueryType type) {
83 LOG_WARNING(Service, "(STUBBED) called type=%u", static_cast<u32>(type));
84 switch (type) {
85 case QueryType::NativeWindowFormat:
86 // TODO(Subv): Use an enum for this
87 static constexpr u32 FormatABGR8 = 1;
88 return FormatABGR8;
89 }
90
91 UNIMPLEMENTED();
92 return 0;
93}
94
95} // namespace NVFlinger
96} // namespace Service
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
new file mode 100644
index 000000000..5c6719407
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -0,0 +1,82 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <vector>
8#include <boost/optional.hpp>
9#include "common/swap.h"
10#include "core/hle/kernel/event.h"
11
12namespace CoreTiming {
13struct EventType;
14}
15
16namespace Service {
17namespace NVFlinger {
18
19struct IGBPBuffer {
20 u32_le magic;
21 u32_le width;
22 u32_le height;
23 u32_le stride;
24 u32_le format;
25 u32_le usage;
26 INSERT_PADDING_WORDS(1);
27 u32_le index;
28 INSERT_PADDING_WORDS(3);
29 u32_le gpu_buffer_id;
30 INSERT_PADDING_WORDS(17);
31 u32_le nvmap_handle;
32 u32_le offset;
33 INSERT_PADDING_WORDS(60);
34};
35
36static_assert(sizeof(IGBPBuffer) == 0x16C, "IGBPBuffer has wrong size");
37
38class BufferQueue final {
39public:
40 enum class QueryType {
41 NativeWindowWidth = 0,
42 NativeWindowHeight = 1,
43 NativeWindowFormat = 2,
44 };
45
46 BufferQueue(u32 id, u64 layer_id);
47 ~BufferQueue() = default;
48
49 struct Buffer {
50 enum class Status { Free = 0, Queued = 1, Dequeued = 2, Acquired = 3 };
51
52 u32 slot;
53 Status status = Status::Free;
54 IGBPBuffer igbp_buffer;
55 };
56
57 void SetPreallocatedBuffer(u32 slot, IGBPBuffer& buffer);
58 u32 DequeueBuffer(u32 pixel_format, u32 width, u32 height);
59 const IGBPBuffer& RequestBuffer(u32 slot) const;
60 void QueueBuffer(u32 slot);
61 boost::optional<const Buffer&> AcquireBuffer();
62 void ReleaseBuffer(u32 slot);
63 u32 Query(QueryType type);
64
65 u32 GetId() const {
66 return id;
67 }
68
69 Kernel::SharedPtr<Kernel::Event> GetNativeHandle() const {
70 return native_handle;
71 }
72
73private:
74 u32 id;
75 u64 layer_id;
76
77 std::vector<Buffer> queue;
78 Kernel::SharedPtr<Kernel::Event> native_handle;
79};
80
81} // namespace NVFlinger
82} // namespace Service
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
new file mode 100644
index 000000000..fe622b986
--- /dev/null
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -0,0 +1,161 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6
7#include "common/alignment.h"
8#include "common/scope_exit.h"
9#include "core/core_timing.h"
10#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
11#include "core/hle/service/nvdrv/nvdrv.h"
12#include "core/hle/service/nvflinger/buffer_queue.h"
13#include "core/hle/service/nvflinger/nvflinger.h"
14#include "video_core/renderer_base.h"
15#include "video_core/video_core.h"
16
17namespace Service {
18namespace NVFlinger {
19
20constexpr size_t SCREEN_REFRESH_RATE = 60;
21constexpr u64 frame_ticks = static_cast<u64>(BASE_CLOCK_RATE / SCREEN_REFRESH_RATE);
22
23NVFlinger::NVFlinger() {
24 // Add the different displays to the list of displays.
25 Display default_{0, "Default"};
26 Display external{1, "External"};
27 Display edid{2, "Edid"};
28 Display internal{3, "Internal"};
29
30 displays.emplace_back(default_);
31 displays.emplace_back(external);
32 displays.emplace_back(edid);
33 displays.emplace_back(internal);
34
35 // Schedule the screen composition events
36 composition_event =
37 CoreTiming::RegisterEvent("ScreenCompositioin", [this](u64 userdata, int cycles_late) {
38 Compose();
39 CoreTiming::ScheduleEvent(frame_ticks - cycles_late, composition_event);
40 });
41
42 CoreTiming::ScheduleEvent(frame_ticks, composition_event);
43}
44
45NVFlinger::~NVFlinger() {
46 CoreTiming::UnscheduleEvent(composition_event, 0);
47}
48
49u64 NVFlinger::OpenDisplay(const std::string& name) {
50 LOG_WARNING(Service, "Opening display %s", name.c_str());
51
52 // TODO(Subv): Currently we only support the Default display.
53 ASSERT(name == "Default");
54
55 auto itr = std::find_if(displays.begin(), displays.end(),
56 [&](const Display& display) { return display.name == name; });
57
58 ASSERT(itr != displays.end());
59
60 return itr->id;
61}
62
63u64 NVFlinger::CreateLayer(u64 display_id) {
64 auto& display = GetDisplay(display_id);
65
66 ASSERT_MSG(display.layers.empty(), "Only one layer is supported per display at the moment");
67
68 u64 layer_id = next_layer_id++;
69 u32 buffer_queue_id = next_buffer_queue_id++;
70 auto buffer_queue = std::make_shared<BufferQueue>(buffer_queue_id, layer_id);
71 display.layers.emplace_back(layer_id, buffer_queue);
72 buffer_queues.emplace_back(std::move(buffer_queue));
73 return layer_id;
74}
75
76u32 NVFlinger::GetBufferQueueId(u64 display_id, u64 layer_id) {
77 const auto& layer = GetLayer(display_id, layer_id);
78 return layer.buffer_queue->GetId();
79}
80
81Kernel::SharedPtr<Kernel::Event> NVFlinger::GetVsyncEvent(u64 display_id) {
82 const auto& display = GetDisplay(display_id);
83 return display.vsync_event;
84}
85
86std::shared_ptr<BufferQueue> NVFlinger::GetBufferQueue(u32 id) const {
87 auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
88 [&](const auto& queue) { return queue->GetId() == id; });
89
90 ASSERT(itr != buffer_queues.end());
91 return *itr;
92}
93
94Display& NVFlinger::GetDisplay(u64 display_id) {
95 auto itr = std::find_if(displays.begin(), displays.end(),
96 [&](const Display& display) { return display.id == display_id; });
97
98 ASSERT(itr != displays.end());
99 return *itr;
100}
101
102Layer& NVFlinger::GetLayer(u64 display_id, u64 layer_id) {
103 auto& display = GetDisplay(display_id);
104
105 auto itr = std::find_if(display.layers.begin(), display.layers.end(),
106 [&](const Layer& layer) { return layer.id == layer_id; });
107
108 ASSERT(itr != display.layers.end());
109 return *itr;
110}
111
112void NVFlinger::Compose() {
113 for (auto& display : displays) {
114 // Trigger vsync for this display at the end of drawing
115 SCOPE_EXIT({ display.vsync_event->Signal(); });
116
117 // Don't do anything for displays without layers.
118 if (display.layers.empty())
119 continue;
120
121 // TODO(Subv): Support more than 1 layer.
122 ASSERT_MSG(display.layers.size() == 1, "Max 1 layer per display is supported");
123
124 Layer& layer = display.layers[0];
125 auto& buffer_queue = layer.buffer_queue;
126
127 // Search for a queued buffer and acquire it
128 auto buffer = buffer_queue->AcquireBuffer();
129
130 if (buffer == boost::none) {
131 // There was no queued buffer to draw, render previous frame
132 VideoCore::g_renderer->SwapBuffers({});
133 continue;
134 }
135
136 auto& igbp_buffer = buffer->igbp_buffer;
137
138 // Now send the buffer to the GPU for drawing.
139 auto nvdrv = Nvidia::nvdrv.lock();
140 ASSERT(nvdrv);
141
142 // TODO(Subv): Support more than just disp0. The display device selection is probably based
143 // on which display we're drawing (Default, Internal, External, etc)
144 auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0");
145 ASSERT(nvdisp);
146
147 nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.format,
148 igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride);
149
150 buffer_queue->ReleaseBuffer(buffer->slot);
151 }
152}
153
154Layer::Layer(u64 id, std::shared_ptr<BufferQueue> queue) : id(id), buffer_queue(std::move(queue)) {}
155
156Display::Display(u64 id, std::string name) : id(id), name(std::move(name)) {
157 vsync_event = Kernel::Event::Create(Kernel::ResetType::Pulse, "Display VSync Event");
158}
159
160} // namespace NVFlinger
161} // namespace Service
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
new file mode 100644
index 000000000..3126018ad
--- /dev/null
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -0,0 +1,84 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <boost/optional.hpp>
9#include "core/hle/kernel/event.h"
10
11namespace CoreTiming {
12struct EventType;
13}
14
15namespace Service {
16namespace NVFlinger {
17
18class BufferQueue;
19
20struct Layer {
21 Layer(u64 id, std::shared_ptr<BufferQueue> queue);
22 ~Layer() = default;
23
24 u64 id;
25 std::shared_ptr<BufferQueue> buffer_queue;
26};
27
28struct Display {
29 Display(u64 id, std::string name);
30 ~Display() = default;
31
32 u64 id;
33 std::string name;
34
35 std::vector<Layer> layers;
36 Kernel::SharedPtr<Kernel::Event> vsync_event;
37};
38
39class NVFlinger final {
40public:
41 NVFlinger();
42 ~NVFlinger();
43
44 /// Opens the specified display and returns the id.
45 u64 OpenDisplay(const std::string& name);
46
47 /// Creates a layer on the specified display and returns the layer id.
48 u64 CreateLayer(u64 display_id);
49
50 /// Gets the buffer queue id of the specified layer in the specified display.
51 u32 GetBufferQueueId(u64 display_id, u64 layer_id);
52
53 /// Gets the vsync event for the specified display.
54 Kernel::SharedPtr<Kernel::Event> GetVsyncEvent(u64 display_id);
55
56 /// Obtains a buffer queue identified by the id.
57 std::shared_ptr<BufferQueue> GetBufferQueue(u32 id) const;
58
59 /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when
60 /// finished.
61 void Compose();
62
63private:
64 /// Returns the display identified by the specified id.
65 Display& GetDisplay(u64 display_id);
66
67 /// Returns the layer identified by the specified id in the desired display.
68 Layer& GetLayer(u64 display_id, u64 layer_id);
69
70 std::vector<Display> displays;
71 std::vector<std::shared_ptr<BufferQueue>> buffer_queues;
72
73 /// Id to use for the next layer that is created, this counter is shared among all displays.
74 u64 next_layer_id = 1;
75 /// Id to use for the next buffer queue that is created, this counter is shared among all
76 /// layers.
77 u32 next_buffer_queue_id = 1;
78
79 /// CoreTiming event that handles screen composition.
80 CoreTiming::EventType* composition_event;
81};
82
83} // namespace NVFlinger
84} // namespace Service
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 3f2fd72b2..e0bfad290 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -8,8 +8,7 @@
8#include "common/scope_exit.h" 8#include "common/scope_exit.h"
9#include "core/core_timing.h" 9#include "core/core_timing.h"
10#include "core/hle/ipc_helpers.h" 10#include "core/hle/ipc_helpers.h"
11#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" 11#include "core/hle/service/nvflinger/buffer_queue.h"
12#include "core/hle/service/nvdrv/nvdrv.h"
13#include "core/hle/service/vi/vi.h" 12#include "core/hle/service/vi/vi.h"
14#include "core/hle/service/vi/vi_m.h" 13#include "core/hle/service/vi/vi_m.h"
15#include "video_core/renderer_base.h" 14#include "video_core/renderer_base.h"
@@ -18,9 +17,6 @@
18namespace Service { 17namespace Service {
19namespace VI { 18namespace VI {
20 19
21constexpr size_t SCREEN_REFRESH_RATE = 60;
22constexpr u64 frame_ticks = static_cast<u64>(BASE_CLOCK_RATE / SCREEN_REFRESH_RATE);
23
24class Parcel { 20class Parcel {
25public: 21public:
26 // This default size was chosen arbitrarily. 22 // This default size was chosen arbitrarily.
@@ -204,8 +200,8 @@ public:
204 void DeserializeData() override { 200 void DeserializeData() override {
205 std::u16string token = ReadInterfaceToken(); 201 std::u16string token = ReadInterfaceToken();
206 data = Read<Data>(); 202 data = Read<Data>();
207 ASSERT(data.graphic_buffer_length == sizeof(IGBPBuffer)); 203 ASSERT(data.graphic_buffer_length == sizeof(NVFlinger::IGBPBuffer));
208 buffer = Read<IGBPBuffer>(); 204 buffer = Read<NVFlinger::IGBPBuffer>();
209 } 205 }
210 206
211 struct Data { 207 struct Data {
@@ -216,7 +212,7 @@ public:
216 }; 212 };
217 213
218 Data data; 214 Data data;
219 IGBPBuffer buffer; 215 NVFlinger::IGBPBuffer buffer;
220}; 216};
221 217
222class IGBPSetPreallocatedBufferResponseParcel : public Parcel { 218class IGBPSetPreallocatedBufferResponseParcel : public Parcel {
@@ -288,7 +284,8 @@ public:
288 284
289class IGBPRequestBufferResponseParcel : public Parcel { 285class IGBPRequestBufferResponseParcel : public Parcel {
290public: 286public:
291 explicit IGBPRequestBufferResponseParcel(IGBPBuffer buffer) : Parcel(), buffer(buffer) {} 287 explicit IGBPRequestBufferResponseParcel(NVFlinger::IGBPBuffer buffer)
288 : Parcel(), buffer(buffer) {}
292 ~IGBPRequestBufferResponseParcel() override = default; 289 ~IGBPRequestBufferResponseParcel() override = default;
293 290
294protected: 291protected:
@@ -296,7 +293,7 @@ protected:
296 // TODO(Subv): Find out what this all means 293 // TODO(Subv): Find out what this all means
297 Write<u32_le>(1); 294 Write<u32_le>(1);
298 295
299 Write<u32_le>(sizeof(IGBPBuffer)); 296 Write<u32_le>(sizeof(NVFlinger::IGBPBuffer));
300 Write<u32_le>(0); // Unknown 297 Write<u32_le>(0); // Unknown
301 298
302 Write(buffer); 299 Write(buffer);
@@ -304,7 +301,7 @@ protected:
304 Write<u32_le>(0); 301 Write<u32_le>(0);
305 } 302 }
306 303
307 IGBPBuffer buffer; 304 NVFlinger::IGBPBuffer buffer;
308}; 305};
309 306
310class IGBPQueueBufferRequestParcel : public Parcel { 307class IGBPQueueBufferRequestParcel : public Parcel {
@@ -387,7 +384,7 @@ private:
387 384
388class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> { 385class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> {
389public: 386public:
390 explicit IHOSBinderDriver(std::shared_ptr<NVFlinger> nv_flinger) 387 explicit IHOSBinderDriver(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
391 : ServiceFramework("IHOSBinderDriver"), nv_flinger(std::move(nv_flinger)) { 388 : ServiceFramework("IHOSBinderDriver"), nv_flinger(std::move(nv_flinger)) {
392 static const FunctionInfo functions[] = { 389 static const FunctionInfo functions[] = {
393 {0, &IHOSBinderDriver::TransactParcel, "TransactParcel"}, 390 {0, &IHOSBinderDriver::TransactParcel, "TransactParcel"},
@@ -477,7 +474,8 @@ private:
477 } else if (transaction == TransactionId::Query) { 474 } else if (transaction == TransactionId::Query) {
478 IGBPQueryRequestParcel request{input_data}; 475 IGBPQueryRequestParcel request{input_data};
479 476
480 u32 value = buffer_queue->Query(static_cast<BufferQueue::QueryType>(request.type)); 477 u32 value =
478 buffer_queue->Query(static_cast<NVFlinger::BufferQueue::QueryType>(request.type));
481 479
482 IGBPQueryResponseParcel response{value}; 480 IGBPQueryResponseParcel response{value};
483 auto response_buffer = response.Serialize(); 481 auto response_buffer = response.Serialize();
@@ -518,7 +516,7 @@ private:
518 rb.PushCopyObjects(buffer_queue->GetNativeHandle()); 516 rb.PushCopyObjects(buffer_queue->GetNativeHandle());
519 } 517 }
520 518
521 std::shared_ptr<NVFlinger> nv_flinger; 519 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
522}; 520};
523 521
524class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> { 522class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> {
@@ -546,7 +544,7 @@ private:
546 544
547class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> { 545class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> {
548public: 546public:
549 explicit IManagerDisplayService(std::shared_ptr<NVFlinger> nv_flinger) 547 explicit IManagerDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
550 : ServiceFramework("IManagerDisplayService"), nv_flinger(std::move(nv_flinger)) { 548 : ServiceFramework("IManagerDisplayService"), nv_flinger(std::move(nv_flinger)) {
551 static const FunctionInfo functions[] = { 549 static const FunctionInfo functions[] = {
552 {1020, &IManagerDisplayService::CloseDisplay, "CloseDisplay"}, 550 {1020, &IManagerDisplayService::CloseDisplay, "CloseDisplay"},
@@ -593,7 +591,7 @@ private:
593 rb.Push(RESULT_SUCCESS); 591 rb.Push(RESULT_SUCCESS);
594 } 592 }
595 593
596 std::shared_ptr<NVFlinger> nv_flinger; 594 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
597}; 595};
598 596
599void IApplicationDisplayService::GetRelayService(Kernel::HLERequestContext& ctx) { 597void IApplicationDisplayService::GetRelayService(Kernel::HLERequestContext& ctx) {
@@ -734,7 +732,8 @@ void IApplicationDisplayService::GetDisplayVsyncEvent(Kernel::HLERequestContext&
734 rb.PushCopyObjects(vsync_event); 732 rb.PushCopyObjects(vsync_event);
735} 733}
736 734
737IApplicationDisplayService::IApplicationDisplayService(std::shared_ptr<NVFlinger> nv_flinger) 735IApplicationDisplayService::IApplicationDisplayService(
736 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
738 : ServiceFramework("IApplicationDisplayService"), nv_flinger(std::move(nv_flinger)) { 737 : ServiceFramework("IApplicationDisplayService"), nv_flinger(std::move(nv_flinger)) {
739 static const FunctionInfo functions[] = { 738 static const FunctionInfo functions[] = {
740 {100, &IApplicationDisplayService::GetRelayService, "GetRelayService"}, 739 {100, &IApplicationDisplayService::GetRelayService, "GetRelayService"},
@@ -758,222 +757,5 @@ void InstallInterfaces(SM::ServiceManager& service_manager) {
758 std::make_shared<VI_M>()->InstallAsService(service_manager); 757 std::make_shared<VI_M>()->InstallAsService(service_manager);
759} 758}
760 759
761NVFlinger::NVFlinger() {
762 // Add the different displays to the list of displays.
763 Display default_{0, "Default"};
764 Display external{1, "External"};
765 Display edid{2, "Edid"};
766 Display internal{3, "Internal"};
767
768 displays.emplace_back(default_);
769 displays.emplace_back(external);
770 displays.emplace_back(edid);
771 displays.emplace_back(internal);
772
773 // Schedule the screen composition events
774 composition_event =
775 CoreTiming::RegisterEvent("ScreenCompositioin", [this](u64 userdata, int cycles_late) {
776 Compose();
777 CoreTiming::ScheduleEvent(frame_ticks - cycles_late, composition_event);
778 });
779
780 CoreTiming::ScheduleEvent(frame_ticks, composition_event);
781}
782
783NVFlinger::~NVFlinger() {
784 CoreTiming::UnscheduleEvent(composition_event, 0);
785}
786
787u64 NVFlinger::OpenDisplay(const std::string& name) {
788 LOG_WARNING(Service, "Opening display %s", name.c_str());
789
790 // TODO(Subv): Currently we only support the Default display.
791 ASSERT(name == "Default");
792
793 auto itr = std::find_if(displays.begin(), displays.end(),
794 [&](const Display& display) { return display.name == name; });
795
796 ASSERT(itr != displays.end());
797
798 return itr->id;
799}
800
801u64 NVFlinger::CreateLayer(u64 display_id) {
802 auto& display = GetDisplay(display_id);
803
804 ASSERT_MSG(display.layers.empty(), "Only one layer is supported per display at the moment");
805
806 u64 layer_id = next_layer_id++;
807 u32 buffer_queue_id = next_buffer_queue_id++;
808 auto buffer_queue = std::make_shared<BufferQueue>(buffer_queue_id, layer_id);
809 display.layers.emplace_back(layer_id, buffer_queue);
810 buffer_queues.emplace_back(std::move(buffer_queue));
811 return layer_id;
812}
813
814u32 NVFlinger::GetBufferQueueId(u64 display_id, u64 layer_id) {
815 const auto& layer = GetLayer(display_id, layer_id);
816 return layer.buffer_queue->GetId();
817}
818
819Kernel::SharedPtr<Kernel::Event> NVFlinger::GetVsyncEvent(u64 display_id) {
820 const auto& display = GetDisplay(display_id);
821 return display.vsync_event;
822}
823
824std::shared_ptr<BufferQueue> NVFlinger::GetBufferQueue(u32 id) const {
825 auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
826 [&](const auto& queue) { return queue->GetId() == id; });
827
828 ASSERT(itr != buffer_queues.end());
829 return *itr;
830}
831
832Display& NVFlinger::GetDisplay(u64 display_id) {
833 auto itr = std::find_if(displays.begin(), displays.end(),
834 [&](const Display& display) { return display.id == display_id; });
835
836 ASSERT(itr != displays.end());
837 return *itr;
838}
839
840Layer& NVFlinger::GetLayer(u64 display_id, u64 layer_id) {
841 auto& display = GetDisplay(display_id);
842
843 auto itr = std::find_if(display.layers.begin(), display.layers.end(),
844 [&](const Layer& layer) { return layer.id == layer_id; });
845
846 ASSERT(itr != display.layers.end());
847 return *itr;
848}
849
850void NVFlinger::Compose() {
851 for (auto& display : displays) {
852 // Trigger vsync for this display at the end of drawing
853 SCOPE_EXIT({ display.vsync_event->Signal(); });
854
855 // Don't do anything for displays without layers.
856 if (display.layers.empty())
857 continue;
858
859 // TODO(Subv): Support more than 1 layer.
860 ASSERT_MSG(display.layers.size() == 1, "Max 1 layer per display is supported");
861
862 Layer& layer = display.layers[0];
863 auto& buffer_queue = layer.buffer_queue;
864
865 // Search for a queued buffer and acquire it
866 auto buffer = buffer_queue->AcquireBuffer();
867
868 if (buffer == boost::none) {
869 // There was no queued buffer to draw, render previous frame
870 VideoCore::g_renderer->SwapBuffers({});
871 continue;
872 }
873
874 auto& igbp_buffer = buffer->igbp_buffer;
875
876 // Now send the buffer to the GPU for drawing.
877 auto nvdrv = Nvidia::nvdrv.lock();
878 ASSERT(nvdrv);
879
880 // TODO(Subv): Support more than just disp0. The display device selection is probably based
881 // on which display we're drawing (Default, Internal, External, etc)
882 auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0");
883 ASSERT(nvdisp);
884
885 nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.format,
886 igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride);
887
888 buffer_queue->ReleaseBuffer(buffer->slot);
889 }
890}
891
892BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) {
893 native_handle = Kernel::Event::Create(Kernel::ResetType::OneShot, "BufferQueue NativeHandle");
894}
895
896void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) {
897 Buffer buffer{};
898 buffer.slot = slot;
899 buffer.igbp_buffer = igbp_buffer;
900 buffer.status = Buffer::Status::Free;
901
902 LOG_WARNING(Service, "Adding graphics buffer %u", slot);
903
904 queue.emplace_back(buffer);
905}
906
907u32 BufferQueue::DequeueBuffer(u32 pixel_format, u32 width, u32 height) {
908 auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) {
909 // Only consider free buffers. Buffers become free once again after they've been Acquired
910 // and Released by the compositor, see the NVFlinger::Compose method.
911 if (buffer.status != Buffer::Status::Free)
912 return false;
913
914 // Make sure that the parameters match.
915 auto& igbp_buffer = buffer.igbp_buffer;
916 return igbp_buffer.format == pixel_format && igbp_buffer.width == width &&
917 igbp_buffer.height == height;
918 });
919 ASSERT(itr != queue.end());
920
921 itr->status = Buffer::Status::Dequeued;
922 return itr->slot;
923}
924
925const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const {
926 auto itr = std::find_if(queue.begin(), queue.end(),
927 [&](const Buffer& buffer) { return buffer.slot == slot; });
928 ASSERT(itr != queue.end());
929 ASSERT(itr->status == Buffer::Status::Dequeued);
930 return itr->igbp_buffer;
931}
932
933void BufferQueue::QueueBuffer(u32 slot) {
934 auto itr = std::find_if(queue.begin(), queue.end(),
935 [&](const Buffer& buffer) { return buffer.slot == slot; });
936 ASSERT(itr != queue.end());
937 ASSERT(itr->status == Buffer::Status::Dequeued);
938 itr->status = Buffer::Status::Queued;
939}
940
941boost::optional<const BufferQueue::Buffer&> BufferQueue::AcquireBuffer() {
942 auto itr = std::find_if(queue.begin(), queue.end(), [](const Buffer& buffer) {
943 return buffer.status == Buffer::Status::Queued;
944 });
945 if (itr == queue.end())
946 return boost::none;
947 itr->status = Buffer::Status::Acquired;
948 return *itr;
949}
950
951void BufferQueue::ReleaseBuffer(u32 slot) {
952 auto itr = std::find_if(queue.begin(), queue.end(),
953 [&](const Buffer& buffer) { return buffer.slot == slot; });
954 ASSERT(itr != queue.end());
955 ASSERT(itr->status == Buffer::Status::Acquired);
956 itr->status = Buffer::Status::Free;
957}
958
959u32 BufferQueue::Query(QueryType type) {
960 LOG_WARNING(Service, "(STUBBED) called type=%u", static_cast<u32>(type));
961 switch (type) {
962 case QueryType::NativeWindowFormat:
963 // TODO(Subv): Use an enum for this
964 static constexpr u32 FormatABGR8 = 1;
965 return FormatABGR8;
966 }
967
968 UNIMPLEMENTED();
969 return 0;
970}
971
972Layer::Layer(u64 id, std::shared_ptr<BufferQueue> queue) : id(id), buffer_queue(std::move(queue)) {}
973
974Display::Display(u64 id, std::string name) : id(id), name(std::move(name)) {
975 vsync_event = Kernel::Event::Create(Kernel::ResetType::Pulse, "Display VSync Event");
976}
977
978} // namespace VI 760} // namespace VI
979} // namespace Service 761} // namespace Service
diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h
index 4dd4d1783..5e9b7e6cf 100644
--- a/src/core/hle/service/vi/vi.h
+++ b/src/core/hle/service/vi/vi.h
@@ -7,6 +7,7 @@
7#include <memory> 7#include <memory>
8#include <boost/optional.hpp> 8#include <boost/optional.hpp>
9#include "core/hle/kernel/event.h" 9#include "core/hle/kernel/event.h"
10#include "core/hle/service/nvflinger/nvflinger.h"
10#include "core/hle/service/service.h" 11#include "core/hle/service/service.h"
11 12
12namespace CoreTiming { 13namespace CoreTiming {
@@ -16,134 +17,9 @@ struct EventType;
16namespace Service { 17namespace Service {
17namespace VI { 18namespace VI {
18 19
19struct IGBPBuffer {
20 u32_le magic;
21 u32_le width;
22 u32_le height;
23 u32_le stride;
24 u32_le format;
25 u32_le usage;
26 INSERT_PADDING_WORDS(1);
27 u32_le index;
28 INSERT_PADDING_WORDS(3);
29 u32_le gpu_buffer_id;
30 INSERT_PADDING_WORDS(17);
31 u32_le nvmap_handle;
32 u32_le offset;
33 INSERT_PADDING_WORDS(60);
34};
35
36static_assert(sizeof(IGBPBuffer) == 0x16C, "IGBPBuffer has wrong size");
37
38class BufferQueue {
39public:
40 enum class QueryType {
41 NativeWindowWidth = 0,
42 NativeWindowHeight = 1,
43 NativeWindowFormat = 2,
44 };
45
46 BufferQueue(u32 id, u64 layer_id);
47 ~BufferQueue() = default;
48
49 struct Buffer {
50 enum class Status { Free = 0, Queued = 1, Dequeued = 2, Acquired = 3 };
51
52 u32 slot;
53 Status status = Status::Free;
54 IGBPBuffer igbp_buffer;
55 };
56
57 void SetPreallocatedBuffer(u32 slot, IGBPBuffer& buffer);
58 u32 DequeueBuffer(u32 pixel_format, u32 width, u32 height);
59 const IGBPBuffer& RequestBuffer(u32 slot) const;
60 void QueueBuffer(u32 slot);
61 boost::optional<const Buffer&> AcquireBuffer();
62 void ReleaseBuffer(u32 slot);
63 u32 Query(QueryType type);
64
65 u32 GetId() const {
66 return id;
67 }
68
69 Kernel::SharedPtr<Kernel::Event> GetNativeHandle() const {
70 return native_handle;
71 }
72
73private:
74 u32 id;
75 u64 layer_id;
76
77 std::vector<Buffer> queue;
78 Kernel::SharedPtr<Kernel::Event> native_handle;
79};
80
81struct Layer {
82 Layer(u64 id, std::shared_ptr<BufferQueue> queue);
83 ~Layer() = default;
84
85 u64 id;
86 std::shared_ptr<BufferQueue> buffer_queue;
87};
88
89struct Display {
90 Display(u64 id, std::string name);
91 ~Display() = default;
92
93 u64 id;
94 std::string name;
95
96 std::vector<Layer> layers;
97 Kernel::SharedPtr<Kernel::Event> vsync_event;
98};
99
100class NVFlinger {
101public:
102 NVFlinger();
103 ~NVFlinger();
104
105 /// Opens the specified display and returns the id.
106 u64 OpenDisplay(const std::string& name);
107
108 /// Creates a layer on the specified display and returns the layer id.
109 u64 CreateLayer(u64 display_id);
110
111 /// Gets the buffer queue id of the specified layer in the specified display.
112 u32 GetBufferQueueId(u64 display_id, u64 layer_id);
113
114 /// Gets the vsync event for the specified display.
115 Kernel::SharedPtr<Kernel::Event> GetVsyncEvent(u64 display_id);
116
117 /// Obtains a buffer queue identified by the id.
118 std::shared_ptr<BufferQueue> GetBufferQueue(u32 id) const;
119
120 /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when
121 /// finished.
122 void Compose();
123
124private:
125 /// Returns the display identified by the specified id.
126 Display& GetDisplay(u64 display_id);
127
128 /// Returns the layer identified by the specified id in the desired display.
129 Layer& GetLayer(u64 display_id, u64 layer_id);
130
131 std::vector<Display> displays;
132 std::vector<std::shared_ptr<BufferQueue>> buffer_queues;
133
134 /// Id to use for the next layer that is created, this counter is shared among all displays.
135 u64 next_layer_id = 1;
136 /// Id to use for the next buffer queue that is created, this counter is shared among all
137 /// layers.
138 u32 next_buffer_queue_id = 1;
139
140 /// CoreTiming event that handles screen composition.
141 CoreTiming::EventType* composition_event;
142};
143
144class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> { 20class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> {
145public: 21public:
146 IApplicationDisplayService(std::shared_ptr<NVFlinger> nv_flinger); 22 IApplicationDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
147 ~IApplicationDisplayService() = default; 23 ~IApplicationDisplayService() = default;
148 24
149private: 25private:
@@ -159,7 +35,7 @@ private:
159 void DestroyStrayLayer(Kernel::HLERequestContext& ctx); 35 void DestroyStrayLayer(Kernel::HLERequestContext& ctx);
160 void GetDisplayVsyncEvent(Kernel::HLERequestContext& ctx); 36 void GetDisplayVsyncEvent(Kernel::HLERequestContext& ctx);
161 37
162 std::shared_ptr<NVFlinger> nv_flinger; 38 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
163}; 39};
164 40
165/// Registers all VI services with the specified service manager. 41/// Registers all VI services with the specified service manager.
diff --git a/src/core/hle/service/vi/vi_m.cpp b/src/core/hle/service/vi/vi_m.cpp
index 1a5a28b0d..6deedf842 100644
--- a/src/core/hle/service/vi/vi_m.cpp
+++ b/src/core/hle/service/vi/vi_m.cpp
@@ -23,7 +23,7 @@ VI_M::VI_M() : ServiceFramework("vi:m") {
23 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, 23 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
24 }; 24 };
25 RegisterHandlers(functions); 25 RegisterHandlers(functions);
26 nv_flinger = std::make_shared<NVFlinger>(); 26 nv_flinger = std::make_shared<NVFlinger::NVFlinger>();
27} 27}
28 28
29} // namespace VI 29} // namespace VI
diff --git a/src/core/hle/service/vi/vi_m.h b/src/core/hle/service/vi/vi_m.h
index 70ff7a2f3..ebe79d5c7 100644
--- a/src/core/hle/service/vi/vi_m.h
+++ b/src/core/hle/service/vi/vi_m.h
@@ -8,6 +8,10 @@
8#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
9 9
10namespace Service { 10namespace Service {
11namespace NVFlinger {
12class NVFlinger;
13}
14
11namespace VI { 15namespace VI {
12 16
13class VI_M final : public ServiceFramework<VI_M> { 17class VI_M final : public ServiceFramework<VI_M> {
@@ -18,7 +22,7 @@ public:
18private: 22private:
19 void GetDisplayService(Kernel::HLERequestContext& ctx); 23 void GetDisplayService(Kernel::HLERequestContext& ctx);
20 24
21 std::shared_ptr<NVFlinger> nv_flinger; 25 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
22}; 26};
23 27
24} // namespace VI 28} // namespace VI