summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules3
-rw-r--r--CMakeLists.txt2
m---------externals/Vulkan-Headers0
-rw-r--r--src/audio_core/audio_out.cpp5
-rw-r--r--src/audio_core/audio_out.h8
-rw-r--r--src/audio_core/audio_renderer.cpp7
-rw-r--r--src/audio_core/audio_renderer.h6
-rw-r--r--src/audio_core/buffer.h2
-rw-r--r--src/audio_core/stream.cpp13
-rw-r--r--src/audio_core/stream.h8
-rw-r--r--src/common/logging/backend.cpp20
-rw-r--r--src/common/logging/backend.h1
-rw-r--r--src/common/logging/log.h1
-rw-r--r--src/common/swap.h6
-rw-r--r--src/common/threadsafe_queue.h53
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp12
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h8
-rw-r--r--src/core/arm/unicorn/arm_unicorn.cpp6
-rw-r--r--src/core/arm/unicorn/arm_unicorn.h8
-rw-r--r--src/core/core.cpp21
-rw-r--r--src/core/core.h10
-rw-r--r--src/core/core_cpu.cpp15
-rw-r--r--src/core/core_cpu.h8
-rw-r--r--src/core/core_timing.cpp195
-rw-r--r--src/core/core_timing.h211
-rw-r--r--src/core/cpu_core_manager.cpp3
-rw-r--r--src/core/crypto/key_manager.cpp3
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp6
-rw-r--r--src/core/hle/kernel/address_arbiter.h6
-rw-r--r--src/core/hle/kernel/kernel.cpp12
-rw-r--r--src/core/hle/kernel/kernel.h9
-rw-r--r--src/core/hle/kernel/scheduler.cpp2
-rw-r--r--src/core/hle/kernel/svc.cpp10
-rw-r--r--src/core/hle/kernel/thread.cpp19
-rw-r--r--src/core/hle/service/audio/audout_u.cpp10
-rw-r--r--src/core/hle/service/audio/audren_u.cpp9
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.h7
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp5
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h2
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp5
-rw-r--r--src/core/hle/service/hid/controllers/gesture.h2
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp5
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.h2
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp5
-rw-r--r--src/core/hle/service/hid/controllers/mouse.h2
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp5
-rw-r--r--src/core/hle/service/hid/controllers/npad.h2
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.cpp5
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.h2
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp7
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h2
-rw-r--r--src/core/hle/service/hid/controllers/xpad.cpp5
-rw-r--r--src/core/hle/service/hid/controllers/xpad.h2
-rw-r--r--src/core/hle/service/hid/hid.cpp18
-rw-r--r--src/core/hle/service/hid/irs.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp3
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp110
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h58
-rw-r--r--src/core/hle/service/service.cpp5
-rw-r--r--src/core/hle/service/service.h15
-rw-r--r--src/core/hle/service/time/time.cpp7
-rw-r--r--src/core/hle/service/vi/display/vi_display.cpp71
-rw-r--r--src/core/hle/service/vi/display/vi_display.h98
-rw-r--r--src/core/hle/service/vi/layer/vi_layer.cpp13
-rw-r--r--src/core/hle/service/vi/layer/vi_layer.h52
-rw-r--r--src/core/hle/service/vi/vi.cpp26
-rw-r--r--src/tests/core/core_timing.cpp215
-rw-r--r--src/video_core/CMakeLists.txt16
-rw-r--r--src/video_core/dma_pusher.cpp57
-rw-r--r--src/video_core/dma_pusher.h5
-rw-r--r--src/video_core/engines/kepler_memory.cpp7
-rw-r--r--src/video_core/engines/kepler_memory.h9
-rw-r--r--src/video_core/engines/maxwell_3d.cpp12
-rw-r--r--src/video_core/engines/maxwell_3d.h9
-rw-r--r--src/video_core/engines/maxwell_dma.cpp8
-rw-r--r--src/video_core/engines/maxwell_dma.h10
-rw-r--r--src/video_core/gpu.cpp11
-rw-r--r--src/video_core/gpu.h7
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h20
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp55
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp144
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp5
-rw-r--r--src/video_core/renderer_vulkan/declarations.h45
-rw-r--r--src/video_core/renderer_vulkan/vk_device.cpp231
-rw-r--r--src/video_core/renderer_vulkan/vk_device.h116
-rw-r--r--src/video_core/renderer_vulkan/vk_memory_manager.cpp252
-rw-r--r--src/video_core/renderer_vulkan/vk_memory_manager.h87
-rw-r--r--src/video_core/renderer_vulkan/vk_resource_manager.cpp285
-rw-r--r--src/video_core/renderer_vulkan/vk_resource_manager.h180
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp60
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.h69
-rw-r--r--src/video_core/shader/decode/memory.cpp2
94 files changed, 2457 insertions, 727 deletions
diff --git a/.gitmodules b/.gitmodules
index a33a04167..2558a5ebc 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -37,3 +37,6 @@
37[submodule "discord-rpc"] 37[submodule "discord-rpc"]
38 path = externals/discord-rpc 38 path = externals/discord-rpc
39 url = https://github.com/discordapp/discord-rpc.git 39 url = https://github.com/discordapp/discord-rpc.git
40[submodule "Vulkan-Headers"]
41 path = externals/Vulkan-Headers
42 url = https://github.com/KhronosGroup/Vulkan-Headers.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 97d888762..32cfa8580 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -23,6 +23,8 @@ option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OF
23 23
24option(ENABLE_CUBEB "Enables the cubeb audio backend" ON) 24option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
25 25
26option(ENABLE_VULKAN "Enables Vulkan backend" ON)
27
26option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF) 28option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
27 29
28if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/hooks/pre-commit) 30if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/hooks/pre-commit)
diff --git a/externals/Vulkan-Headers b/externals/Vulkan-Headers
new file mode 160000
Subproject 15e5c4db7500b936ae758236f2e72fc1aec2202
diff --git a/src/audio_core/audio_out.cpp b/src/audio_core/audio_out.cpp
index 50d2a1ed3..8619a3f03 100644
--- a/src/audio_core/audio_out.cpp
+++ b/src/audio_core/audio_out.cpp
@@ -26,14 +26,15 @@ static Stream::Format ChannelsToStreamFormat(u32 num_channels) {
26 return {}; 26 return {};
27} 27}
28 28
29StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&& name, 29StreamPtr AudioOut::OpenStream(Core::Timing::CoreTiming& core_timing, u32 sample_rate,
30 u32 num_channels, std::string&& name,
30 Stream::ReleaseCallback&& release_callback) { 31 Stream::ReleaseCallback&& release_callback) {
31 if (!sink) { 32 if (!sink) {
32 sink = CreateSinkFromID(Settings::values.sink_id, Settings::values.audio_device_id); 33 sink = CreateSinkFromID(Settings::values.sink_id, Settings::values.audio_device_id);
33 } 34 }
34 35
35 return std::make_shared<Stream>( 36 return std::make_shared<Stream>(
36 sample_rate, ChannelsToStreamFormat(num_channels), std::move(release_callback), 37 core_timing, sample_rate, ChannelsToStreamFormat(num_channels), std::move(release_callback),
37 sink->AcquireSinkStream(sample_rate, num_channels, name), std::move(name)); 38 sink->AcquireSinkStream(sample_rate, num_channels, name), std::move(name));
38} 39}
39 40
diff --git a/src/audio_core/audio_out.h b/src/audio_core/audio_out.h
index df9607ac7..b07588287 100644
--- a/src/audio_core/audio_out.h
+++ b/src/audio_core/audio_out.h
@@ -13,6 +13,10 @@
13#include "audio_core/stream.h" 13#include "audio_core/stream.h"
14#include "common/common_types.h" 14#include "common/common_types.h"
15 15
16namespace Core::Timing {
17class CoreTiming;
18}
19
16namespace AudioCore { 20namespace AudioCore {
17 21
18/** 22/**
@@ -21,8 +25,8 @@ namespace AudioCore {
21class AudioOut { 25class AudioOut {
22public: 26public:
23 /// Opens a new audio stream 27 /// Opens a new audio stream
24 StreamPtr OpenStream(u32 sample_rate, u32 num_channels, std::string&& name, 28 StreamPtr OpenStream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, u32 num_channels,
25 Stream::ReleaseCallback&& release_callback); 29 std::string&& name, Stream::ReleaseCallback&& release_callback);
26 30
27 /// Returns a vector of recently released buffers specified by tag for the specified stream 31 /// Returns a vector of recently released buffers specified by tag for the specified stream
28 std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream, std::size_t max_count); 32 std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream, std::size_t max_count);
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp
index 00c026511..9a0939883 100644
--- a/src/audio_core/audio_renderer.cpp
+++ b/src/audio_core/audio_renderer.cpp
@@ -8,6 +8,7 @@
8#include "audio_core/codec.h" 8#include "audio_core/codec.h"
9#include "common/assert.h" 9#include "common/assert.h"
10#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "core/core.h"
11#include "core/hle/kernel/writable_event.h" 12#include "core/hle/kernel/writable_event.h"
12#include "core/memory.h" 13#include "core/memory.h"
13 14
@@ -71,14 +72,14 @@ private:
71 EffectOutStatus out_status{}; 72 EffectOutStatus out_status{};
72 EffectInStatus info{}; 73 EffectInStatus info{};
73}; 74};
74AudioRenderer::AudioRenderer(AudioRendererParameter params, 75AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, AudioRendererParameter params,
75 Kernel::SharedPtr<Kernel::WritableEvent> buffer_event) 76 Kernel::SharedPtr<Kernel::WritableEvent> buffer_event)
76 : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count), 77 : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count),
77 effects(params.effect_count) { 78 effects(params.effect_count) {
78 79
79 audio_out = std::make_unique<AudioCore::AudioOut>(); 80 audio_out = std::make_unique<AudioCore::AudioOut>();
80 stream = audio_out->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer", 81 stream = audio_out->OpenStream(core_timing, STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS,
81 [=]() { buffer_event->Signal(); }); 82 "AudioRenderer", [=]() { buffer_event->Signal(); });
82 audio_out->StartStream(stream); 83 audio_out->StartStream(stream);
83 84
84 QueueMixedBuffer(0); 85 QueueMixedBuffer(0);
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h
index 7826881bf..201ec7a3c 100644
--- a/src/audio_core/audio_renderer.h
+++ b/src/audio_core/audio_renderer.h
@@ -14,6 +14,10 @@
14#include "common/swap.h" 14#include "common/swap.h"
15#include "core/hle/kernel/object.h" 15#include "core/hle/kernel/object.h"
16 16
17namespace Core::Timing {
18class CoreTiming;
19}
20
17namespace Kernel { 21namespace Kernel {
18class WritableEvent; 22class WritableEvent;
19} 23}
@@ -208,7 +212,7 @@ static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size
208 212
209class AudioRenderer { 213class AudioRenderer {
210public: 214public:
211 AudioRenderer(AudioRendererParameter params, 215 AudioRenderer(Core::Timing::CoreTiming& core_timing, AudioRendererParameter params,
212 Kernel::SharedPtr<Kernel::WritableEvent> buffer_event); 216 Kernel::SharedPtr<Kernel::WritableEvent> buffer_event);
213 ~AudioRenderer(); 217 ~AudioRenderer();
214 218
diff --git a/src/audio_core/buffer.h b/src/audio_core/buffer.h
index a323b23ec..5ee09e9aa 100644
--- a/src/audio_core/buffer.h
+++ b/src/audio_core/buffer.h
@@ -21,7 +21,7 @@ public:
21 Buffer(Tag tag, std::vector<s16>&& samples) : tag{tag}, samples{std::move(samples)} {} 21 Buffer(Tag tag, std::vector<s16>&& samples) : tag{tag}, samples{std::move(samples)} {}
22 22
23 /// Returns the raw audio data for the buffer 23 /// Returns the raw audio data for the buffer
24 std::vector<s16>& Samples() { 24 std::vector<s16>& GetSamples() {
25 return samples; 25 return samples;
26 } 26 }
27 27
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
index 8ab5649df..4b66a6786 100644
--- a/src/audio_core/stream.cpp
+++ b/src/audio_core/stream.cpp
@@ -32,12 +32,12 @@ u32 Stream::GetNumChannels() const {
32 return {}; 32 return {};
33} 33}
34 34
35Stream::Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback, 35Stream::Stream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, Format format,
36 SinkStream& sink_stream, std::string&& name_) 36 ReleaseCallback&& release_callback, SinkStream& sink_stream, std::string&& name_)
37 : sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)}, 37 : sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)},
38 sink_stream{sink_stream}, name{std::move(name_)} { 38 sink_stream{sink_stream}, core_timing{core_timing}, name{std::move(name_)} {
39 39
40 release_event = Core::Timing::RegisterEvent( 40 release_event = core_timing.RegisterEvent(
41 name, [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); }); 41 name, [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
42} 42}
43 43
@@ -95,12 +95,11 @@ void Stream::PlayNextBuffer() {
95 active_buffer = queued_buffers.front(); 95 active_buffer = queued_buffers.front();
96 queued_buffers.pop(); 96 queued_buffers.pop();
97 97
98 VolumeAdjustSamples(active_buffer->Samples()); 98 VolumeAdjustSamples(active_buffer->GetSamples());
99 99
100 sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples()); 100 sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
101 101
102 Core::Timing::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, 102 core_timing.ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
103 {});
104} 103}
105 104
106void Stream::ReleaseActiveBuffer() { 105void Stream::ReleaseActiveBuffer() {
diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h
index caa775544..05071243b 100644
--- a/src/audio_core/stream.h
+++ b/src/audio_core/stream.h
@@ -14,8 +14,9 @@
14#include "common/common_types.h" 14#include "common/common_types.h"
15 15
16namespace Core::Timing { 16namespace Core::Timing {
17class CoreTiming;
17struct EventType; 18struct EventType;
18} 19} // namespace Core::Timing
19 20
20namespace AudioCore { 21namespace AudioCore {
21 22
@@ -42,8 +43,8 @@ public:
42 /// Callback function type, used to change guest state on a buffer being released 43 /// Callback function type, used to change guest state on a buffer being released
43 using ReleaseCallback = std::function<void()>; 44 using ReleaseCallback = std::function<void()>;
44 45
45 Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback, 46 Stream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, Format format,
46 SinkStream& sink_stream, std::string&& name_); 47 ReleaseCallback&& release_callback, SinkStream& sink_stream, std::string&& name_);
47 48
48 /// Plays the audio stream 49 /// Plays the audio stream
49 void Play(); 50 void Play();
@@ -100,6 +101,7 @@ private:
100 std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream 101 std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream
101 std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream 102 std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream
102 SinkStream& sink_stream; ///< Output sink for the stream 103 SinkStream& sink_stream; ///< Output sink for the stream
104 Core::Timing::CoreTiming& core_timing; ///< Core timing instance.
103 std::string name; ///< Name of the stream, must be unique 105 std::string name; ///< Name of the stream, must be unique
104}; 106};
105 107
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 12f6d0114..b369f199f 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -40,9 +40,7 @@ public:
40 const Impl& operator=(Impl const&) = delete; 40 const Impl& operator=(Impl const&) = delete;
41 41
42 void PushEntry(Entry e) { 42 void PushEntry(Entry e) {
43 std::lock_guard<std::mutex> lock(message_mutex);
44 message_queue.Push(std::move(e)); 43 message_queue.Push(std::move(e));
45 message_cv.notify_one();
46 } 44 }
47 45
48 void AddBackend(std::unique_ptr<Backend> backend) { 46 void AddBackend(std::unique_ptr<Backend> backend) {
@@ -86,15 +84,13 @@ private:
86 } 84 }
87 }; 85 };
88 while (true) { 86 while (true) {
89 { 87 entry = message_queue.PopWait();
90 std::unique_lock<std::mutex> lock(message_mutex); 88 if (entry.final_entry) {
91 message_cv.wait(lock, [&] { return !running || message_queue.Pop(entry); });
92 }
93 if (!running) {
94 break; 89 break;
95 } 90 }
96 write_logs(entry); 91 write_logs(entry);
97 } 92 }
93
98 // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a case 94 // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a case
99 // where a system is repeatedly spamming logs even on close. 95 // where a system is repeatedly spamming logs even on close.
100 const int MAX_LOGS_TO_WRITE = filter.IsDebug() ? INT_MAX : 100; 96 const int MAX_LOGS_TO_WRITE = filter.IsDebug() ? INT_MAX : 100;
@@ -106,14 +102,13 @@ private:
106 } 102 }
107 103
108 ~Impl() { 104 ~Impl() {
109 running = false; 105 Entry entry;
110 message_cv.notify_one(); 106 entry.final_entry = true;
107 message_queue.Push(entry);
111 backend_thread.join(); 108 backend_thread.join();
112 } 109 }
113 110
114 std::atomic_bool running{true}; 111 std::mutex writing_mutex;
115 std::mutex message_mutex, writing_mutex;
116 std::condition_variable message_cv;
117 std::thread backend_thread; 112 std::thread backend_thread;
118 std::vector<std::unique_ptr<Backend>> backends; 113 std::vector<std::unique_ptr<Backend>> backends;
119 Common::MPSCQueue<Log::Entry> message_queue; 114 Common::MPSCQueue<Log::Entry> message_queue;
@@ -232,6 +227,7 @@ void DebuggerBackend::Write(const Entry& entry) {
232 CLS(Render) \ 227 CLS(Render) \
233 SUB(Render, Software) \ 228 SUB(Render, Software) \
234 SUB(Render, OpenGL) \ 229 SUB(Render, OpenGL) \
230 SUB(Render, Vulkan) \
235 CLS(Audio) \ 231 CLS(Audio) \
236 SUB(Audio, DSP) \ 232 SUB(Audio, DSP) \
237 SUB(Audio, Sink) \ 233 SUB(Audio, Sink) \
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
index 91bb0c309..a31ee6968 100644
--- a/src/common/logging/backend.h
+++ b/src/common/logging/backend.h
@@ -27,6 +27,7 @@ struct Entry {
27 unsigned int line_num; 27 unsigned int line_num;
28 std::string function; 28 std::string function;
29 std::string message; 29 std::string message;
30 bool final_entry = false;
30 31
31 Entry() = default; 32 Entry() = default;
32 Entry(Entry&& o) = default; 33 Entry(Entry&& o) = default;
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index d4ec31ec3..8ed6d5050 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -112,6 +112,7 @@ enum class Class : ClassType {
112 Render, ///< Emulator video output and hardware acceleration 112 Render, ///< Emulator video output and hardware acceleration
113 Render_Software, ///< Software renderer backend 113 Render_Software, ///< Software renderer backend
114 Render_OpenGL, ///< OpenGL backend 114 Render_OpenGL, ///< OpenGL backend
115 Render_Vulkan, ///< Vulkan backend
115 Audio, ///< Audio emulation 116 Audio, ///< Audio emulation
116 Audio_DSP, ///< The HLE implementation of the DSP 117 Audio_DSP, ///< The HLE implementation of the DSP
117 Audio_Sink, ///< Emulator audio output backend 118 Audio_Sink, ///< Emulator audio output backend
diff --git a/src/common/swap.h b/src/common/swap.h
index 32af0b6ac..0e219747f 100644
--- a/src/common/swap.h
+++ b/src/common/swap.h
@@ -28,8 +28,8 @@
28#include <cstring> 28#include <cstring>
29#include "common/common_types.h" 29#include "common/common_types.h"
30 30
31// GCC 4.6+ 31// GCC
32#if __GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) 32#ifdef __GNUC__
33 33
34#if __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) && !defined(COMMON_LITTLE_ENDIAN) 34#if __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) && !defined(COMMON_LITTLE_ENDIAN)
35#define COMMON_LITTLE_ENDIAN 1 35#define COMMON_LITTLE_ENDIAN 1
@@ -38,7 +38,7 @@
38#endif 38#endif
39 39
40// LLVM/clang 40// LLVM/clang
41#elif __clang__ 41#elif defined(__clang__)
42 42
43#if __LITTLE_ENDIAN__ && !defined(COMMON_LITTLE_ENDIAN) 43#if __LITTLE_ENDIAN__ && !defined(COMMON_LITTLE_ENDIAN)
44#define COMMON_LITTLE_ENDIAN 1 44#define COMMON_LITTLE_ENDIAN 1
diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h
index edf13bc49..821e8536a 100644
--- a/src/common/threadsafe_queue.h
+++ b/src/common/threadsafe_queue.h
@@ -7,17 +7,17 @@
7// a simple lockless thread-safe, 7// a simple lockless thread-safe,
8// single reader, single writer queue 8// single reader, single writer queue
9 9
10#include <algorithm>
11#include <atomic> 10#include <atomic>
11#include <condition_variable>
12#include <cstddef> 12#include <cstddef>
13#include <mutex> 13#include <mutex>
14#include "common/common_types.h" 14#include <utility>
15 15
16namespace Common { 16namespace Common {
17template <typename T, bool NeedSize = true> 17template <typename T>
18class SPSCQueue { 18class SPSCQueue {
19public: 19public:
20 SPSCQueue() : size(0) { 20 SPSCQueue() {
21 write_ptr = read_ptr = new ElementPtr(); 21 write_ptr = read_ptr = new ElementPtr();
22 } 22 }
23 ~SPSCQueue() { 23 ~SPSCQueue() {
@@ -25,13 +25,12 @@ public:
25 delete read_ptr; 25 delete read_ptr;
26 } 26 }
27 27
28 u32 Size() const { 28 std::size_t Size() const {
29 static_assert(NeedSize, "using Size() on FifoQueue without NeedSize");
30 return size.load(); 29 return size.load();
31 } 30 }
32 31
33 bool Empty() const { 32 bool Empty() const {
34 return !read_ptr->next.load(); 33 return Size() == 0;
35 } 34 }
36 35
37 T& Front() const { 36 T& Front() const {
@@ -47,13 +46,14 @@ public:
47 ElementPtr* new_ptr = new ElementPtr(); 46 ElementPtr* new_ptr = new ElementPtr();
48 write_ptr->next.store(new_ptr, std::memory_order_release); 47 write_ptr->next.store(new_ptr, std::memory_order_release);
49 write_ptr = new_ptr; 48 write_ptr = new_ptr;
50 if (NeedSize) 49 cv.notify_one();
51 size++; 50
51 ++size;
52 } 52 }
53 53
54 void Pop() { 54 void Pop() {
55 if (NeedSize) 55 --size;
56 size--; 56
57 ElementPtr* tmpptr = read_ptr; 57 ElementPtr* tmpptr = read_ptr;
58 // advance the read pointer 58 // advance the read pointer
59 read_ptr = tmpptr->next.load(); 59 read_ptr = tmpptr->next.load();
@@ -66,8 +66,7 @@ public:
66 if (Empty()) 66 if (Empty())
67 return false; 67 return false;
68 68
69 if (NeedSize) 69 --size;
70 size--;
71 70
72 ElementPtr* tmpptr = read_ptr; 71 ElementPtr* tmpptr = read_ptr;
73 read_ptr = tmpptr->next.load(std::memory_order_acquire); 72 read_ptr = tmpptr->next.load(std::memory_order_acquire);
@@ -77,6 +76,16 @@ public:
77 return true; 76 return true;
78 } 77 }
79 78
79 T PopWait() {
80 if (Empty()) {
81 std::unique_lock<std::mutex> lock(cv_mutex);
82 cv.wait(lock, [this]() { return !Empty(); });
83 }
84 T t;
85 Pop(t);
86 return t;
87 }
88
80 // not thread-safe 89 // not thread-safe
81 void Clear() { 90 void Clear() {
82 size.store(0); 91 size.store(0);
@@ -89,7 +98,7 @@ private:
89 // and a pointer to the next ElementPtr 98 // and a pointer to the next ElementPtr
90 class ElementPtr { 99 class ElementPtr {
91 public: 100 public:
92 ElementPtr() : next(nullptr) {} 101 ElementPtr() {}
93 ~ElementPtr() { 102 ~ElementPtr() {
94 ElementPtr* next_ptr = next.load(); 103 ElementPtr* next_ptr = next.load();
95 104
@@ -98,21 +107,23 @@ private:
98 } 107 }
99 108
100 T current; 109 T current;
101 std::atomic<ElementPtr*> next; 110 std::atomic<ElementPtr*> next{nullptr};
102 }; 111 };
103 112
104 ElementPtr* write_ptr; 113 ElementPtr* write_ptr;
105 ElementPtr* read_ptr; 114 ElementPtr* read_ptr;
106 std::atomic<u32> size; 115 std::atomic_size_t size{0};
116 std::mutex cv_mutex;
117 std::condition_variable cv;
107}; 118};
108 119
109// a simple thread-safe, 120// a simple thread-safe,
110// single reader, multiple writer queue 121// single reader, multiple writer queue
111 122
112template <typename T, bool NeedSize = true> 123template <typename T>
113class MPSCQueue { 124class MPSCQueue {
114public: 125public:
115 u32 Size() const { 126 std::size_t Size() const {
116 return spsc_queue.Size(); 127 return spsc_queue.Size();
117 } 128 }
118 129
@@ -138,13 +149,17 @@ public:
138 return spsc_queue.Pop(t); 149 return spsc_queue.Pop(t);
139 } 150 }
140 151
152 T PopWait() {
153 return spsc_queue.PopWait();
154 }
155
141 // not thread-safe 156 // not thread-safe
142 void Clear() { 157 void Clear() {
143 spsc_queue.Clear(); 158 spsc_queue.Clear();
144 } 159 }
145 160
146private: 161private:
147 SPSCQueue<T, NeedSize> spsc_queue; 162 SPSCQueue<T> spsc_queue;
148 std::mutex write_lock; 163 std::mutex write_lock;
149}; 164};
150} // namespace Common 165} // namespace Common
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index f61bcd40d..988356c65 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -400,6 +400,10 @@ add_library(core STATIC
400 hle/service/time/time.h 400 hle/service/time/time.h
401 hle/service/usb/usb.cpp 401 hle/service/usb/usb.cpp
402 hle/service/usb/usb.h 402 hle/service/usb/usb.h
403 hle/service/vi/display/vi_display.cpp
404 hle/service/vi/display/vi_display.h
405 hle/service/vi/layer/vi_layer.cpp
406 hle/service/vi/layer/vi_layer.h
403 hle/service/vi/vi.cpp 407 hle/service/vi/vi.cpp
404 hle/service/vi/vi.h 408 hle/service/vi/vi.h
405 hle/service/vi/vi_m.cpp 409 hle/service/vi/vi_m.cpp
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index f28951f8a..9b7ca4030 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -112,14 +112,14 @@ public:
112 // Always execute at least one tick. 112 // Always execute at least one tick.
113 amortized_ticks = std::max<u64>(amortized_ticks, 1); 113 amortized_ticks = std::max<u64>(amortized_ticks, 1);
114 114
115 Timing::AddTicks(amortized_ticks); 115 parent.core_timing.AddTicks(amortized_ticks);
116 num_interpreted_instructions = 0; 116 num_interpreted_instructions = 0;
117 } 117 }
118 u64 GetTicksRemaining() override { 118 u64 GetTicksRemaining() override {
119 return std::max(Timing::GetDowncount(), 0); 119 return std::max(parent.core_timing.GetDowncount(), 0);
120 } 120 }
121 u64 GetCNTPCT() override { 121 u64 GetCNTPCT() override {
122 return Timing::GetTicks(); 122 return parent.core_timing.GetTicks();
123 } 123 }
124 124
125 ARM_Dynarmic& parent; 125 ARM_Dynarmic& parent;
@@ -172,8 +172,10 @@ void ARM_Dynarmic::Step() {
172 cb->InterpreterFallback(jit->GetPC(), 1); 172 cb->InterpreterFallback(jit->GetPC(), 1);
173} 173}
174 174
175ARM_Dynarmic::ARM_Dynarmic(ExclusiveMonitor& exclusive_monitor, std::size_t core_index) 175ARM_Dynarmic::ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor,
176 : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), core_index{core_index}, 176 std::size_t core_index)
177 : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{core_timing},
178 core_index{core_index}, core_timing{core_timing},
177 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} { 179 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {
178 ThreadContext ctx{}; 180 ThreadContext ctx{};
179 inner_unicorn.SaveContext(ctx); 181 inner_unicorn.SaveContext(ctx);
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
index 512bf8ce9..6cc458296 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -16,6 +16,10 @@ namespace Memory {
16struct PageTable; 16struct PageTable;
17} 17}
18 18
19namespace Core::Timing {
20class CoreTiming;
21}
22
19namespace Core { 23namespace Core {
20 24
21class ARM_Dynarmic_Callbacks; 25class ARM_Dynarmic_Callbacks;
@@ -23,7 +27,8 @@ class DynarmicExclusiveMonitor;
23 27
24class ARM_Dynarmic final : public ARM_Interface { 28class ARM_Dynarmic final : public ARM_Interface {
25public: 29public:
26 ARM_Dynarmic(ExclusiveMonitor& exclusive_monitor, std::size_t core_index); 30 ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor,
31 std::size_t core_index);
27 ~ARM_Dynarmic(); 32 ~ARM_Dynarmic();
28 33
29 void MapBackingMemory(VAddr address, std::size_t size, u8* memory, 34 void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
@@ -62,6 +67,7 @@ private:
62 ARM_Unicorn inner_unicorn; 67 ARM_Unicorn inner_unicorn;
63 68
64 std::size_t core_index; 69 std::size_t core_index;
70 Timing::CoreTiming& core_timing;
65 DynarmicExclusiveMonitor& exclusive_monitor; 71 DynarmicExclusiveMonitor& exclusive_monitor;
66 72
67 Memory::PageTable* current_page_table = nullptr; 73 Memory::PageTable* current_page_table = nullptr;
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp
index c36c15c02..a542a098b 100644
--- a/src/core/arm/unicorn/arm_unicorn.cpp
+++ b/src/core/arm/unicorn/arm_unicorn.cpp
@@ -72,7 +72,7 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si
72 return {}; 72 return {};
73} 73}
74 74
75ARM_Unicorn::ARM_Unicorn() { 75ARM_Unicorn::ARM_Unicorn(Timing::CoreTiming& core_timing) : core_timing{core_timing} {
76 CHECKED(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc)); 76 CHECKED(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc));
77 77
78 auto fpv = 3 << 20; 78 auto fpv = 3 << 20;
@@ -177,7 +177,7 @@ void ARM_Unicorn::Run() {
177 if (GDBStub::IsServerEnabled()) { 177 if (GDBStub::IsServerEnabled()) {
178 ExecuteInstructions(std::max(4000000, 0)); 178 ExecuteInstructions(std::max(4000000, 0));
179 } else { 179 } else {
180 ExecuteInstructions(std::max(Timing::GetDowncount(), 0)); 180 ExecuteInstructions(std::max(core_timing.GetDowncount(), 0));
181 } 181 }
182} 182}
183 183
@@ -190,7 +190,7 @@ MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64));
190void ARM_Unicorn::ExecuteInstructions(int num_instructions) { 190void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
191 MICROPROFILE_SCOPE(ARM_Jit_Unicorn); 191 MICROPROFILE_SCOPE(ARM_Jit_Unicorn);
192 CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions)); 192 CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions));
193 Timing::AddTicks(num_instructions); 193 core_timing.AddTicks(num_instructions);
194 if (GDBStub::IsServerEnabled()) { 194 if (GDBStub::IsServerEnabled()) {
195 if (last_bkpt_hit) { 195 if (last_bkpt_hit) {
196 uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address); 196 uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address);
diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h
index 75761950b..dbd6955ea 100644
--- a/src/core/arm/unicorn/arm_unicorn.h
+++ b/src/core/arm/unicorn/arm_unicorn.h
@@ -9,12 +9,17 @@
9#include "core/arm/arm_interface.h" 9#include "core/arm/arm_interface.h"
10#include "core/gdbstub/gdbstub.h" 10#include "core/gdbstub/gdbstub.h"
11 11
12namespace Core::Timing {
13class CoreTiming;
14}
15
12namespace Core { 16namespace Core {
13 17
14class ARM_Unicorn final : public ARM_Interface { 18class ARM_Unicorn final : public ARM_Interface {
15public: 19public:
16 ARM_Unicorn(); 20 explicit ARM_Unicorn(Timing::CoreTiming& core_timing);
17 ~ARM_Unicorn(); 21 ~ARM_Unicorn();
22
18 void MapBackingMemory(VAddr address, std::size_t size, u8* memory, 23 void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
19 Kernel::VMAPermission perms) override; 24 Kernel::VMAPermission perms) override;
20 void UnmapMemory(VAddr address, std::size_t size) override; 25 void UnmapMemory(VAddr address, std::size_t size) override;
@@ -43,6 +48,7 @@ public:
43 48
44private: 49private:
45 uc_engine* uc{}; 50 uc_engine* uc{};
51 Timing::CoreTiming& core_timing;
46 GDBStub::BreakpointAddress last_bkpt{}; 52 GDBStub::BreakpointAddress last_bkpt{};
47 bool last_bkpt_hit; 53 bool last_bkpt_hit;
48}; 54};
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 4d9d21ee4..ab7181a05 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -94,8 +94,8 @@ struct System::Impl {
94 ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) { 94 ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
95 LOG_DEBUG(HW_Memory, "initialized OK"); 95 LOG_DEBUG(HW_Memory, "initialized OK");
96 96
97 Timing::Init(); 97 core_timing.Initialize();
98 kernel.Initialize(); 98 kernel.Initialize(core_timing);
99 99
100 const auto current_time = std::chrono::duration_cast<std::chrono::seconds>( 100 const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
101 std::chrono::system_clock::now().time_since_epoch()); 101 std::chrono::system_clock::now().time_since_epoch());
@@ -120,7 +120,7 @@ struct System::Impl {
120 telemetry_session = std::make_unique<Core::TelemetrySession>(); 120 telemetry_session = std::make_unique<Core::TelemetrySession>();
121 service_manager = std::make_shared<Service::SM::ServiceManager>(); 121 service_manager = std::make_shared<Service::SM::ServiceManager>();
122 122
123 Service::Init(service_manager, *virtual_filesystem); 123 Service::Init(service_manager, system, *virtual_filesystem);
124 GDBStub::Init(); 124 GDBStub::Init();
125 125
126 renderer = VideoCore::CreateRenderer(emu_window, system); 126 renderer = VideoCore::CreateRenderer(emu_window, system);
@@ -128,7 +128,7 @@ struct System::Impl {
128 return ResultStatus::ErrorVideoCore; 128 return ResultStatus::ErrorVideoCore;
129 } 129 }
130 130
131 gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer()); 131 gpu_core = std::make_unique<Tegra::GPU>(system, renderer->Rasterizer());
132 132
133 cpu_core_manager.Initialize(system); 133 cpu_core_manager.Initialize(system);
134 is_powered_on = true; 134 is_powered_on = true;
@@ -205,7 +205,7 @@ struct System::Impl {
205 205
206 // Shutdown kernel and core timing 206 // Shutdown kernel and core timing
207 kernel.Shutdown(); 207 kernel.Shutdown();
208 Timing::Shutdown(); 208 core_timing.Shutdown();
209 209
210 // Close app loader 210 // Close app loader
211 app_loader.reset(); 211 app_loader.reset();
@@ -232,9 +232,10 @@ struct System::Impl {
232 } 232 }
233 233
234 PerfStatsResults GetAndResetPerfStats() { 234 PerfStatsResults GetAndResetPerfStats() {
235 return perf_stats.GetAndResetStats(Timing::GetGlobalTimeUs()); 235 return perf_stats.GetAndResetStats(core_timing.GetGlobalTimeUs());
236 } 236 }
237 237
238 Timing::CoreTiming core_timing;
238 Kernel::KernelCore kernel; 239 Kernel::KernelCore kernel;
239 /// RealVfsFilesystem instance 240 /// RealVfsFilesystem instance
240 FileSys::VirtualFilesystem virtual_filesystem; 241 FileSys::VirtualFilesystem virtual_filesystem;
@@ -396,6 +397,14 @@ const Kernel::KernelCore& System::Kernel() const {
396 return impl->kernel; 397 return impl->kernel;
397} 398}
398 399
400Timing::CoreTiming& System::CoreTiming() {
401 return impl->core_timing;
402}
403
404const Timing::CoreTiming& System::CoreTiming() const {
405 return impl->core_timing;
406}
407
399Core::PerfStats& System::GetPerfStats() { 408Core::PerfStats& System::GetPerfStats() {
400 return impl->perf_stats; 409 return impl->perf_stats;
401} 410}
diff --git a/src/core/core.h b/src/core/core.h
index 511a5ad3a..d720013f7 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -47,6 +47,10 @@ namespace VideoCore {
47class RendererBase; 47class RendererBase;
48} // namespace VideoCore 48} // namespace VideoCore
49 49
50namespace Core::Timing {
51class CoreTiming;
52}
53
50namespace Core { 54namespace Core {
51 55
52class ARM_Interface; 56class ARM_Interface;
@@ -205,6 +209,12 @@ public:
205 /// Provides a constant pointer to the current process. 209 /// Provides a constant pointer to the current process.
206 const Kernel::Process* CurrentProcess() const; 210 const Kernel::Process* CurrentProcess() const;
207 211
212 /// Provides a reference to the core timing instance.
213 Timing::CoreTiming& CoreTiming();
214
215 /// Provides a constant reference to the core timing instance.
216 const Timing::CoreTiming& CoreTiming() const;
217
208 /// Provides a reference to the kernel instance. 218 /// Provides a reference to the kernel instance.
209 Kernel::KernelCore& Kernel(); 219 Kernel::KernelCore& Kernel();
210 220
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp
index 452366250..54aa21a3a 100644
--- a/src/core/core_cpu.cpp
+++ b/src/core/core_cpu.cpp
@@ -49,17 +49,18 @@ bool CpuBarrier::Rendezvous() {
49 return false; 49 return false;
50} 50}
51 51
52Cpu::Cpu(ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, std::size_t core_index) 52Cpu::Cpu(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor,
53 : cpu_barrier{cpu_barrier}, core_index{core_index} { 53 CpuBarrier& cpu_barrier, std::size_t core_index)
54 : cpu_barrier{cpu_barrier}, core_timing{core_timing}, core_index{core_index} {
54 if (Settings::values.use_cpu_jit) { 55 if (Settings::values.use_cpu_jit) {
55#ifdef ARCHITECTURE_x86_64 56#ifdef ARCHITECTURE_x86_64
56 arm_interface = std::make_unique<ARM_Dynarmic>(exclusive_monitor, core_index); 57 arm_interface = std::make_unique<ARM_Dynarmic>(core_timing, exclusive_monitor, core_index);
57#else 58#else
58 arm_interface = std::make_unique<ARM_Unicorn>(); 59 arm_interface = std::make_unique<ARM_Unicorn>();
59 LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); 60 LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
60#endif 61#endif
61 } else { 62 } else {
62 arm_interface = std::make_unique<ARM_Unicorn>(); 63 arm_interface = std::make_unique<ARM_Unicorn>(core_timing);
63 } 64 }
64 65
65 scheduler = std::make_unique<Kernel::Scheduler>(*arm_interface); 66 scheduler = std::make_unique<Kernel::Scheduler>(*arm_interface);
@@ -93,14 +94,14 @@ void Cpu::RunLoop(bool tight_loop) {
93 94
94 if (IsMainCore()) { 95 if (IsMainCore()) {
95 // TODO(Subv): Only let CoreTiming idle if all 4 cores are idling. 96 // TODO(Subv): Only let CoreTiming idle if all 4 cores are idling.
96 Timing::Idle(); 97 core_timing.Idle();
97 Timing::Advance(); 98 core_timing.Advance();
98 } 99 }
99 100
100 PrepareReschedule(); 101 PrepareReschedule();
101 } else { 102 } else {
102 if (IsMainCore()) { 103 if (IsMainCore()) {
103 Timing::Advance(); 104 core_timing.Advance();
104 } 105 }
105 106
106 if (tight_loop) { 107 if (tight_loop) {
diff --git a/src/core/core_cpu.h b/src/core/core_cpu.h
index 1d2bdc6cd..e2204c6b0 100644
--- a/src/core/core_cpu.h
+++ b/src/core/core_cpu.h
@@ -15,6 +15,10 @@ namespace Kernel {
15class Scheduler; 15class Scheduler;
16} 16}
17 17
18namespace Core::Timing {
19class CoreTiming;
20}
21
18namespace Core { 22namespace Core {
19 23
20class ARM_Interface; 24class ARM_Interface;
@@ -41,7 +45,8 @@ private:
41 45
42class Cpu { 46class Cpu {
43public: 47public:
44 Cpu(ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, std::size_t core_index); 48 Cpu(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor,
49 CpuBarrier& cpu_barrier, std::size_t core_index);
45 ~Cpu(); 50 ~Cpu();
46 51
47 void RunLoop(bool tight_loop = true); 52 void RunLoop(bool tight_loop = true);
@@ -82,6 +87,7 @@ private:
82 std::unique_ptr<ARM_Interface> arm_interface; 87 std::unique_ptr<ARM_Interface> arm_interface;
83 CpuBarrier& cpu_barrier; 88 CpuBarrier& cpu_barrier;
84 std::unique_ptr<Kernel::Scheduler> scheduler; 89 std::unique_ptr<Kernel::Scheduler> scheduler;
90 Timing::CoreTiming& core_timing;
85 91
86 std::atomic<bool> reschedule_pending = false; 92 std::atomic<bool> reschedule_pending = false;
87 std::size_t core_index; 93 std::size_t core_index;
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 2b7ca9766..a0dd5db24 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -8,149 +8,98 @@
8#include <mutex> 8#include <mutex>
9#include <string> 9#include <string>
10#include <tuple> 10#include <tuple>
11#include <unordered_map> 11
12#include <vector>
13#include "common/assert.h" 12#include "common/assert.h"
14#include "common/thread.h" 13#include "common/thread.h"
15#include "common/threadsafe_queue.h"
16#include "core/core_timing_util.h" 14#include "core/core_timing_util.h"
17 15
18namespace Core::Timing { 16namespace Core::Timing {
19 17
20static s64 global_timer; 18constexpr int MAX_SLICE_LENGTH = 20000;
21static int slice_length;
22static int downcount;
23
24struct EventType {
25 TimedCallback callback;
26 const std::string* name;
27};
28 19
29struct Event { 20struct CoreTiming::Event {
30 s64 time; 21 s64 time;
31 u64 fifo_order; 22 u64 fifo_order;
32 u64 userdata; 23 u64 userdata;
33 const EventType* type; 24 const EventType* type;
34};
35
36// Sort by time, unless the times are the same, in which case sort by the order added to the queue
37static bool operator>(const Event& left, const Event& right) {
38 return std::tie(left.time, left.fifo_order) > std::tie(right.time, right.fifo_order);
39}
40
41static bool operator<(const Event& left, const Event& right) {
42 return std::tie(left.time, left.fifo_order) < std::tie(right.time, right.fifo_order);
43}
44
45// unordered_map stores each element separately as a linked list node so pointers to elements
46// remain stable regardless of rehashes/resizing.
47static std::unordered_map<std::string, EventType> event_types;
48 25
49// The queue is a min-heap using std::make_heap/push_heap/pop_heap. 26 // Sort by time, unless the times are the same, in which case sort by
50// We don't use std::priority_queue because we need to be able to serialize, unserialize and 27 // the order added to the queue
51// erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't accomodated 28 friend bool operator>(const Event& left, const Event& right) {
52// by the standard adaptor class. 29 return std::tie(left.time, left.fifo_order) > std::tie(right.time, right.fifo_order);
53static std::vector<Event> event_queue; 30 }
54static u64 event_fifo_id;
55// the queue for storing the events from other threads threadsafe until they will be added
56// to the event_queue by the emu thread
57static Common::MPSCQueue<Event, false> ts_queue;
58
59// the queue for unscheduling the events from other threads threadsafe
60static Common::MPSCQueue<std::pair<const EventType*, u64>, false> unschedule_queue;
61
62constexpr int MAX_SLICE_LENGTH = 20000;
63
64static s64 idled_cycles;
65
66// Are we in a function that has been called from Advance()
67// If events are sheduled from a function that gets called from Advance(),
68// don't change slice_length and downcount.
69static bool is_global_timer_sane;
70
71static EventType* ev_lost = nullptr;
72
73static void EmptyTimedCallback(u64 userdata, s64 cyclesLate) {}
74
75EventType* RegisterEvent(const std::string& name, TimedCallback callback) {
76 // check for existing type with same name.
77 // we want event type names to remain unique so that we can use them for serialization.
78 ASSERT_MSG(event_types.find(name) == event_types.end(),
79 "CoreTiming Event \"{}\" is already registered. Events should only be registered "
80 "during Init to avoid breaking save states.",
81 name.c_str());
82 31
83 auto info = event_types.emplace(name, EventType{callback, nullptr}); 32 friend bool operator<(const Event& left, const Event& right) {
84 EventType* event_type = &info.first->second; 33 return std::tie(left.time, left.fifo_order) < std::tie(right.time, right.fifo_order);
85 event_type->name = &info.first->first; 34 }
86 return event_type; 35};
87}
88 36
89void UnregisterAllEvents() { 37CoreTiming::CoreTiming() = default;
90 ASSERT_MSG(event_queue.empty(), "Cannot unregister events with events pending"); 38CoreTiming::~CoreTiming() = default;
91 event_types.clear();
92}
93 39
94void Init() { 40void CoreTiming::Initialize() {
95 downcount = MAX_SLICE_LENGTH; 41 downcount = MAX_SLICE_LENGTH;
96 slice_length = MAX_SLICE_LENGTH; 42 slice_length = MAX_SLICE_LENGTH;
97 global_timer = 0; 43 global_timer = 0;
98 idled_cycles = 0; 44 idled_cycles = 0;
99 45
100 // The time between CoreTiming being intialized and the first call to Advance() is considered 46 // The time between CoreTiming being initialized and the first call to Advance() is considered
101 // the slice boundary between slice -1 and slice 0. Dispatcher loops must call Advance() before 47 // the slice boundary between slice -1 and slice 0. Dispatcher loops must call Advance() before
102 // executing the first cycle of each slice to prepare the slice length and downcount for 48 // executing the first cycle of each slice to prepare the slice length and downcount for
103 // that slice. 49 // that slice.
104 is_global_timer_sane = true; 50 is_global_timer_sane = true;
105 51
106 event_fifo_id = 0; 52 event_fifo_id = 0;
107 ev_lost = RegisterEvent("_lost_event", &EmptyTimedCallback); 53
54 const auto empty_timed_callback = [](u64, s64) {};
55 ev_lost = RegisterEvent("_lost_event", empty_timed_callback);
108} 56}
109 57
110void Shutdown() { 58void CoreTiming::Shutdown() {
111 MoveEvents(); 59 MoveEvents();
112 ClearPendingEvents(); 60 ClearPendingEvents();
113 UnregisterAllEvents(); 61 UnregisterAllEvents();
114} 62}
115 63
116// This should only be called from the CPU thread. If you are calling 64EventType* CoreTiming::RegisterEvent(const std::string& name, TimedCallback callback) {
117// it from any other thread, you are doing something evil 65 // check for existing type with same name.
118u64 GetTicks() { 66 // we want event type names to remain unique so that we can use them for serialization.
119 u64 ticks = static_cast<u64>(global_timer); 67 ASSERT_MSG(event_types.find(name) == event_types.end(),
120 if (!is_global_timer_sane) { 68 "CoreTiming Event \"{}\" is already registered. Events should only be registered "
121 ticks += slice_length - downcount; 69 "during Init to avoid breaking save states.",
122 } 70 name.c_str());
123 return ticks;
124}
125
126void AddTicks(u64 ticks) {
127 downcount -= static_cast<int>(ticks);
128}
129 71
130u64 GetIdleTicks() { 72 auto info = event_types.emplace(name, EventType{callback, nullptr});
131 return static_cast<u64>(idled_cycles); 73 EventType* event_type = &info.first->second;
74 event_type->name = &info.first->first;
75 return event_type;
132} 76}
133 77
134void ClearPendingEvents() { 78void CoreTiming::UnregisterAllEvents() {
135 event_queue.clear(); 79 ASSERT_MSG(event_queue.empty(), "Cannot unregister events with events pending");
80 event_types.clear();
136} 81}
137 82
138void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) { 83void CoreTiming::ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) {
139 ASSERT(event_type != nullptr); 84 ASSERT(event_type != nullptr);
140 s64 timeout = GetTicks() + cycles_into_future; 85 const s64 timeout = GetTicks() + cycles_into_future;
86
141 // If this event needs to be scheduled before the next advance(), force one early 87 // If this event needs to be scheduled before the next advance(), force one early
142 if (!is_global_timer_sane) 88 if (!is_global_timer_sane) {
143 ForceExceptionCheck(cycles_into_future); 89 ForceExceptionCheck(cycles_into_future);
90 }
91
144 event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); 92 event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type});
145 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); 93 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
146} 94}
147 95
148void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata) { 96void CoreTiming::ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type,
97 u64 userdata) {
149 ts_queue.Push(Event{global_timer + cycles_into_future, 0, userdata, event_type}); 98 ts_queue.Push(Event{global_timer + cycles_into_future, 0, userdata, event_type});
150} 99}
151 100
152void UnscheduleEvent(const EventType* event_type, u64 userdata) { 101void CoreTiming::UnscheduleEvent(const EventType* event_type, u64 userdata) {
153 auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { 102 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
154 return e.type == event_type && e.userdata == userdata; 103 return e.type == event_type && e.userdata == userdata;
155 }); 104 });
156 105
@@ -161,13 +110,33 @@ void UnscheduleEvent(const EventType* event_type, u64 userdata) {
161 } 110 }
162} 111}
163 112
164void UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata) { 113void CoreTiming::UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata) {
165 unschedule_queue.Push(std::make_pair(event_type, userdata)); 114 unschedule_queue.Push(std::make_pair(event_type, userdata));
166} 115}
167 116
168void RemoveEvent(const EventType* event_type) { 117u64 CoreTiming::GetTicks() const {
169 auto itr = std::remove_if(event_queue.begin(), event_queue.end(), 118 u64 ticks = static_cast<u64>(global_timer);
170 [&](const Event& e) { return e.type == event_type; }); 119 if (!is_global_timer_sane) {
120 ticks += slice_length - downcount;
121 }
122 return ticks;
123}
124
125u64 CoreTiming::GetIdleTicks() const {
126 return static_cast<u64>(idled_cycles);
127}
128
129void CoreTiming::AddTicks(u64 ticks) {
130 downcount -= static_cast<int>(ticks);
131}
132
133void CoreTiming::ClearPendingEvents() {
134 event_queue.clear();
135}
136
137void CoreTiming::RemoveEvent(const EventType* event_type) {
138 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(),
139 [&](const Event& e) { return e.type == event_type; });
171 140
172 // Removing random items breaks the invariant so we have to re-establish it. 141 // Removing random items breaks the invariant so we have to re-establish it.
173 if (itr != event_queue.end()) { 142 if (itr != event_queue.end()) {
@@ -176,22 +145,24 @@ void RemoveEvent(const EventType* event_type) {
176 } 145 }
177} 146}
178 147
179void RemoveNormalAndThreadsafeEvent(const EventType* event_type) { 148void CoreTiming::RemoveNormalAndThreadsafeEvent(const EventType* event_type) {
180 MoveEvents(); 149 MoveEvents();
181 RemoveEvent(event_type); 150 RemoveEvent(event_type);
182} 151}
183 152
184void ForceExceptionCheck(s64 cycles) { 153void CoreTiming::ForceExceptionCheck(s64 cycles) {
185 cycles = std::max<s64>(0, cycles); 154 cycles = std::max<s64>(0, cycles);
186 if (downcount > cycles) { 155 if (downcount <= cycles) {
187 // downcount is always (much) smaller than MAX_INT so we can safely cast cycles to an int 156 return;
188 // here. Account for cycles already executed by adjusting the g.slice_length
189 slice_length -= downcount - static_cast<int>(cycles);
190 downcount = static_cast<int>(cycles);
191 } 157 }
158
159 // downcount is always (much) smaller than MAX_INT so we can safely cast cycles to an int
160 // here. Account for cycles already executed by adjusting the g.slice_length
161 slice_length -= downcount - static_cast<int>(cycles);
162 downcount = static_cast<int>(cycles);
192} 163}
193 164
194void MoveEvents() { 165void CoreTiming::MoveEvents() {
195 for (Event ev; ts_queue.Pop(ev);) { 166 for (Event ev; ts_queue.Pop(ev);) {
196 ev.fifo_order = event_fifo_id++; 167 ev.fifo_order = event_fifo_id++;
197 event_queue.emplace_back(std::move(ev)); 168 event_queue.emplace_back(std::move(ev));
@@ -199,13 +170,13 @@ void MoveEvents() {
199 } 170 }
200} 171}
201 172
202void Advance() { 173void CoreTiming::Advance() {
203 MoveEvents(); 174 MoveEvents();
204 for (std::pair<const EventType*, u64> ev; unschedule_queue.Pop(ev);) { 175 for (std::pair<const EventType*, u64> ev; unschedule_queue.Pop(ev);) {
205 UnscheduleEvent(ev.first, ev.second); 176 UnscheduleEvent(ev.first, ev.second);
206 } 177 }
207 178
208 int cycles_executed = slice_length - downcount; 179 const int cycles_executed = slice_length - downcount;
209 global_timer += cycles_executed; 180 global_timer += cycles_executed;
210 slice_length = MAX_SLICE_LENGTH; 181 slice_length = MAX_SLICE_LENGTH;
211 182
@@ -229,16 +200,16 @@ void Advance() {
229 downcount = slice_length; 200 downcount = slice_length;
230} 201}
231 202
232void Idle() { 203void CoreTiming::Idle() {
233 idled_cycles += downcount; 204 idled_cycles += downcount;
234 downcount = 0; 205 downcount = 0;
235} 206}
236 207
237std::chrono::microseconds GetGlobalTimeUs() { 208std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const {
238 return std::chrono::microseconds{GetTicks() * 1000000 / BASE_CLOCK_RATE}; 209 return std::chrono::microseconds{GetTicks() * 1000000 / BASE_CLOCK_RATE};
239} 210}
240 211
241int GetDowncount() { 212int CoreTiming::GetDowncount() const {
242 return downcount; 213 return downcount;
243} 214}
244 215
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 093989d4c..59163bae1 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -4,92 +4,153 @@
4 4
5#pragma once 5#pragma once
6 6
7/**
8 * This is a system to schedule events into the emulated machine's future. Time is measured
9 * in main CPU clock cycles.
10 *
11 * To schedule an event, you first have to register its type. This is where you pass in the
12 * callback. You then schedule events using the type id you get back.
13 *
14 * The int cyclesLate that the callbacks get is how many cycles late it was.
15 * So to schedule a new event on a regular basis:
16 * inside callback:
17 * ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever")
18 */
19
20#include <chrono> 7#include <chrono>
21#include <functional> 8#include <functional>
22#include <string> 9#include <string>
10#include <unordered_map>
11#include <vector>
23#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/threadsafe_queue.h"
24 14
25namespace Core::Timing { 15namespace Core::Timing {
26 16
27struct EventType; 17/// A callback that may be scheduled for a particular core timing event.
28
29using TimedCallback = std::function<void(u64 userdata, int cycles_late)>; 18using TimedCallback = std::function<void(u64 userdata, int cycles_late)>;
30 19
31/** 20/// Contains the characteristics of a particular event.
32 * CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is 21struct EventType {
33 * required to end slice -1 and start slice 0 before the first cycle of code is executed. 22 /// The event's callback function.
34 */ 23 TimedCallback callback;
35void Init(); 24 /// A pointer to the name of the event.
36void Shutdown(); 25 const std::string* name;
37 26};
38/**
39 * This should only be called from the emu thread, if you are calling it any other thread, you are
40 * doing something evil
41 */
42u64 GetTicks();
43u64 GetIdleTicks();
44void AddTicks(u64 ticks);
45
46/**
47 * Returns the event_type identifier. if name is not unique, it will assert.
48 */
49EventType* RegisterEvent(const std::string& name, TimedCallback callback);
50void UnregisterAllEvents();
51
52/**
53 * After the first Advance, the slice lengths and the downcount will be reduced whenever an event
54 * is scheduled earlier than the current values.
55 * Scheduling from a callback will not update the downcount until the Advance() completes.
56 */
57void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata = 0);
58 27
59/** 28/**
60 * This is to be called when outside of hle threads, such as the graphics thread, wants to 29 * This is a system to schedule events into the emulated machine's future. Time is measured
61 * schedule things to be executed on the main thread. 30 * in main CPU clock cycles.
62 * Not that this doesn't change slice_length and thus events scheduled by this might be called 31 *
63 * with a delay of up to MAX_SLICE_LENGTH 32 * To schedule an event, you first have to register its type. This is where you pass in the
64 */ 33 * callback. You then schedule events using the type id you get back.
65void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata); 34 *
66 35 * The int cyclesLate that the callbacks get is how many cycles late it was.
67void UnscheduleEvent(const EventType* event_type, u64 userdata); 36 * So to schedule a new event on a regular basis:
68void UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata); 37 * inside callback:
69 38 * ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever")
70/// We only permit one event of each type in the queue at a time.
71void RemoveEvent(const EventType* event_type);
72void RemoveNormalAndThreadsafeEvent(const EventType* event_type);
73
74/** Advance must be called at the beginning of dispatcher loops, not the end. Advance() ends
75 * the previous timing slice and begins the next one, you must Advance from the previous
76 * slice to the current one before executing any cycles. CoreTiming starts in slice -1 so an
77 * Advance() is required to initialize the slice length before the first cycle of emulated
78 * instructions is executed.
79 */ 39 */
80void Advance(); 40class CoreTiming {
81void MoveEvents(); 41public:
82 42 CoreTiming();
83/// Pretend that the main CPU has executed enough cycles to reach the next event. 43 ~CoreTiming();
84void Idle(); 44
85 45 CoreTiming(const CoreTiming&) = delete;
86/// Clear all pending events. This should ONLY be done on exit. 46 CoreTiming(CoreTiming&&) = delete;
87void ClearPendingEvents(); 47
88 48 CoreTiming& operator=(const CoreTiming&) = delete;
89void ForceExceptionCheck(s64 cycles); 49 CoreTiming& operator=(CoreTiming&&) = delete;
90 50
91std::chrono::microseconds GetGlobalTimeUs(); 51 /// CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is
92 52 /// required to end slice - 1 and start slice 0 before the first cycle of code is executed.
93int GetDowncount(); 53 void Initialize();
54
55 /// Tears down all timing related functionality.
56 void Shutdown();
57
58 /// Registers a core timing event with the given name and callback.
59 ///
60 /// @param name The name of the core timing event to register.
61 /// @param callback The callback to execute for the event.
62 ///
63 /// @returns An EventType instance representing the registered event.
64 ///
65 /// @pre The name of the event being registered must be unique among all
66 /// registered events.
67 ///
68 EventType* RegisterEvent(const std::string& name, TimedCallback callback);
69
70 /// Unregisters all registered events thus far.
71 void UnregisterAllEvents();
72
73 /// After the first Advance, the slice lengths and the downcount will be reduced whenever an
74 /// event is scheduled earlier than the current values.
75 ///
76 /// Scheduling from a callback will not update the downcount until the Advance() completes.
77 void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata = 0);
78
79 /// This is to be called when outside of hle threads, such as the graphics thread, wants to
80 /// schedule things to be executed on the main thread.
81 ///
82 /// @note This doesn't change slice_length and thus events scheduled by this might be
83 /// called with a delay of up to MAX_SLICE_LENGTH
84 void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type,
85 u64 userdata = 0);
86
87 void UnscheduleEvent(const EventType* event_type, u64 userdata);
88 void UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata);
89
90 /// We only permit one event of each type in the queue at a time.
91 void RemoveEvent(const EventType* event_type);
92 void RemoveNormalAndThreadsafeEvent(const EventType* event_type);
93
94 void ForceExceptionCheck(s64 cycles);
95
96 /// This should only be called from the emu thread, if you are calling it any other thread,
97 /// you are doing something evil
98 u64 GetTicks() const;
99
100 u64 GetIdleTicks() const;
101
102 void AddTicks(u64 ticks);
103
104 /// Advance must be called at the beginning of dispatcher loops, not the end. Advance() ends
105 /// the previous timing slice and begins the next one, you must Advance from the previous
106 /// slice to the current one before executing any cycles. CoreTiming starts in slice -1 so an
107 /// Advance() is required to initialize the slice length before the first cycle of emulated
108 /// instructions is executed.
109 void Advance();
110
111 /// Pretend that the main CPU has executed enough cycles to reach the next event.
112 void Idle();
113
114 std::chrono::microseconds GetGlobalTimeUs() const;
115
116 int GetDowncount() const;
117
118private:
119 struct Event;
120
121 /// Clear all pending events. This should ONLY be done on exit.
122 void ClearPendingEvents();
123 void MoveEvents();
124
125 s64 global_timer = 0;
126 s64 idled_cycles = 0;
127 int slice_length = 0;
128 int downcount = 0;
129
130 // Are we in a function that has been called from Advance()
131 // If events are scheduled from a function that gets called from Advance(),
132 // don't change slice_length and downcount.
133 bool is_global_timer_sane = false;
134
135 // The queue is a min-heap using std::make_heap/push_heap/pop_heap.
136 // We don't use std::priority_queue because we need to be able to serialize, unserialize and
137 // erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't
138 // accomodated by the standard adaptor class.
139 std::vector<Event> event_queue;
140 u64 event_fifo_id = 0;
141
142 // Stores each element separately as a linked list node so pointers to elements
143 // remain stable regardless of rehashes/resizing.
144 std::unordered_map<std::string, EventType> event_types;
145
146 // The queue for storing the events from other threads threadsafe until they will be added
147 // to the event_queue by the emu thread
148 Common::MPSCQueue<Event> ts_queue;
149
150 // The queue for unscheduling the events from other threads threadsafe
151 Common::MPSCQueue<std::pair<const EventType*, u64>> unschedule_queue;
152
153 EventType* ev_lost = nullptr;
154};
94 155
95} // namespace Core::Timing 156} // namespace Core::Timing
diff --git a/src/core/cpu_core_manager.cpp b/src/core/cpu_core_manager.cpp
index 769a6fefa..2ddb3610d 100644
--- a/src/core/cpu_core_manager.cpp
+++ b/src/core/cpu_core_manager.cpp
@@ -27,7 +27,8 @@ void CpuCoreManager::Initialize(System& system) {
27 exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size()); 27 exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size());
28 28
29 for (std::size_t index = 0; index < cores.size(); ++index) { 29 for (std::size_t index = 0; index < cores.size(); ++index) {
30 cores[index] = std::make_unique<Cpu>(*exclusive_monitor, *barrier, index); 30 cores[index] =
31 std::make_unique<Cpu>(system.CoreTiming(), *exclusive_monitor, *barrier, index);
31 } 32 }
32 33
33 // Create threads for CPU cores 1-3, and build thread_to_cpu map 34 // Create threads for CPU cores 1-3, and build thread_to_cpu map
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index ca12fb4ab..dfac9a4b3 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -398,7 +398,8 @@ static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_
398} 398}
399 399
400void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) { 400void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
401 std::ifstream file(filename); 401 std::ifstream file;
402 OpenFStream(file, filename, std::ios_base::in);
402 if (!file.is_open()) 403 if (!file.is_open())
403 return; 404 return;
404 405
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index 57157beb4..a250d088d 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -17,8 +17,7 @@
17#include "core/hle/result.h" 17#include "core/hle/result.h"
18#include "core/memory.h" 18#include "core/memory.h"
19 19
20namespace Kernel { 20namespace Kernel::AddressArbiter {
21namespace AddressArbiter {
22 21
23// Performs actual address waiting logic. 22// Performs actual address waiting logic.
24static ResultCode WaitForAddress(VAddr address, s64 timeout) { 23static ResultCode WaitForAddress(VAddr address, s64 timeout) {
@@ -176,5 +175,4 @@ ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) {
176 175
177 return WaitForAddress(address, timeout); 176 return WaitForAddress(address, timeout);
178} 177}
179} // namespace AddressArbiter 178} // namespace Kernel::AddressArbiter
180} // namespace Kernel
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h
index e3657b8e9..b58f21bec 100644
--- a/src/core/hle/kernel/address_arbiter.h
+++ b/src/core/hle/kernel/address_arbiter.h
@@ -8,9 +8,8 @@
8 8
9union ResultCode; 9union ResultCode;
10 10
11namespace Kernel { 11namespace Kernel::AddressArbiter {
12 12
13namespace AddressArbiter {
14enum class ArbitrationType { 13enum class ArbitrationType {
15 WaitIfLessThan = 0, 14 WaitIfLessThan = 0,
16 DecrementAndWaitIfLessThan = 1, 15 DecrementAndWaitIfLessThan = 1,
@@ -29,6 +28,5 @@ ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 valu
29 28
30ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement); 29ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement);
31ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout); 30ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout);
32} // namespace AddressArbiter
33 31
34} // namespace Kernel 32} // namespace Kernel::AddressArbiter
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 3721ae8fe..dd749eed4 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -86,11 +86,11 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_
86} 86}
87 87
88struct KernelCore::Impl { 88struct KernelCore::Impl {
89 void Initialize(KernelCore& kernel) { 89 void Initialize(KernelCore& kernel, Core::Timing::CoreTiming& core_timing) {
90 Shutdown(); 90 Shutdown();
91 91
92 InitializeSystemResourceLimit(kernel); 92 InitializeSystemResourceLimit(kernel);
93 InitializeThreads(); 93 InitializeThreads(core_timing);
94 } 94 }
95 95
96 void Shutdown() { 96 void Shutdown() {
@@ -122,9 +122,9 @@ struct KernelCore::Impl {
122 ASSERT(system_resource_limit->SetLimitValue(ResourceType::Sessions, 900).IsSuccess()); 122 ASSERT(system_resource_limit->SetLimitValue(ResourceType::Sessions, 900).IsSuccess());
123 } 123 }
124 124
125 void InitializeThreads() { 125 void InitializeThreads(Core::Timing::CoreTiming& core_timing) {
126 thread_wakeup_event_type = 126 thread_wakeup_event_type =
127 Core::Timing::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback); 127 core_timing.RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
128 } 128 }
129 129
130 std::atomic<u32> next_object_id{0}; 130 std::atomic<u32> next_object_id{0};
@@ -152,8 +152,8 @@ KernelCore::~KernelCore() {
152 Shutdown(); 152 Shutdown();
153} 153}
154 154
155void KernelCore::Initialize() { 155void KernelCore::Initialize(Core::Timing::CoreTiming& core_timing) {
156 impl->Initialize(*this); 156 impl->Initialize(*this, core_timing);
157} 157}
158 158
159void KernelCore::Shutdown() { 159void KernelCore::Shutdown() {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 7406f107e..154bced42 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -12,8 +12,9 @@ template <typename T>
12class ResultVal; 12class ResultVal;
13 13
14namespace Core::Timing { 14namespace Core::Timing {
15class CoreTiming;
15struct EventType; 16struct EventType;
16} 17} // namespace Core::Timing
17 18
18namespace Kernel { 19namespace Kernel {
19 20
@@ -39,7 +40,11 @@ public:
39 KernelCore& operator=(KernelCore&&) = delete; 40 KernelCore& operator=(KernelCore&&) = delete;
40 41
41 /// Resets the kernel to a clean slate for use. 42 /// Resets the kernel to a clean slate for use.
42 void Initialize(); 43 ///
44 /// @param core_timing CoreTiming instance used to create any necessary
45 /// kernel-specific callback events.
46 ///
47 void Initialize(Core::Timing::CoreTiming& core_timing);
43 48
44 /// Clears all resources in use by the kernel instance. 49 /// Clears all resources in use by the kernel instance.
45 void Shutdown(); 50 void Shutdown();
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index 9e2517e1b..44f30d070 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -111,7 +111,7 @@ void Scheduler::SwitchContext(Thread* new_thread) {
111 111
112void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) { 112void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
113 const u64 prev_switch_ticks = last_context_switch_time; 113 const u64 prev_switch_ticks = last_context_switch_time;
114 const u64 most_recent_switch_ticks = Core::Timing::GetTicks(); 114 const u64 most_recent_switch_ticks = Core::System::GetInstance().CoreTiming().GetTicks();
115 const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks; 115 const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
116 116
117 if (thread != nullptr) { 117 if (thread != nullptr) {
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 5f040f79f..c5d399bab 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -918,6 +918,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
918 } 918 }
919 919
920 const auto& system = Core::System::GetInstance(); 920 const auto& system = Core::System::GetInstance();
921 const auto& core_timing = system.CoreTiming();
921 const auto& scheduler = system.CurrentScheduler(); 922 const auto& scheduler = system.CurrentScheduler();
922 const auto* const current_thread = scheduler.GetCurrentThread(); 923 const auto* const current_thread = scheduler.GetCurrentThread();
923 const bool same_thread = current_thread == thread; 924 const bool same_thread = current_thread == thread;
@@ -927,9 +928,9 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
927 if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) { 928 if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
928 const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks(); 929 const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks();
929 930
930 out_ticks = thread_ticks + (Core::Timing::GetTicks() - prev_ctx_ticks); 931 out_ticks = thread_ticks + (core_timing.GetTicks() - prev_ctx_ticks);
931 } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) { 932 } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) {
932 out_ticks = Core::Timing::GetTicks() - prev_ctx_ticks; 933 out_ticks = core_timing.GetTicks() - prev_ctx_ticks;
933 } 934 }
934 935
935 *result = out_ticks; 936 *result = out_ticks;
@@ -1546,10 +1547,11 @@ static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to
1546static u64 GetSystemTick() { 1547static u64 GetSystemTick() {
1547 LOG_TRACE(Kernel_SVC, "called"); 1548 LOG_TRACE(Kernel_SVC, "called");
1548 1549
1549 const u64 result{Core::Timing::GetTicks()}; 1550 auto& core_timing = Core::System::GetInstance().CoreTiming();
1551 const u64 result{core_timing.GetTicks()};
1550 1552
1551 // Advance time to defeat dumb games that busy-wait for the frame to end. 1553 // Advance time to defeat dumb games that busy-wait for the frame to end.
1552 Core::Timing::AddTicks(400); 1554 core_timing.AddTicks(400);
1553 1555
1554 return result; 1556 return result;
1555} 1557}
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 7881c2b90..6661e2130 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -43,7 +43,8 @@ Thread::~Thread() = default;
43 43
44void Thread::Stop() { 44void Thread::Stop() {
45 // Cancel any outstanding wakeup events for this thread 45 // Cancel any outstanding wakeup events for this thread
46 Core::Timing::UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), callback_handle); 46 Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(),
47 callback_handle);
47 kernel.ThreadWakeupCallbackHandleTable().Close(callback_handle); 48 kernel.ThreadWakeupCallbackHandleTable().Close(callback_handle);
48 callback_handle = 0; 49 callback_handle = 0;
49 50
@@ -85,13 +86,14 @@ void Thread::WakeAfterDelay(s64 nanoseconds) {
85 86
86 // This function might be called from any thread so we have to be cautious and use the 87 // This function might be called from any thread so we have to be cautious and use the
87 // thread-safe version of ScheduleEvent. 88 // thread-safe version of ScheduleEvent.
88 Core::Timing::ScheduleEventThreadsafe(Core::Timing::nsToCycles(nanoseconds), 89 Core::System::GetInstance().CoreTiming().ScheduleEventThreadsafe(
89 kernel.ThreadWakeupCallbackEventType(), callback_handle); 90 Core::Timing::nsToCycles(nanoseconds), kernel.ThreadWakeupCallbackEventType(),
91 callback_handle);
90} 92}
91 93
92void Thread::CancelWakeupTimer() { 94void Thread::CancelWakeupTimer() {
93 Core::Timing::UnscheduleEventThreadsafe(kernel.ThreadWakeupCallbackEventType(), 95 Core::System::GetInstance().CoreTiming().UnscheduleEventThreadsafe(
94 callback_handle); 96 kernel.ThreadWakeupCallbackEventType(), callback_handle);
95} 97}
96 98
97static std::optional<s32> GetNextProcessorId(u64 mask) { 99static std::optional<s32> GetNextProcessorId(u64 mask) {
@@ -190,6 +192,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
190 return ResultCode(-1); 192 return ResultCode(-1);
191 } 193 }
192 194
195 auto& system = Core::System::GetInstance();
193 SharedPtr<Thread> thread(new Thread(kernel)); 196 SharedPtr<Thread> thread(new Thread(kernel));
194 197
195 thread->thread_id = kernel.CreateNewThreadID(); 198 thread->thread_id = kernel.CreateNewThreadID();
@@ -198,7 +201,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
198 thread->stack_top = stack_top; 201 thread->stack_top = stack_top;
199 thread->tpidr_el0 = 0; 202 thread->tpidr_el0 = 0;
200 thread->nominal_priority = thread->current_priority = priority; 203 thread->nominal_priority = thread->current_priority = priority;
201 thread->last_running_ticks = Core::Timing::GetTicks(); 204 thread->last_running_ticks = system.CoreTiming().GetTicks();
202 thread->processor_id = processor_id; 205 thread->processor_id = processor_id;
203 thread->ideal_core = processor_id; 206 thread->ideal_core = processor_id;
204 thread->affinity_mask = 1ULL << processor_id; 207 thread->affinity_mask = 1ULL << processor_id;
@@ -209,7 +212,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
209 thread->name = std::move(name); 212 thread->name = std::move(name);
210 thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap(); 213 thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap();
211 thread->owner_process = &owner_process; 214 thread->owner_process = &owner_process;
212 thread->scheduler = &Core::System::GetInstance().Scheduler(processor_id); 215 thread->scheduler = &system.Scheduler(processor_id);
213 thread->scheduler->AddThread(thread, priority); 216 thread->scheduler->AddThread(thread, priority);
214 thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread); 217 thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread);
215 218
@@ -258,7 +261,7 @@ void Thread::SetStatus(ThreadStatus new_status) {
258 } 261 }
259 262
260 if (status == ThreadStatus::Running) { 263 if (status == ThreadStatus::Running) {
261 last_running_ticks = Core::Timing::GetTicks(); 264 last_running_ticks = Core::System::GetInstance().CoreTiming().GetTicks();
262 } 265 }
263 266
264 status = new_status; 267 status = new_status;
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index dc6a6b188..6831c0735 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -68,12 +68,12 @@ public:
68 RegisterHandlers(functions); 68 RegisterHandlers(functions);
69 69
70 // This is the event handle used to check if the audio buffer was released 70 // This is the event handle used to check if the audio buffer was released
71 auto& kernel = Core::System::GetInstance().Kernel(); 71 auto& system = Core::System::GetInstance();
72 buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, 72 buffer_event = Kernel::WritableEvent::CreateEventPair(
73 "IAudioOutBufferReleased"); 73 system.Kernel(), Kernel::ResetType::Sticky, "IAudioOutBufferReleased");
74 74
75 stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count, 75 stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate,
76 std::move(unique_name), 76 audio_params.channel_count, std::move(unique_name),
77 [=]() { buffer_event.writable->Signal(); }); 77 [=]() { buffer_event.writable->Signal(); });
78 } 78 }
79 79
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 76cc48254..7e0cc64a8 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -42,10 +42,11 @@ public:
42 // clang-format on 42 // clang-format on
43 RegisterHandlers(functions); 43 RegisterHandlers(functions);
44 44
45 auto& kernel = Core::System::GetInstance().Kernel(); 45 auto& system = Core::System::GetInstance();
46 system_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, 46 system_event = Kernel::WritableEvent::CreateEventPair(
47 "IAudioRenderer:SystemEvent"); 47 system.Kernel(), Kernel::ResetType::Sticky, "IAudioRenderer:SystemEvent");
48 renderer = std::make_unique<AudioCore::AudioRenderer>(audren_params, system_event.writable); 48 renderer = std::make_unique<AudioCore::AudioRenderer>(system.CoreTiming(), audren_params,
49 system_event.writable);
49 } 50 }
50 51
51private: 52private:
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h
index f0e092b1b..5e5097a03 100644
--- a/src/core/hle/service/hid/controllers/controller_base.h
+++ b/src/core/hle/service/hid/controllers/controller_base.h
@@ -7,6 +7,10 @@
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "common/swap.h" 8#include "common/swap.h"
9 9
10namespace Core::Timing {
11class CoreTiming;
12}
13
10namespace Service::HID { 14namespace Service::HID {
11class ControllerBase { 15class ControllerBase {
12public: 16public:
@@ -20,7 +24,8 @@ public:
20 virtual void OnRelease() = 0; 24 virtual void OnRelease() = 0;
21 25
22 // When the controller is requesting an update for the shared memory 26 // When the controller is requesting an update for the shared memory
23 virtual void OnUpdate(u8* data, std::size_t size) = 0; 27 virtual void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
28 std::size_t size) = 0;
24 29
25 // Called when input devices should be loaded 30 // Called when input devices should be loaded
26 virtual void OnLoadInputDevices() = 0; 31 virtual void OnLoadInputDevices() = 0;
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
index b264c9503..c5c2e032a 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.cpp
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -21,8 +21,9 @@ void Controller_DebugPad::OnInit() {}
21 21
22void Controller_DebugPad::OnRelease() {} 22void Controller_DebugPad::OnRelease() {}
23 23
24void Controller_DebugPad::OnUpdate(u8* data, std::size_t size) { 24void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
25 shared_memory.header.timestamp = Core::Timing::GetTicks(); 25 std::size_t size) {
26 shared_memory.header.timestamp = core_timing.GetTicks();
26 shared_memory.header.total_entry_count = 17; 27 shared_memory.header.total_entry_count = 17;
27 28
28 if (!IsControllerActivated()) { 29 if (!IsControllerActivated()) {
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
index 68b734248..929035034 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.h
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -26,7 +26,7 @@ public:
26 void OnRelease() override; 26 void OnRelease() override;
27 27
28 // When the controller is requesting an update for the shared memory 28 // When the controller is requesting an update for the shared memory
29 void OnUpdate(u8* data, std::size_t size) override; 29 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
30 30
31 // Called when input devices should be loaded 31 // Called when input devices should be loaded
32 void OnLoadInputDevices() override; 32 void OnLoadInputDevices() override;
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
index 6d21f1a7d..a179252e3 100644
--- a/src/core/hle/service/hid/controllers/gesture.cpp
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -17,8 +17,9 @@ void Controller_Gesture::OnInit() {}
17 17
18void Controller_Gesture::OnRelease() {} 18void Controller_Gesture::OnRelease() {}
19 19
20void Controller_Gesture::OnUpdate(u8* data, std::size_t size) { 20void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
21 shared_memory.header.timestamp = Core::Timing::GetTicks(); 21 std::size_t size) {
22 shared_memory.header.timestamp = core_timing.GetTicks();
22 shared_memory.header.total_entry_count = 17; 23 shared_memory.header.total_entry_count = 17;
23 24
24 if (!IsControllerActivated()) { 25 if (!IsControllerActivated()) {
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h
index 1056ffbcd..f305fe90f 100644
--- a/src/core/hle/service/hid/controllers/gesture.h
+++ b/src/core/hle/service/hid/controllers/gesture.h
@@ -22,7 +22,7 @@ public:
22 void OnRelease() override; 22 void OnRelease() override;
23 23
24 // When the controller is requesting an update for the shared memory 24 // When the controller is requesting an update for the shared memory
25 void OnUpdate(u8* data, size_t size) override; 25 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override;
26 26
27 // Called when input devices should be loaded 27 // Called when input devices should be loaded
28 void OnLoadInputDevices() override; 28 void OnLoadInputDevices() override;
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
index 798f30436..92d7bfb52 100644
--- a/src/core/hle/service/hid/controllers/keyboard.cpp
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -19,8 +19,9 @@ void Controller_Keyboard::OnInit() {}
19 19
20void Controller_Keyboard::OnRelease() {} 20void Controller_Keyboard::OnRelease() {}
21 21
22void Controller_Keyboard::OnUpdate(u8* data, std::size_t size) { 22void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
23 shared_memory.header.timestamp = Core::Timing::GetTicks(); 23 std::size_t size) {
24 shared_memory.header.timestamp = core_timing.GetTicks();
24 shared_memory.header.total_entry_count = 17; 25 shared_memory.header.total_entry_count = 17;
25 26
26 if (!IsControllerActivated()) { 27 if (!IsControllerActivated()) {
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h
index f52775456..73cd2c7bb 100644
--- a/src/core/hle/service/hid/controllers/keyboard.h
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -25,7 +25,7 @@ public:
25 void OnRelease() override; 25 void OnRelease() override;
26 26
27 // When the controller is requesting an update for the shared memory 27 // When the controller is requesting an update for the shared memory
28 void OnUpdate(u8* data, std::size_t size) override; 28 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
29 29
30 // Called when input devices should be loaded 30 // Called when input devices should be loaded
31 void OnLoadInputDevices() override; 31 void OnLoadInputDevices() override;
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
index 4985037be..11ab096d9 100644
--- a/src/core/hle/service/hid/controllers/mouse.cpp
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -17,8 +17,9 @@ Controller_Mouse::~Controller_Mouse() = default;
17void Controller_Mouse::OnInit() {} 17void Controller_Mouse::OnInit() {}
18void Controller_Mouse::OnRelease() {} 18void Controller_Mouse::OnRelease() {}
19 19
20void Controller_Mouse::OnUpdate(u8* data, std::size_t size) { 20void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
21 shared_memory.header.timestamp = Core::Timing::GetTicks(); 21 std::size_t size) {
22 shared_memory.header.timestamp = core_timing.GetTicks();
22 shared_memory.header.total_entry_count = 17; 23 shared_memory.header.total_entry_count = 17;
23 24
24 if (!IsControllerActivated()) { 25 if (!IsControllerActivated()) {
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h
index 70b654d07..9d46eecbe 100644
--- a/src/core/hle/service/hid/controllers/mouse.h
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -24,7 +24,7 @@ public:
24 void OnRelease() override; 24 void OnRelease() override;
25 25
26 // When the controller is requesting an update for the shared memory 26 // When the controller is requesting an update for the shared memory
27 void OnUpdate(u8* data, std::size_t size) override; 27 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
28 28
29 // Called when input devices should be loaded 29 // Called when input devices should be loaded
30 void OnLoadInputDevices() override; 30 void OnLoadInputDevices() override;
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index ffdd1c593..e7fc7a619 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -288,7 +288,8 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
288 rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX); 288 rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
289} 289}
290 290
291void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) { 291void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
292 std::size_t data_len) {
292 if (!IsControllerActivated()) 293 if (!IsControllerActivated())
293 return; 294 return;
294 for (std::size_t i = 0; i < shared_memory_entries.size(); i++) { 295 for (std::size_t i = 0; i < shared_memory_entries.size(); i++) {
@@ -308,7 +309,7 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
308 const auto& last_entry = 309 const auto& last_entry =
309 main_controller->npad[main_controller->common.last_entry_index]; 310 main_controller->npad[main_controller->common.last_entry_index];
310 311
311 main_controller->common.timestamp = Core::Timing::GetTicks(); 312 main_controller->common.timestamp = core_timing.GetTicks();
312 main_controller->common.last_entry_index = 313 main_controller->common.last_entry_index =
313 (main_controller->common.last_entry_index + 1) % 17; 314 (main_controller->common.last_entry_index + 1) % 17;
314 315
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 106cf58c8..18c7a94e6 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -30,7 +30,7 @@ public:
30 void OnRelease() override; 30 void OnRelease() override;
31 31
32 // When the controller is requesting an update for the shared memory 32 // When the controller is requesting an update for the shared memory
33 void OnUpdate(u8* data, std::size_t size) override; 33 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
34 34
35 // Called when input devices should be loaded 35 // Called when input devices should be loaded
36 void OnLoadInputDevices() override; 36 void OnLoadInputDevices() override;
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp
index cca4dca1d..946948f5e 100644
--- a/src/core/hle/service/hid/controllers/stubbed.cpp
+++ b/src/core/hle/service/hid/controllers/stubbed.cpp
@@ -16,13 +16,14 @@ void Controller_Stubbed::OnInit() {}
16 16
17void Controller_Stubbed::OnRelease() {} 17void Controller_Stubbed::OnRelease() {}
18 18
19void Controller_Stubbed::OnUpdate(u8* data, std::size_t size) { 19void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
20 std::size_t size) {
20 if (!smart_update) { 21 if (!smart_update) {
21 return; 22 return;
22 } 23 }
23 24
24 CommonHeader header{}; 25 CommonHeader header{};
25 header.timestamp = Core::Timing::GetTicks(); 26 header.timestamp = core_timing.GetTicks();
26 header.total_entry_count = 17; 27 header.total_entry_count = 17;
27 header.entry_count = 0; 28 header.entry_count = 0;
28 header.last_entry_index = 0; 29 header.last_entry_index = 0;
diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/stubbed.h
index 4a21c643e..24469f03e 100644
--- a/src/core/hle/service/hid/controllers/stubbed.h
+++ b/src/core/hle/service/hid/controllers/stubbed.h
@@ -20,7 +20,7 @@ public:
20 void OnRelease() override; 20 void OnRelease() override;
21 21
22 // When the controller is requesting an update for the shared memory 22 // When the controller is requesting an update for the shared memory
23 void OnUpdate(u8* data, std::size_t size) override; 23 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
24 24
25 // Called when input devices should be loaded 25 // Called when input devices should be loaded
26 void OnLoadInputDevices() override; 26 void OnLoadInputDevices() override;
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index a7c8acc72..1a8445a43 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -20,8 +20,9 @@ void Controller_Touchscreen::OnInit() {}
20 20
21void Controller_Touchscreen::OnRelease() {} 21void Controller_Touchscreen::OnRelease() {}
22 22
23void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) { 23void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
24 shared_memory.header.timestamp = Core::Timing::GetTicks(); 24 std::size_t size) {
25 shared_memory.header.timestamp = core_timing.GetTicks();
25 shared_memory.header.total_entry_count = 17; 26 shared_memory.header.total_entry_count = 17;
26 27
27 if (!IsControllerActivated()) { 28 if (!IsControllerActivated()) {
@@ -48,7 +49,7 @@ void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) {
48 touch_entry.diameter_x = Settings::values.touchscreen.diameter_x; 49 touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
49 touch_entry.diameter_y = Settings::values.touchscreen.diameter_y; 50 touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
50 touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle; 51 touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
51 const u64 tick = Core::Timing::GetTicks(); 52 const u64 tick = core_timing.GetTicks();
52 touch_entry.delta_time = tick - last_touch; 53 touch_entry.delta_time = tick - last_touch;
53 last_touch = tick; 54 last_touch = tick;
54 touch_entry.finger = Settings::values.touchscreen.finger; 55 touch_entry.finger = Settings::values.touchscreen.finger;
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index 94cd0eba9..012b6e0dd 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -24,7 +24,7 @@ public:
24 void OnRelease() override; 24 void OnRelease() override;
25 25
26 // When the controller is requesting an update for the shared memory 26 // When the controller is requesting an update for the shared memory
27 void OnUpdate(u8* data, std::size_t size) override; 27 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
28 28
29 // Called when input devices should be loaded 29 // Called when input devices should be loaded
30 void OnLoadInputDevices() override; 30 void OnLoadInputDevices() override;
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp
index eff03d14e..1a9da9576 100644
--- a/src/core/hle/service/hid/controllers/xpad.cpp
+++ b/src/core/hle/service/hid/controllers/xpad.cpp
@@ -17,9 +17,10 @@ void Controller_XPad::OnInit() {}
17 17
18void Controller_XPad::OnRelease() {} 18void Controller_XPad::OnRelease() {}
19 19
20void Controller_XPad::OnUpdate(u8* data, std::size_t size) { 20void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
21 std::size_t size) {
21 for (auto& xpad_entry : shared_memory.shared_memory_entries) { 22 for (auto& xpad_entry : shared_memory.shared_memory_entries) {
22 xpad_entry.header.timestamp = Core::Timing::GetTicks(); 23 xpad_entry.header.timestamp = core_timing.GetTicks();
23 xpad_entry.header.total_entry_count = 17; 24 xpad_entry.header.total_entry_count = 17;
24 25
25 if (!IsControllerActivated()) { 26 if (!IsControllerActivated()) {
diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h
index ff836989f..2864e6617 100644
--- a/src/core/hle/service/hid/controllers/xpad.h
+++ b/src/core/hle/service/hid/controllers/xpad.h
@@ -22,7 +22,7 @@ public:
22 void OnRelease() override; 22 void OnRelease() override;
23 23
24 // When the controller is requesting an update for the shared memory 24 // When the controller is requesting an update for the shared memory
25 void OnUpdate(u8* data, std::size_t size) override; 25 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
26 26
27 // Called when input devices should be loaded 27 // Called when input devices should be loaded
28 void OnLoadInputDevices() override; 28 void OnLoadInputDevices() override;
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 79c320d04..8a6de83a2 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -73,13 +73,15 @@ IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") {
73 GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000); 73 GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000);
74 74
75 // Register update callbacks 75 // Register update callbacks
76 pad_update_event = Core::Timing::RegisterEvent( 76 auto& core_timing = Core::System::GetInstance().CoreTiming();
77 "HID::UpdatePadCallback", 77 pad_update_event =
78 [this](u64 userdata, int cycles_late) { UpdateControllers(userdata, cycles_late); }); 78 core_timing.RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, int cycles_late) {
79 UpdateControllers(userdata, cycles_late);
80 });
79 81
80 // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) 82 // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
81 83
82 Core::Timing::ScheduleEvent(pad_update_ticks, pad_update_event); 84 core_timing.ScheduleEvent(pad_update_ticks, pad_update_event);
83 85
84 ReloadInputDevices(); 86 ReloadInputDevices();
85} 87}
@@ -93,7 +95,7 @@ void IAppletResource::DeactivateController(HidController controller) {
93} 95}
94 96
95IAppletResource ::~IAppletResource() { 97IAppletResource ::~IAppletResource() {
96 Core::Timing::UnscheduleEvent(pad_update_event, 0); 98 Core::System::GetInstance().CoreTiming().UnscheduleEvent(pad_update_event, 0);
97} 99}
98 100
99void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) { 101void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
@@ -105,15 +107,17 @@ void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
105} 107}
106 108
107void IAppletResource::UpdateControllers(u64 userdata, int cycles_late) { 109void IAppletResource::UpdateControllers(u64 userdata, int cycles_late) {
110 auto& core_timing = Core::System::GetInstance().CoreTiming();
111
108 const bool should_reload = Settings::values.is_device_reload_pending.exchange(false); 112 const bool should_reload = Settings::values.is_device_reload_pending.exchange(false);
109 for (const auto& controller : controllers) { 113 for (const auto& controller : controllers) {
110 if (should_reload) { 114 if (should_reload) {
111 controller->OnLoadInputDevices(); 115 controller->OnLoadInputDevices();
112 } 116 }
113 controller->OnUpdate(shared_mem->GetPointer(), SHARED_MEMORY_SIZE); 117 controller->OnUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
114 } 118 }
115 119
116 Core::Timing::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event); 120 core_timing.ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event);
117} 121}
118 122
119class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { 123class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp
index b427d4068..2c4625c99 100644
--- a/src/core/hle/service/hid/irs.cpp
+++ b/src/core/hle/service/hid/irs.cpp
@@ -98,7 +98,7 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
98 98
99 IPC::ResponseBuilder rb{ctx, 5}; 99 IPC::ResponseBuilder rb{ctx, 5};
100 rb.Push(RESULT_SUCCESS); 100 rb.Push(RESULT_SUCCESS);
101 rb.PushRaw<u64>(Core::Timing::GetTicks()); 101 rb.PushRaw<u64>(Core::System::GetInstance().CoreTiming().GetTicks());
102 rb.PushRaw<u32>(0); 102 rb.PushRaw<u32>(0);
103} 103}
104 104
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index 88d80ba06..45812d238 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -5,6 +5,7 @@
5#include <cstring> 5#include <cstring>
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/core.h"
8#include "core/core_timing.h" 9#include "core/core_timing.h"
9#include "core/core_timing_util.h" 10#include "core/core_timing_util.h"
10#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" 11#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h"
@@ -184,7 +185,7 @@ u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& o
184 185
185 IoctlGetGpuTime params{}; 186 IoctlGetGpuTime params{};
186 std::memcpy(&params, input.data(), input.size()); 187 std::memcpy(&params, input.data(), input.size());
187 params.gpu_time = Core::Timing::cyclesToNs(Core::Timing::GetTicks()); 188 params.gpu_time = Core::Timing::cyclesToNs(Core::System::GetInstance().CoreTiming().GetTicks());
188 std::memcpy(output.data(), &params, output.size()); 189 std::memcpy(output.data(), &params, output.size());
189 return 0; 190 return 0;
190} 191}
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index ce1b59860..56f31e2ac 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -14,11 +14,12 @@
14#include "core/core_timing_util.h" 14#include "core/core_timing_util.h"
15#include "core/hle/kernel/kernel.h" 15#include "core/hle/kernel/kernel.h"
16#include "core/hle/kernel/readable_event.h" 16#include "core/hle/kernel/readable_event.h"
17#include "core/hle/kernel/writable_event.h"
18#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" 17#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
19#include "core/hle/service/nvdrv/nvdrv.h" 18#include "core/hle/service/nvdrv/nvdrv.h"
20#include "core/hle/service/nvflinger/buffer_queue.h" 19#include "core/hle/service/nvflinger/buffer_queue.h"
21#include "core/hle/service/nvflinger/nvflinger.h" 20#include "core/hle/service/nvflinger/nvflinger.h"
21#include "core/hle/service/vi/display/vi_display.h"
22#include "core/hle/service/vi/layer/vi_layer.h"
22#include "core/perf_stats.h" 23#include "core/perf_stats.h"
23#include "video_core/renderer_base.h" 24#include "video_core/renderer_base.h"
24 25
@@ -27,19 +28,25 @@ namespace Service::NVFlinger {
27constexpr std::size_t SCREEN_REFRESH_RATE = 60; 28constexpr std::size_t SCREEN_REFRESH_RATE = 60;
28constexpr u64 frame_ticks = static_cast<u64>(Core::Timing::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE); 29constexpr u64 frame_ticks = static_cast<u64>(Core::Timing::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE);
29 30
30NVFlinger::NVFlinger() { 31NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_timing} {
32 displays.emplace_back(0, "Default");
33 displays.emplace_back(1, "External");
34 displays.emplace_back(2, "Edid");
35 displays.emplace_back(3, "Internal");
36 displays.emplace_back(4, "Null");
37
31 // Schedule the screen composition events 38 // Schedule the screen composition events
32 composition_event = 39 composition_event =
33 Core::Timing::RegisterEvent("ScreenComposition", [this](u64 userdata, int cycles_late) { 40 core_timing.RegisterEvent("ScreenComposition", [this](u64 userdata, int cycles_late) {
34 Compose(); 41 Compose();
35 Core::Timing::ScheduleEvent(frame_ticks - cycles_late, composition_event); 42 this->core_timing.ScheduleEvent(frame_ticks - cycles_late, composition_event);
36 }); 43 });
37 44
38 Core::Timing::ScheduleEvent(frame_ticks, composition_event); 45 core_timing.ScheduleEvent(frame_ticks, composition_event);
39} 46}
40 47
41NVFlinger::~NVFlinger() { 48NVFlinger::~NVFlinger() {
42 Core::Timing::UnscheduleEvent(composition_event, 0); 49 core_timing.UnscheduleEvent(composition_event, 0);
43} 50}
44 51
45void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { 52void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
@@ -52,13 +59,14 @@ std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
52 // TODO(Subv): Currently we only support the Default display. 59 // TODO(Subv): Currently we only support the Default display.
53 ASSERT(name == "Default"); 60 ASSERT(name == "Default");
54 61
55 const auto itr = std::find_if(displays.begin(), displays.end(), 62 const auto itr =
56 [&](const Display& display) { return display.name == name; }); 63 std::find_if(displays.begin(), displays.end(),
64 [&](const VI::Display& display) { return display.GetName() == name; });
57 if (itr == displays.end()) { 65 if (itr == displays.end()) {
58 return {}; 66 return {};
59 } 67 }
60 68
61 return itr->id; 69 return itr->GetID();
62} 70}
63 71
64std::optional<u64> NVFlinger::CreateLayer(u64 display_id) { 72std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
@@ -68,13 +76,10 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
68 return {}; 76 return {};
69 } 77 }
70 78
71 ASSERT_MSG(display->layers.empty(), "Only one layer is supported per display at the moment");
72
73 const u64 layer_id = next_layer_id++; 79 const u64 layer_id = next_layer_id++;
74 const u32 buffer_queue_id = next_buffer_queue_id++; 80 const u32 buffer_queue_id = next_buffer_queue_id++;
75 auto buffer_queue = std::make_shared<BufferQueue>(buffer_queue_id, layer_id); 81 buffer_queues.emplace_back(buffer_queue_id, layer_id);
76 display->layers.emplace_back(layer_id, buffer_queue); 82 display->CreateLayer(layer_id, buffer_queues.back());
77 buffer_queues.emplace_back(std::move(buffer_queue));
78 return layer_id; 83 return layer_id;
79} 84}
80 85
@@ -85,7 +90,7 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) co
85 return {}; 90 return {};
86 } 91 }
87 92
88 return layer->buffer_queue->GetId(); 93 return layer->GetBufferQueue().GetId();
89} 94}
90 95
91Kernel::SharedPtr<Kernel::ReadableEvent> NVFlinger::FindVsyncEvent(u64 display_id) const { 96Kernel::SharedPtr<Kernel::ReadableEvent> NVFlinger::FindVsyncEvent(u64 display_id) const {
@@ -95,20 +100,29 @@ Kernel::SharedPtr<Kernel::ReadableEvent> NVFlinger::FindVsyncEvent(u64 display_i
95 return nullptr; 100 return nullptr;
96 } 101 }
97 102
98 return display->vsync_event.readable; 103 return display->GetVSyncEvent();
104}
105
106BufferQueue& NVFlinger::FindBufferQueue(u32 id) {
107 const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
108 [id](const auto& queue) { return queue.GetId() == id; });
109
110 ASSERT(itr != buffer_queues.end());
111 return *itr;
99} 112}
100 113
101std::shared_ptr<BufferQueue> NVFlinger::FindBufferQueue(u32 id) const { 114const BufferQueue& NVFlinger::FindBufferQueue(u32 id) const {
102 const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(), 115 const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
103 [&](const auto& queue) { return queue->GetId() == id; }); 116 [id](const auto& queue) { return queue.GetId() == id; });
104 117
105 ASSERT(itr != buffer_queues.end()); 118 ASSERT(itr != buffer_queues.end());
106 return *itr; 119 return *itr;
107} 120}
108 121
109Display* NVFlinger::FindDisplay(u64 display_id) { 122VI::Display* NVFlinger::FindDisplay(u64 display_id) {
110 const auto itr = std::find_if(displays.begin(), displays.end(), 123 const auto itr =
111 [&](const Display& display) { return display.id == display_id; }); 124 std::find_if(displays.begin(), displays.end(),
125 [&](const VI::Display& display) { return display.GetID() == display_id; });
112 126
113 if (itr == displays.end()) { 127 if (itr == displays.end()) {
114 return nullptr; 128 return nullptr;
@@ -117,9 +131,10 @@ Display* NVFlinger::FindDisplay(u64 display_id) {
117 return &*itr; 131 return &*itr;
118} 132}
119 133
120const Display* NVFlinger::FindDisplay(u64 display_id) const { 134const VI::Display* NVFlinger::FindDisplay(u64 display_id) const {
121 const auto itr = std::find_if(displays.begin(), displays.end(), 135 const auto itr =
122 [&](const Display& display) { return display.id == display_id; }); 136 std::find_if(displays.begin(), displays.end(),
137 [&](const VI::Display& display) { return display.GetID() == display_id; });
123 138
124 if (itr == displays.end()) { 139 if (itr == displays.end()) {
125 return nullptr; 140 return nullptr;
@@ -128,57 +143,41 @@ const Display* NVFlinger::FindDisplay(u64 display_id) const {
128 return &*itr; 143 return &*itr;
129} 144}
130 145
131Layer* NVFlinger::FindLayer(u64 display_id, u64 layer_id) { 146VI::Layer* NVFlinger::FindLayer(u64 display_id, u64 layer_id) {
132 auto* const display = FindDisplay(display_id); 147 auto* const display = FindDisplay(display_id);
133 148
134 if (display == nullptr) { 149 if (display == nullptr) {
135 return nullptr; 150 return nullptr;
136 } 151 }
137 152
138 const auto itr = std::find_if(display->layers.begin(), display->layers.end(), 153 return display->FindLayer(layer_id);
139 [&](const Layer& layer) { return layer.id == layer_id; });
140
141 if (itr == display->layers.end()) {
142 return nullptr;
143 }
144
145 return &*itr;
146} 154}
147 155
148const Layer* NVFlinger::FindLayer(u64 display_id, u64 layer_id) const { 156const VI::Layer* NVFlinger::FindLayer(u64 display_id, u64 layer_id) const {
149 const auto* const display = FindDisplay(display_id); 157 const auto* const display = FindDisplay(display_id);
150 158
151 if (display == nullptr) { 159 if (display == nullptr) {
152 return nullptr; 160 return nullptr;
153 } 161 }
154 162
155 const auto itr = std::find_if(display->layers.begin(), display->layers.end(), 163 return display->FindLayer(layer_id);
156 [&](const Layer& layer) { return layer.id == layer_id; });
157
158 if (itr == display->layers.end()) {
159 return nullptr;
160 }
161
162 return &*itr;
163} 164}
164 165
165void NVFlinger::Compose() { 166void NVFlinger::Compose() {
166 for (auto& display : displays) { 167 for (auto& display : displays) {
167 // Trigger vsync for this display at the end of drawing 168 // Trigger vsync for this display at the end of drawing
168 SCOPE_EXIT({ display.vsync_event.writable->Signal(); }); 169 SCOPE_EXIT({ display.SignalVSyncEvent(); });
169 170
170 // Don't do anything for displays without layers. 171 // Don't do anything for displays without layers.
171 if (display.layers.empty()) 172 if (!display.HasLayers())
172 continue; 173 continue;
173 174
174 // TODO(Subv): Support more than 1 layer. 175 // TODO(Subv): Support more than 1 layer.
175 ASSERT_MSG(display.layers.size() == 1, "Max 1 layer per display is supported"); 176 VI::Layer& layer = display.GetLayer(0);
176 177 auto& buffer_queue = layer.GetBufferQueue();
177 Layer& layer = display.layers[0];
178 auto& buffer_queue = layer.buffer_queue;
179 178
180 // Search for a queued buffer and acquire it 179 // Search for a queued buffer and acquire it
181 auto buffer = buffer_queue->AcquireBuffer(); 180 auto buffer = buffer_queue.AcquireBuffer();
182 181
183 MicroProfileFlip(); 182 MicroProfileFlip();
184 183
@@ -203,19 +202,8 @@ void NVFlinger::Compose() {
203 igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, 202 igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride,
204 buffer->get().transform, buffer->get().crop_rect); 203 buffer->get().transform, buffer->get().crop_rect);
205 204
206 buffer_queue->ReleaseBuffer(buffer->get().slot); 205 buffer_queue.ReleaseBuffer(buffer->get().slot);
207 } 206 }
208} 207}
209 208
210Layer::Layer(u64 id, std::shared_ptr<BufferQueue> queue) : id(id), buffer_queue(std::move(queue)) {}
211Layer::~Layer() = default;
212
213Display::Display(u64 id, std::string name) : id(id), name(std::move(name)) {
214 auto& kernel = Core::System::GetInstance().Kernel();
215 vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
216 fmt::format("Display VSync Event {}", id));
217}
218
219Display::~Display() = default;
220
221} // namespace Service::NVFlinger 209} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index 6d8bcbd30..c0a83fffb 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -4,7 +4,6 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include <memory> 7#include <memory>
9#include <optional> 8#include <optional>
10#include <string> 9#include <string>
@@ -15,8 +14,9 @@
15#include "core/hle/kernel/object.h" 14#include "core/hle/kernel/object.h"
16 15
17namespace Core::Timing { 16namespace Core::Timing {
17class CoreTiming;
18struct EventType; 18struct EventType;
19} 19} // namespace Core::Timing
20 20
21namespace Kernel { 21namespace Kernel {
22class ReadableEvent; 22class ReadableEvent;
@@ -25,34 +25,20 @@ class WritableEvent;
25 25
26namespace Service::Nvidia { 26namespace Service::Nvidia {
27class Module; 27class Module;
28} 28} // namespace Service::Nvidia
29
30namespace Service::VI {
31class Display;
32class Layer;
33} // namespace Service::VI
29 34
30namespace Service::NVFlinger { 35namespace Service::NVFlinger {
31 36
32class BufferQueue; 37class BufferQueue;
33 38
34struct Layer {
35 Layer(u64 id, std::shared_ptr<BufferQueue> queue);
36 ~Layer();
37
38 u64 id;
39 std::shared_ptr<BufferQueue> buffer_queue;
40};
41
42struct Display {
43 Display(u64 id, std::string name);
44 ~Display();
45
46 u64 id;
47 std::string name;
48
49 std::vector<Layer> layers;
50 Kernel::EventPair vsync_event;
51};
52
53class NVFlinger final { 39class NVFlinger final {
54public: 40public:
55 NVFlinger(); 41 explicit NVFlinger(Core::Timing::CoreTiming& core_timing);
56 ~NVFlinger(); 42 ~NVFlinger();
57 43
58 /// Sets the NVDrv module instance to use to send buffers to the GPU. 44 /// Sets the NVDrv module instance to use to send buffers to the GPU.
@@ -79,7 +65,10 @@ public:
79 Kernel::SharedPtr<Kernel::ReadableEvent> FindVsyncEvent(u64 display_id) const; 65 Kernel::SharedPtr<Kernel::ReadableEvent> FindVsyncEvent(u64 display_id) const;
80 66
81 /// Obtains a buffer queue identified by the ID. 67 /// Obtains a buffer queue identified by the ID.
82 std::shared_ptr<BufferQueue> FindBufferQueue(u32 id) const; 68 BufferQueue& FindBufferQueue(u32 id);
69
70 /// Obtains a buffer queue identified by the ID.
71 const BufferQueue& FindBufferQueue(u32 id) const;
83 72
84 /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when 73 /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when
85 /// finished. 74 /// finished.
@@ -87,27 +76,21 @@ public:
87 76
88private: 77private:
89 /// Finds the display identified by the specified ID. 78 /// Finds the display identified by the specified ID.
90 Display* FindDisplay(u64 display_id); 79 VI::Display* FindDisplay(u64 display_id);
91 80
92 /// Finds the display identified by the specified ID. 81 /// Finds the display identified by the specified ID.
93 const Display* FindDisplay(u64 display_id) const; 82 const VI::Display* FindDisplay(u64 display_id) const;
94 83
95 /// Finds the layer identified by the specified ID in the desired display. 84 /// Finds the layer identified by the specified ID in the desired display.
96 Layer* FindLayer(u64 display_id, u64 layer_id); 85 VI::Layer* FindLayer(u64 display_id, u64 layer_id);
97 86
98 /// Finds the layer identified by the specified ID in the desired display. 87 /// Finds the layer identified by the specified ID in the desired display.
99 const Layer* FindLayer(u64 display_id, u64 layer_id) const; 88 const VI::Layer* FindLayer(u64 display_id, u64 layer_id) const;
100 89
101 std::shared_ptr<Nvidia::Module> nvdrv; 90 std::shared_ptr<Nvidia::Module> nvdrv;
102 91
103 std::array<Display, 5> displays{{ 92 std::vector<VI::Display> displays;
104 {0, "Default"}, 93 std::vector<BufferQueue> buffer_queues;
105 {1, "External"},
106 {2, "Edid"},
107 {3, "Internal"},
108 {4, "Null"},
109 }};
110 std::vector<std::shared_ptr<BufferQueue>> buffer_queues;
111 94
112 /// Id to use for the next layer that is created, this counter is shared among all displays. 95 /// Id to use for the next layer that is created, this counter is shared among all displays.
113 u64 next_layer_id = 1; 96 u64 next_layer_id = 1;
@@ -117,6 +100,9 @@ private:
117 100
118 /// Event that handles screen composition. 101 /// Event that handles screen composition.
119 Core::Timing::EventType* composition_event; 102 Core::Timing::EventType* composition_event;
103
104 /// Core timing instance for registering/unregistering the composition event.
105 Core::Timing::CoreTiming& core_timing;
120}; 106};
121 107
122} // namespace Service::NVFlinger 108} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index d25b80ab0..117f87a45 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -194,10 +194,11 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
194// Module interface 194// Module interface
195 195
196/// Initialize ServiceManager 196/// Initialize ServiceManager
197void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs) { 197void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system,
198 FileSys::VfsFilesystem& vfs) {
198 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it 199 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
199 // here and pass it into the respective InstallInterfaces functions. 200 // here and pass it into the respective InstallInterfaces functions.
200 auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(); 201 auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(system.CoreTiming());
201 202
202 SM::ServiceManager::InstallInterfaces(sm); 203 SM::ServiceManager::InstallInterfaces(sm);
203 204
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 029533628..830790269 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -14,6 +14,14 @@
14//////////////////////////////////////////////////////////////////////////////////////////////////// 14////////////////////////////////////////////////////////////////////////////////////////////////////
15// Namespace Service 15// Namespace Service
16 16
17namespace Core {
18class System;
19}
20
21namespace FileSys {
22class VfsFilesystem;
23}
24
17namespace Kernel { 25namespace Kernel {
18class ClientPort; 26class ClientPort;
19class ServerPort; 27class ServerPort;
@@ -21,10 +29,6 @@ class ServerSession;
21class HLERequestContext; 29class HLERequestContext;
22} // namespace Kernel 30} // namespace Kernel
23 31
24namespace FileSys {
25class VfsFilesystem;
26}
27
28namespace Service { 32namespace Service {
29 33
30namespace SM { 34namespace SM {
@@ -178,7 +182,8 @@ private:
178}; 182};
179 183
180/// Initialize ServiceManager 184/// Initialize ServiceManager
181void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs); 185void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system,
186 FileSys::VfsFilesystem& vfs);
182 187
183/// Shutdown ServiceManager 188/// Shutdown ServiceManager
184void Shutdown(); 189void Shutdown();
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index efebd1b24..aa115935d 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -5,6 +5,7 @@
5#include <chrono> 5#include <chrono>
6#include <ctime> 6#include <ctime>
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/core.h"
8#include "core/core_timing.h" 9#include "core/core_timing.h"
9#include "core/core_timing_util.h" 10#include "core/core_timing_util.h"
10#include "core/hle/ipc_helpers.h" 11#include "core/hle/ipc_helpers.h"
@@ -106,8 +107,9 @@ private:
106 void GetCurrentTimePoint(Kernel::HLERequestContext& ctx) { 107 void GetCurrentTimePoint(Kernel::HLERequestContext& ctx) {
107 LOG_DEBUG(Service_Time, "called"); 108 LOG_DEBUG(Service_Time, "called");
108 109
110 const auto& core_timing = Core::System::GetInstance().CoreTiming();
109 const SteadyClockTimePoint steady_clock_time_point{ 111 const SteadyClockTimePoint steady_clock_time_point{
110 Core::Timing::cyclesToMs(Core::Timing::GetTicks()) / 1000}; 112 Core::Timing::cyclesToMs(core_timing.GetTicks()) / 1000};
111 IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2}; 113 IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2};
112 rb.Push(RESULT_SUCCESS); 114 rb.Push(RESULT_SUCCESS);
113 rb.PushRaw(steady_clock_time_point); 115 rb.PushRaw(steady_clock_time_point);
@@ -281,8 +283,9 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
281 return; 283 return;
282 } 284 }
283 285
286 const auto& core_timing = Core::System::GetInstance().CoreTiming();
284 const SteadyClockTimePoint steady_clock_time_point{ 287 const SteadyClockTimePoint steady_clock_time_point{
285 Core::Timing::cyclesToMs(Core::Timing::GetTicks()) / 1000, {}}; 288 Core::Timing::cyclesToMs(core_timing.GetTicks()) / 1000, {}};
286 289
287 CalendarTime calendar_time{}; 290 CalendarTime calendar_time{};
288 calendar_time.year = tm->tm_year + 1900; 291 calendar_time.year = tm->tm_year + 1900;
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
new file mode 100644
index 000000000..01d80311b
--- /dev/null
+++ b/src/core/hle/service/vi/display/vi_display.cpp
@@ -0,0 +1,71 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <utility>
7
8#include <fmt/format.h>
9
10#include "common/assert.h"
11#include "core/core.h"
12#include "core/hle/kernel/readable_event.h"
13#include "core/hle/service/vi/display/vi_display.h"
14#include "core/hle/service/vi/layer/vi_layer.h"
15
16namespace Service::VI {
17
18Display::Display(u64 id, std::string name) : id{id}, name{std::move(name)} {
19 auto& kernel = Core::System::GetInstance().Kernel();
20 vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
21 fmt::format("Display VSync Event {}", id));
22}
23
24Display::~Display() = default;
25
26Layer& Display::GetLayer(std::size_t index) {
27 return layers.at(index);
28}
29
30const Layer& Display::GetLayer(std::size_t index) const {
31 return layers.at(index);
32}
33
34Kernel::SharedPtr<Kernel::ReadableEvent> Display::GetVSyncEvent() const {
35 return vsync_event.readable;
36}
37
38void Display::SignalVSyncEvent() {
39 vsync_event.writable->Signal();
40}
41
42void Display::CreateLayer(u64 id, NVFlinger::BufferQueue& buffer_queue) {
43 // TODO(Subv): Support more than 1 layer.
44 ASSERT_MSG(layers.empty(), "Only one layer is supported per display at the moment");
45
46 layers.emplace_back(id, buffer_queue);
47}
48
49Layer* Display::FindLayer(u64 id) {
50 const auto itr = std::find_if(layers.begin(), layers.end(),
51 [id](const VI::Layer& layer) { return layer.GetID() == id; });
52
53 if (itr == layers.end()) {
54 return nullptr;
55 }
56
57 return &*itr;
58}
59
60const Layer* Display::FindLayer(u64 id) const {
61 const auto itr = std::find_if(layers.begin(), layers.end(),
62 [id](const VI::Layer& layer) { return layer.GetID() == id; });
63
64 if (itr == layers.end()) {
65 return nullptr;
66 }
67
68 return &*itr;
69}
70
71} // namespace Service::VI
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h
new file mode 100644
index 000000000..2acd46ff8
--- /dev/null
+++ b/src/core/hle/service/vi/display/vi_display.h
@@ -0,0 +1,98 @@
1// Copyright 2019 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 <string>
8#include <vector>
9
10#include "common/common_types.h"
11#include "core/hle/kernel/writable_event.h"
12
13namespace Service::NVFlinger {
14class BufferQueue;
15}
16
17namespace Service::VI {
18
19class Layer;
20
21/// Represents a single display type
22class Display {
23public:
24 /// Constructs a display with a given unique ID and name.
25 ///
26 /// @param id The unique ID for this display.
27 /// @param name The name for this display.
28 ///
29 Display(u64 id, std::string name);
30 ~Display();
31
32 Display(const Display&) = delete;
33 Display& operator=(const Display&) = delete;
34
35 Display(Display&&) = default;
36 Display& operator=(Display&&) = default;
37
38 /// Gets the unique ID assigned to this display.
39 u64 GetID() const {
40 return id;
41 }
42
43 /// Gets the name of this display
44 const std::string& GetName() const {
45 return name;
46 }
47
48 /// Whether or not this display has any layers added to it.
49 bool HasLayers() const {
50 return !layers.empty();
51 }
52
53 /// Gets a layer for this display based off an index.
54 Layer& GetLayer(std::size_t index);
55
56 /// Gets a layer for this display based off an index.
57 const Layer& GetLayer(std::size_t index) const;
58
59 /// Gets the readable vsync event.
60 Kernel::SharedPtr<Kernel::ReadableEvent> GetVSyncEvent() const;
61
62 /// Signals the internal vsync event.
63 void SignalVSyncEvent();
64
65 /// Creates and adds a layer to this display with the given ID.
66 ///
67 /// @param id The ID to assign to the created layer.
68 /// @param buffer_queue The buffer queue for the layer instance to use.
69 ///
70 void CreateLayer(u64 id, NVFlinger::BufferQueue& buffer_queue);
71
72 /// Attempts to find a layer with the given ID.
73 ///
74 /// @param id The layer ID.
75 ///
76 /// @returns If found, the Layer instance with the given ID.
77 /// If not found, then nullptr is returned.
78 ///
79 Layer* FindLayer(u64 id);
80
81 /// Attempts to find a layer with the given ID.
82 ///
83 /// @param id The layer ID.
84 ///
85 /// @returns If found, the Layer instance with the given ID.
86 /// If not found, then nullptr is returned.
87 ///
88 const Layer* FindLayer(u64 id) const;
89
90private:
91 u64 id;
92 std::string name;
93
94 std::vector<Layer> layers;
95 Kernel::EventPair vsync_event;
96};
97
98} // namespace Service::VI
diff --git a/src/core/hle/service/vi/layer/vi_layer.cpp b/src/core/hle/service/vi/layer/vi_layer.cpp
new file mode 100644
index 000000000..954225c26
--- /dev/null
+++ b/src/core/hle/service/vi/layer/vi_layer.cpp
@@ -0,0 +1,13 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/vi/layer/vi_layer.h"
6
7namespace Service::VI {
8
9Layer::Layer(u64 id, NVFlinger::BufferQueue& queue) : id{id}, buffer_queue{queue} {}
10
11Layer::~Layer() = default;
12
13} // namespace Service::VI
diff --git a/src/core/hle/service/vi/layer/vi_layer.h b/src/core/hle/service/vi/layer/vi_layer.h
new file mode 100644
index 000000000..c6bfd01f6
--- /dev/null
+++ b/src/core/hle/service/vi/layer/vi_layer.h
@@ -0,0 +1,52 @@
1// Copyright 2019 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 "common/common_types.h"
8
9namespace Service::NVFlinger {
10class BufferQueue;
11}
12
13namespace Service::VI {
14
15/// Represents a single display layer.
16class Layer {
17public:
18 /// Constructs a layer with a given ID and buffer queue.
19 ///
20 /// @param id The ID to assign to this layer.
21 /// @param queue The buffer queue for this layer to use.
22 ///
23 Layer(u64 id, NVFlinger::BufferQueue& queue);
24 ~Layer();
25
26 Layer(const Layer&) = delete;
27 Layer& operator=(const Layer&) = delete;
28
29 Layer(Layer&&) = default;
30 Layer& operator=(Layer&&) = delete;
31
32 /// Gets the ID for this layer.
33 u64 GetID() const {
34 return id;
35 }
36
37 /// Gets a reference to the buffer queue this layer is using.
38 NVFlinger::BufferQueue& GetBufferQueue() {
39 return buffer_queue;
40 }
41
42 /// Gets a const reference to the buffer queue this layer is using.
43 const NVFlinger::BufferQueue& GetBufferQueue() const {
44 return buffer_queue;
45 }
46
47private:
48 u64 id;
49 NVFlinger::BufferQueue& buffer_queue;
50};
51
52} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index a317a2885..7369a09ec 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -525,7 +525,7 @@ private:
525 LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, 525 LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id,
526 static_cast<u32>(transaction), flags); 526 static_cast<u32>(transaction), flags);
527 527
528 auto buffer_queue = nv_flinger->FindBufferQueue(id); 528 auto& buffer_queue = nv_flinger->FindBufferQueue(id);
529 529
530 if (transaction == TransactionId::Connect) { 530 if (transaction == TransactionId::Connect) {
531 IGBPConnectRequestParcel request{ctx.ReadBuffer()}; 531 IGBPConnectRequestParcel request{ctx.ReadBuffer()};
@@ -538,7 +538,7 @@ private:
538 } else if (transaction == TransactionId::SetPreallocatedBuffer) { 538 } else if (transaction == TransactionId::SetPreallocatedBuffer) {
539 IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()}; 539 IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()};
540 540
541 buffer_queue->SetPreallocatedBuffer(request.data.slot, request.buffer); 541 buffer_queue.SetPreallocatedBuffer(request.data.slot, request.buffer);
542 542
543 IGBPSetPreallocatedBufferResponseParcel response{}; 543 IGBPSetPreallocatedBufferResponseParcel response{};
544 ctx.WriteBuffer(response.Serialize()); 544 ctx.WriteBuffer(response.Serialize());
@@ -546,7 +546,7 @@ private:
546 IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()}; 546 IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()};
547 const u32 width{request.data.width}; 547 const u32 width{request.data.width};
548 const u32 height{request.data.height}; 548 const u32 height{request.data.height};
549 std::optional<u32> slot = buffer_queue->DequeueBuffer(width, height); 549 std::optional<u32> slot = buffer_queue.DequeueBuffer(width, height);
550 550
551 if (slot) { 551 if (slot) {
552 // Buffer is available 552 // Buffer is available
@@ -559,8 +559,8 @@ private:
559 [=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx, 559 [=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
560 Kernel::ThreadWakeupReason reason) { 560 Kernel::ThreadWakeupReason reason) {
561 // Repeat TransactParcel DequeueBuffer when a buffer is available 561 // Repeat TransactParcel DequeueBuffer when a buffer is available
562 auto buffer_queue = nv_flinger->FindBufferQueue(id); 562 auto& buffer_queue = nv_flinger->FindBufferQueue(id);
563 std::optional<u32> slot = buffer_queue->DequeueBuffer(width, height); 563 std::optional<u32> slot = buffer_queue.DequeueBuffer(width, height);
564 ASSERT_MSG(slot != std::nullopt, "Could not dequeue buffer."); 564 ASSERT_MSG(slot != std::nullopt, "Could not dequeue buffer.");
565 565
566 IGBPDequeueBufferResponseParcel response{*slot}; 566 IGBPDequeueBufferResponseParcel response{*slot};
@@ -568,28 +568,28 @@ private:
568 IPC::ResponseBuilder rb{ctx, 2}; 568 IPC::ResponseBuilder rb{ctx, 2};
569 rb.Push(RESULT_SUCCESS); 569 rb.Push(RESULT_SUCCESS);
570 }, 570 },
571 buffer_queue->GetWritableBufferWaitEvent()); 571 buffer_queue.GetWritableBufferWaitEvent());
572 } 572 }
573 } else if (transaction == TransactionId::RequestBuffer) { 573 } else if (transaction == TransactionId::RequestBuffer) {
574 IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()}; 574 IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()};
575 575
576 auto& buffer = buffer_queue->RequestBuffer(request.slot); 576 auto& buffer = buffer_queue.RequestBuffer(request.slot);
577 577
578 IGBPRequestBufferResponseParcel response{buffer}; 578 IGBPRequestBufferResponseParcel response{buffer};
579 ctx.WriteBuffer(response.Serialize()); 579 ctx.WriteBuffer(response.Serialize());
580 } else if (transaction == TransactionId::QueueBuffer) { 580 } else if (transaction == TransactionId::QueueBuffer) {
581 IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()}; 581 IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()};
582 582
583 buffer_queue->QueueBuffer(request.data.slot, request.data.transform, 583 buffer_queue.QueueBuffer(request.data.slot, request.data.transform,
584 request.data.GetCropRect()); 584 request.data.GetCropRect());
585 585
586 IGBPQueueBufferResponseParcel response{1280, 720}; 586 IGBPQueueBufferResponseParcel response{1280, 720};
587 ctx.WriteBuffer(response.Serialize()); 587 ctx.WriteBuffer(response.Serialize());
588 } else if (transaction == TransactionId::Query) { 588 } else if (transaction == TransactionId::Query) {
589 IGBPQueryRequestParcel request{ctx.ReadBuffer()}; 589 IGBPQueryRequestParcel request{ctx.ReadBuffer()};
590 590
591 u32 value = 591 const u32 value =
592 buffer_queue->Query(static_cast<NVFlinger::BufferQueue::QueryType>(request.type)); 592 buffer_queue.Query(static_cast<NVFlinger::BufferQueue::QueryType>(request.type));
593 593
594 IGBPQueryResponseParcel response{value}; 594 IGBPQueryResponseParcel response{value};
595 ctx.WriteBuffer(response.Serialize()); 595 ctx.WriteBuffer(response.Serialize());
@@ -629,12 +629,12 @@ private:
629 629
630 LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown); 630 LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
631 631
632 const auto buffer_queue = nv_flinger->FindBufferQueue(id); 632 const auto& buffer_queue = nv_flinger->FindBufferQueue(id);
633 633
634 // TODO(Subv): Find out what this actually is. 634 // TODO(Subv): Find out what this actually is.
635 IPC::ResponseBuilder rb{ctx, 2, 1}; 635 IPC::ResponseBuilder rb{ctx, 2, 1};
636 rb.Push(RESULT_SUCCESS); 636 rb.Push(RESULT_SUCCESS);
637 rb.PushCopyObjects(buffer_queue->GetBufferWaitEvent()); 637 rb.PushCopyObjects(buffer_queue.GetBufferWaitEvent());
638 } 638 }
639 639
640 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; 640 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp
index 77607a755..340d6a272 100644
--- a/src/tests/core/core_timing.cpp
+++ b/src/tests/core/core_timing.cpp
@@ -28,100 +28,103 @@ void CallbackTemplate(u64 userdata, s64 cycles_late) {
28 REQUIRE(lateness == cycles_late); 28 REQUIRE(lateness == cycles_late);
29} 29}
30 30
31class ScopeInit final { 31struct ScopeInit final {
32public:
33 ScopeInit() { 32 ScopeInit() {
34 Core::Timing::Init(); 33 core_timing.Initialize();
35 } 34 }
36 ~ScopeInit() { 35 ~ScopeInit() {
37 Core::Timing::Shutdown(); 36 core_timing.Shutdown();
38 } 37 }
38
39 Core::Timing::CoreTiming core_timing;
39}; 40};
40 41
41static void AdvanceAndCheck(u32 idx, int downcount, int expected_lateness = 0, 42static void AdvanceAndCheck(Core::Timing::CoreTiming& core_timing, u32 idx, int downcount,
42 int cpu_downcount = 0) { 43 int expected_lateness = 0, int cpu_downcount = 0) {
43 callbacks_ran_flags = 0; 44 callbacks_ran_flags = 0;
44 expected_callback = CB_IDS[idx]; 45 expected_callback = CB_IDS[idx];
45 lateness = expected_lateness; 46 lateness = expected_lateness;
46 47
47 // Pretend we executed X cycles of instructions. 48 // Pretend we executed X cycles of instructions.
48 Core::Timing::AddTicks(Core::Timing::GetDowncount() - cpu_downcount); 49 core_timing.AddTicks(core_timing.GetDowncount() - cpu_downcount);
49 Core::Timing::Advance(); 50 core_timing.Advance();
50 51
51 REQUIRE(decltype(callbacks_ran_flags)().set(idx) == callbacks_ran_flags); 52 REQUIRE(decltype(callbacks_ran_flags)().set(idx) == callbacks_ran_flags);
52 REQUIRE(downcount == Core::Timing::GetDowncount()); 53 REQUIRE(downcount == core_timing.GetDowncount());
53} 54}
54 55
55TEST_CASE("CoreTiming[BasicOrder]", "[core]") { 56TEST_CASE("CoreTiming[BasicOrder]", "[core]") {
56 ScopeInit guard; 57 ScopeInit guard;
58 auto& core_timing = guard.core_timing;
57 59
58 Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", CallbackTemplate<0>); 60 Core::Timing::EventType* cb_a = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>);
59 Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", CallbackTemplate<1>); 61 Core::Timing::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>);
60 Core::Timing::EventType* cb_c = Core::Timing::RegisterEvent("callbackC", CallbackTemplate<2>); 62 Core::Timing::EventType* cb_c = core_timing.RegisterEvent("callbackC", CallbackTemplate<2>);
61 Core::Timing::EventType* cb_d = Core::Timing::RegisterEvent("callbackD", CallbackTemplate<3>); 63 Core::Timing::EventType* cb_d = core_timing.RegisterEvent("callbackD", CallbackTemplate<3>);
62 Core::Timing::EventType* cb_e = Core::Timing::RegisterEvent("callbackE", CallbackTemplate<4>); 64 Core::Timing::EventType* cb_e = core_timing.RegisterEvent("callbackE", CallbackTemplate<4>);
63 65
64 // Enter slice 0 66 // Enter slice 0
65 Core::Timing::Advance(); 67 core_timing.Advance();
66 68
67 // D -> B -> C -> A -> E 69 // D -> B -> C -> A -> E
68 Core::Timing::ScheduleEvent(1000, cb_a, CB_IDS[0]); 70 core_timing.ScheduleEvent(1000, cb_a, CB_IDS[0]);
69 REQUIRE(1000 == Core::Timing::GetDowncount()); 71 REQUIRE(1000 == core_timing.GetDowncount());
70 Core::Timing::ScheduleEvent(500, cb_b, CB_IDS[1]); 72 core_timing.ScheduleEvent(500, cb_b, CB_IDS[1]);
71 REQUIRE(500 == Core::Timing::GetDowncount()); 73 REQUIRE(500 == core_timing.GetDowncount());
72 Core::Timing::ScheduleEvent(800, cb_c, CB_IDS[2]); 74 core_timing.ScheduleEvent(800, cb_c, CB_IDS[2]);
73 REQUIRE(500 == Core::Timing::GetDowncount()); 75 REQUIRE(500 == core_timing.GetDowncount());
74 Core::Timing::ScheduleEvent(100, cb_d, CB_IDS[3]); 76 core_timing.ScheduleEvent(100, cb_d, CB_IDS[3]);
75 REQUIRE(100 == Core::Timing::GetDowncount()); 77 REQUIRE(100 == core_timing.GetDowncount());
76 Core::Timing::ScheduleEvent(1200, cb_e, CB_IDS[4]); 78 core_timing.ScheduleEvent(1200, cb_e, CB_IDS[4]);
77 REQUIRE(100 == Core::Timing::GetDowncount()); 79 REQUIRE(100 == core_timing.GetDowncount());
78 80
79 AdvanceAndCheck(3, 400); 81 AdvanceAndCheck(core_timing, 3, 400);
80 AdvanceAndCheck(1, 300); 82 AdvanceAndCheck(core_timing, 1, 300);
81 AdvanceAndCheck(2, 200); 83 AdvanceAndCheck(core_timing, 2, 200);
82 AdvanceAndCheck(0, 200); 84 AdvanceAndCheck(core_timing, 0, 200);
83 AdvanceAndCheck(4, MAX_SLICE_LENGTH); 85 AdvanceAndCheck(core_timing, 4, MAX_SLICE_LENGTH);
84} 86}
85 87
86TEST_CASE("CoreTiming[Threadsave]", "[core]") { 88TEST_CASE("CoreTiming[Threadsave]", "[core]") {
87 ScopeInit guard; 89 ScopeInit guard;
90 auto& core_timing = guard.core_timing;
88 91
89 Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", CallbackTemplate<0>); 92 Core::Timing::EventType* cb_a = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>);
90 Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", CallbackTemplate<1>); 93 Core::Timing::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>);
91 Core::Timing::EventType* cb_c = Core::Timing::RegisterEvent("callbackC", CallbackTemplate<2>); 94 Core::Timing::EventType* cb_c = core_timing.RegisterEvent("callbackC", CallbackTemplate<2>);
92 Core::Timing::EventType* cb_d = Core::Timing::RegisterEvent("callbackD", CallbackTemplate<3>); 95 Core::Timing::EventType* cb_d = core_timing.RegisterEvent("callbackD", CallbackTemplate<3>);
93 Core::Timing::EventType* cb_e = Core::Timing::RegisterEvent("callbackE", CallbackTemplate<4>); 96 Core::Timing::EventType* cb_e = core_timing.RegisterEvent("callbackE", CallbackTemplate<4>);
94 97
95 // Enter slice 0 98 // Enter slice 0
96 Core::Timing::Advance(); 99 core_timing.Advance();
97 100
98 // D -> B -> C -> A -> E 101 // D -> B -> C -> A -> E
99 Core::Timing::ScheduleEventThreadsafe(1000, cb_a, CB_IDS[0]); 102 core_timing.ScheduleEventThreadsafe(1000, cb_a, CB_IDS[0]);
100 // Manually force since ScheduleEventThreadsafe doesn't call it 103 // Manually force since ScheduleEventThreadsafe doesn't call it
101 Core::Timing::ForceExceptionCheck(1000); 104 core_timing.ForceExceptionCheck(1000);
102 REQUIRE(1000 == Core::Timing::GetDowncount()); 105 REQUIRE(1000 == core_timing.GetDowncount());
103 Core::Timing::ScheduleEventThreadsafe(500, cb_b, CB_IDS[1]); 106 core_timing.ScheduleEventThreadsafe(500, cb_b, CB_IDS[1]);
104 // Manually force since ScheduleEventThreadsafe doesn't call it 107 // Manually force since ScheduleEventThreadsafe doesn't call it
105 Core::Timing::ForceExceptionCheck(500); 108 core_timing.ForceExceptionCheck(500);
106 REQUIRE(500 == Core::Timing::GetDowncount()); 109 REQUIRE(500 == core_timing.GetDowncount());
107 Core::Timing::ScheduleEventThreadsafe(800, cb_c, CB_IDS[2]); 110 core_timing.ScheduleEventThreadsafe(800, cb_c, CB_IDS[2]);
108 // Manually force since ScheduleEventThreadsafe doesn't call it 111 // Manually force since ScheduleEventThreadsafe doesn't call it
109 Core::Timing::ForceExceptionCheck(800); 112 core_timing.ForceExceptionCheck(800);
110 REQUIRE(500 == Core::Timing::GetDowncount()); 113 REQUIRE(500 == core_timing.GetDowncount());
111 Core::Timing::ScheduleEventThreadsafe(100, cb_d, CB_IDS[3]); 114 core_timing.ScheduleEventThreadsafe(100, cb_d, CB_IDS[3]);
112 // Manually force since ScheduleEventThreadsafe doesn't call it 115 // Manually force since ScheduleEventThreadsafe doesn't call it
113 Core::Timing::ForceExceptionCheck(100); 116 core_timing.ForceExceptionCheck(100);
114 REQUIRE(100 == Core::Timing::GetDowncount()); 117 REQUIRE(100 == core_timing.GetDowncount());
115 Core::Timing::ScheduleEventThreadsafe(1200, cb_e, CB_IDS[4]); 118 core_timing.ScheduleEventThreadsafe(1200, cb_e, CB_IDS[4]);
116 // Manually force since ScheduleEventThreadsafe doesn't call it 119 // Manually force since ScheduleEventThreadsafe doesn't call it
117 Core::Timing::ForceExceptionCheck(1200); 120 core_timing.ForceExceptionCheck(1200);
118 REQUIRE(100 == Core::Timing::GetDowncount()); 121 REQUIRE(100 == core_timing.GetDowncount());
119 122
120 AdvanceAndCheck(3, 400); 123 AdvanceAndCheck(core_timing, 3, 400);
121 AdvanceAndCheck(1, 300); 124 AdvanceAndCheck(core_timing, 1, 300);
122 AdvanceAndCheck(2, 200); 125 AdvanceAndCheck(core_timing, 2, 200);
123 AdvanceAndCheck(0, 200); 126 AdvanceAndCheck(core_timing, 0, 200);
124 AdvanceAndCheck(4, MAX_SLICE_LENGTH); 127 AdvanceAndCheck(core_timing, 4, MAX_SLICE_LENGTH);
125} 128}
126 129
127namespace SharedSlotTest { 130namespace SharedSlotTest {
@@ -142,59 +145,62 @@ TEST_CASE("CoreTiming[SharedSlot]", "[core]") {
142 using namespace SharedSlotTest; 145 using namespace SharedSlotTest;
143 146
144 ScopeInit guard; 147 ScopeInit guard;
148 auto& core_timing = guard.core_timing;
145 149
146 Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", FifoCallback<0>); 150 Core::Timing::EventType* cb_a = core_timing.RegisterEvent("callbackA", FifoCallback<0>);
147 Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", FifoCallback<1>); 151 Core::Timing::EventType* cb_b = core_timing.RegisterEvent("callbackB", FifoCallback<1>);
148 Core::Timing::EventType* cb_c = Core::Timing::RegisterEvent("callbackC", FifoCallback<2>); 152 Core::Timing::EventType* cb_c = core_timing.RegisterEvent("callbackC", FifoCallback<2>);
149 Core::Timing::EventType* cb_d = Core::Timing::RegisterEvent("callbackD", FifoCallback<3>); 153 Core::Timing::EventType* cb_d = core_timing.RegisterEvent("callbackD", FifoCallback<3>);
150 Core::Timing::EventType* cb_e = Core::Timing::RegisterEvent("callbackE", FifoCallback<4>); 154 Core::Timing::EventType* cb_e = core_timing.RegisterEvent("callbackE", FifoCallback<4>);
151 155
152 Core::Timing::ScheduleEvent(1000, cb_a, CB_IDS[0]); 156 core_timing.ScheduleEvent(1000, cb_a, CB_IDS[0]);
153 Core::Timing::ScheduleEvent(1000, cb_b, CB_IDS[1]); 157 core_timing.ScheduleEvent(1000, cb_b, CB_IDS[1]);
154 Core::Timing::ScheduleEvent(1000, cb_c, CB_IDS[2]); 158 core_timing.ScheduleEvent(1000, cb_c, CB_IDS[2]);
155 Core::Timing::ScheduleEvent(1000, cb_d, CB_IDS[3]); 159 core_timing.ScheduleEvent(1000, cb_d, CB_IDS[3]);
156 Core::Timing::ScheduleEvent(1000, cb_e, CB_IDS[4]); 160 core_timing.ScheduleEvent(1000, cb_e, CB_IDS[4]);
157 161
158 // Enter slice 0 162 // Enter slice 0
159 Core::Timing::Advance(); 163 core_timing.Advance();
160 REQUIRE(1000 == Core::Timing::GetDowncount()); 164 REQUIRE(1000 == core_timing.GetDowncount());
161 165
162 callbacks_ran_flags = 0; 166 callbacks_ran_flags = 0;
163 counter = 0; 167 counter = 0;
164 lateness = 0; 168 lateness = 0;
165 Core::Timing::AddTicks(Core::Timing::GetDowncount()); 169 core_timing.AddTicks(core_timing.GetDowncount());
166 Core::Timing::Advance(); 170 core_timing.Advance();
167 REQUIRE(MAX_SLICE_LENGTH == Core::Timing::GetDowncount()); 171 REQUIRE(MAX_SLICE_LENGTH == core_timing.GetDowncount());
168 REQUIRE(0x1FULL == callbacks_ran_flags.to_ullong()); 172 REQUIRE(0x1FULL == callbacks_ran_flags.to_ullong());
169} 173}
170 174
171TEST_CASE("Core::Timing[PredictableLateness]", "[core]") { 175TEST_CASE("Core::Timing[PredictableLateness]", "[core]") {
172 ScopeInit guard; 176 ScopeInit guard;
177 auto& core_timing = guard.core_timing;
173 178
174 Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", CallbackTemplate<0>); 179 Core::Timing::EventType* cb_a = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>);
175 Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", CallbackTemplate<1>); 180 Core::Timing::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>);
176 181
177 // Enter slice 0 182 // Enter slice 0
178 Core::Timing::Advance(); 183 core_timing.Advance();
179 184
180 Core::Timing::ScheduleEvent(100, cb_a, CB_IDS[0]); 185 core_timing.ScheduleEvent(100, cb_a, CB_IDS[0]);
181 Core::Timing::ScheduleEvent(200, cb_b, CB_IDS[1]); 186 core_timing.ScheduleEvent(200, cb_b, CB_IDS[1]);
182 187
183 AdvanceAndCheck(0, 90, 10, -10); // (100 - 10) 188 AdvanceAndCheck(core_timing, 0, 90, 10, -10); // (100 - 10)
184 AdvanceAndCheck(1, MAX_SLICE_LENGTH, 50, -50); 189 AdvanceAndCheck(core_timing, 1, MAX_SLICE_LENGTH, 50, -50);
185} 190}
186 191
187namespace ChainSchedulingTest { 192namespace ChainSchedulingTest {
188static int reschedules = 0; 193static int reschedules = 0;
189 194
190static void RescheduleCallback(u64 userdata, s64 cycles_late) { 195static void RescheduleCallback(Core::Timing::CoreTiming& core_timing, u64 userdata,
196 s64 cycles_late) {
191 --reschedules; 197 --reschedules;
192 REQUIRE(reschedules >= 0); 198 REQUIRE(reschedules >= 0);
193 REQUIRE(lateness == cycles_late); 199 REQUIRE(lateness == cycles_late);
194 200
195 if (reschedules > 0) { 201 if (reschedules > 0) {
196 Core::Timing::ScheduleEvent(1000, reinterpret_cast<Core::Timing::EventType*>(userdata), 202 core_timing.ScheduleEvent(1000, reinterpret_cast<Core::Timing::EventType*>(userdata),
197 userdata); 203 userdata);
198 } 204 }
199} 205}
200} // namespace ChainSchedulingTest 206} // namespace ChainSchedulingTest
@@ -203,36 +209,39 @@ TEST_CASE("CoreTiming[ChainScheduling]", "[core]") {
203 using namespace ChainSchedulingTest; 209 using namespace ChainSchedulingTest;
204 210
205 ScopeInit guard; 211 ScopeInit guard;
212 auto& core_timing = guard.core_timing;
206 213
207 Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", CallbackTemplate<0>); 214 Core::Timing::EventType* cb_a = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>);
208 Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", CallbackTemplate<1>); 215 Core::Timing::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>);
209 Core::Timing::EventType* cb_c = Core::Timing::RegisterEvent("callbackC", CallbackTemplate<2>); 216 Core::Timing::EventType* cb_c = core_timing.RegisterEvent("callbackC", CallbackTemplate<2>);
210 Core::Timing::EventType* cb_rs = 217 Core::Timing::EventType* cb_rs = core_timing.RegisterEvent(
211 Core::Timing::RegisterEvent("callbackReschedule", RescheduleCallback); 218 "callbackReschedule", [&core_timing](u64 userdata, s64 cycles_late) {
219 RescheduleCallback(core_timing, userdata, cycles_late);
220 });
212 221
213 // Enter slice 0 222 // Enter slice 0
214 Core::Timing::Advance(); 223 core_timing.Advance();
215 224
216 Core::Timing::ScheduleEvent(800, cb_a, CB_IDS[0]); 225 core_timing.ScheduleEvent(800, cb_a, CB_IDS[0]);
217 Core::Timing::ScheduleEvent(1000, cb_b, CB_IDS[1]); 226 core_timing.ScheduleEvent(1000, cb_b, CB_IDS[1]);
218 Core::Timing::ScheduleEvent(2200, cb_c, CB_IDS[2]); 227 core_timing.ScheduleEvent(2200, cb_c, CB_IDS[2]);
219 Core::Timing::ScheduleEvent(1000, cb_rs, reinterpret_cast<u64>(cb_rs)); 228 core_timing.ScheduleEvent(1000, cb_rs, reinterpret_cast<u64>(cb_rs));
220 REQUIRE(800 == Core::Timing::GetDowncount()); 229 REQUIRE(800 == core_timing.GetDowncount());
221 230
222 reschedules = 3; 231 reschedules = 3;
223 AdvanceAndCheck(0, 200); // cb_a 232 AdvanceAndCheck(core_timing, 0, 200); // cb_a
224 AdvanceAndCheck(1, 1000); // cb_b, cb_rs 233 AdvanceAndCheck(core_timing, 1, 1000); // cb_b, cb_rs
225 REQUIRE(2 == reschedules); 234 REQUIRE(2 == reschedules);
226 235
227 Core::Timing::AddTicks(Core::Timing::GetDowncount()); 236 core_timing.AddTicks(core_timing.GetDowncount());
228 Core::Timing::Advance(); // cb_rs 237 core_timing.Advance(); // cb_rs
229 REQUIRE(1 == reschedules); 238 REQUIRE(1 == reschedules);
230 REQUIRE(200 == Core::Timing::GetDowncount()); 239 REQUIRE(200 == core_timing.GetDowncount());
231 240
232 AdvanceAndCheck(2, 800); // cb_c 241 AdvanceAndCheck(core_timing, 2, 800); // cb_c
233 242
234 Core::Timing::AddTicks(Core::Timing::GetDowncount()); 243 core_timing.AddTicks(core_timing.GetDowncount());
235 Core::Timing::Advance(); // cb_rs 244 core_timing.Advance(); // cb_rs
236 REQUIRE(0 == reschedules); 245 REQUIRE(0 == reschedules);
237 REQUIRE(MAX_SLICE_LENGTH == Core::Timing::GetDowncount()); 246 REQUIRE(MAX_SLICE_LENGTH == core_timing.GetDowncount());
238} 247}
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 1db0d031d..6036d6ed3 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -101,6 +101,22 @@ add_library(video_core STATIC
101 video_core.h 101 video_core.h
102) 102)
103 103
104if (ENABLE_VULKAN)
105 target_sources(video_core PRIVATE
106 renderer_vulkan/declarations.h
107 renderer_vulkan/vk_device.cpp
108 renderer_vulkan/vk_device.h
109 renderer_vulkan/vk_memory_manager.cpp
110 renderer_vulkan/vk_memory_manager.h
111 renderer_vulkan/vk_resource_manager.cpp
112 renderer_vulkan/vk_resource_manager.h
113 renderer_vulkan/vk_scheduler.cpp
114 renderer_vulkan/vk_scheduler.h)
115
116 target_include_directories(video_core PRIVATE ../../externals/Vulkan-Headers/include)
117 target_compile_definitions(video_core PRIVATE HAS_VULKAN)
118endif()
119
104create_target_directory_groups(video_core) 120create_target_directory_groups(video_core)
105 121
106target_link_libraries(video_core PUBLIC common core) 122target_link_libraries(video_core PUBLIC common core)
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp
index eb9bf1878..669541b4b 100644
--- a/src/video_core/dma_pusher.cpp
+++ b/src/video_core/dma_pusher.cpp
@@ -33,18 +33,36 @@ void DmaPusher::DispatchCalls() {
33} 33}
34 34
35bool DmaPusher::Step() { 35bool DmaPusher::Step() {
36 if (dma_get != dma_put) { 36 if (!ib_enable || dma_pushbuffer.empty()) {
37 // Push buffer non-empty, read a word 37 // pushbuffer empty and IB empty or nonexistent - nothing to do
38 const auto address = gpu.MemoryManager().GpuToCpuAddress(dma_get); 38 return false;
39 ASSERT_MSG(address, "Invalid GPU address"); 39 }
40 40
41 const CommandHeader command_header{Memory::Read32(*address)}; 41 const CommandList& command_list{dma_pushbuffer.front()};
42 const CommandListHeader& command_list_header{command_list[dma_pushbuffer_subindex++]};
43 GPUVAddr dma_get = command_list_header.addr;
44 GPUVAddr dma_put = dma_get + command_list_header.size * sizeof(u32);
45 bool non_main = command_list_header.is_non_main;
42 46
43 dma_get += sizeof(u32); 47 if (dma_pushbuffer_subindex >= command_list.size()) {
48 // We've gone through the current list, remove it from the queue
49 dma_pushbuffer.pop();
50 dma_pushbuffer_subindex = 0;
51 }
44 52
45 if (!non_main) { 53 if (command_list_header.size == 0) {
46 dma_mget = dma_get; 54 return true;
47 } 55 }
56
57 // Push buffer non-empty, read a word
58 const auto address = gpu.MemoryManager().GpuToCpuAddress(dma_get);
59 ASSERT_MSG(address, "Invalid GPU address");
60
61 command_headers.resize(command_list_header.size);
62
63 Memory::ReadBlock(*address, command_headers.data(), command_list_header.size * sizeof(u32));
64
65 for (const CommandHeader& command_header : command_headers) {
48 66
49 // now, see if we're in the middle of a command 67 // now, see if we're in the middle of a command
50 if (dma_state.length_pending) { 68 if (dma_state.length_pending) {
@@ -91,22 +109,11 @@ bool DmaPusher::Step() {
91 break; 109 break;
92 } 110 }
93 } 111 }
94 } else if (ib_enable && !dma_pushbuffer.empty()) { 112 }
95 // Current pushbuffer empty, but we have more IB entries to read 113
96 const CommandList& command_list{dma_pushbuffer.front()}; 114 if (!non_main) {
97 const CommandListHeader& command_list_header{command_list[dma_pushbuffer_subindex++]}; 115 // TODO (degasus): This is dead code, as dma_mget is never read.
98 dma_get = command_list_header.addr; 116 dma_mget = dma_put;
99 dma_put = dma_get + command_list_header.size * sizeof(u32);
100 non_main = command_list_header.is_non_main;
101
102 if (dma_pushbuffer_subindex >= command_list.size()) {
103 // We've gone through the current list, remove it from the queue
104 dma_pushbuffer.pop();
105 dma_pushbuffer_subindex = 0;
106 }
107 } else {
108 // Otherwise, pushbuffer empty and IB empty or nonexistent - nothing to do
109 return {};
110 } 117 }
111 118
112 return true; 119 return true;
diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h
index 1097e5c49..27a36348c 100644
--- a/src/video_core/dma_pusher.h
+++ b/src/video_core/dma_pusher.h
@@ -75,6 +75,8 @@ private:
75 75
76 GPU& gpu; 76 GPU& gpu;
77 77
78 std::vector<CommandHeader> command_headers; ///< Buffer for list of commands fetched at once
79
78 std::queue<CommandList> dma_pushbuffer; ///< Queue of command lists to be processed 80 std::queue<CommandList> dma_pushbuffer; ///< Queue of command lists to be processed
79 std::size_t dma_pushbuffer_subindex{}; ///< Index within a command list within the pushbuffer 81 std::size_t dma_pushbuffer_subindex{}; ///< Index within a command list within the pushbuffer
80 82
@@ -89,11 +91,8 @@ private:
89 DmaState dma_state{}; 91 DmaState dma_state{};
90 bool dma_increment_once{}; 92 bool dma_increment_once{};
91 93
92 GPUVAddr dma_put{}; ///< pushbuffer current end address
93 GPUVAddr dma_get{}; ///< pushbuffer current read address
94 GPUVAddr dma_mget{}; ///< main pushbuffer last read address 94 GPUVAddr dma_mget{}; ///< main pushbuffer last read address
95 bool ib_enable{true}; ///< IB mode enabled 95 bool ib_enable{true}; ///< IB mode enabled
96 bool non_main{}; ///< non-main pushbuffer active
97}; 96};
98 97
99} // namespace Tegra 98} // namespace Tegra
diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp
index 5c1029ddf..4f6126116 100644
--- a/src/video_core/engines/kepler_memory.cpp
+++ b/src/video_core/engines/kepler_memory.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/assert.h"
5#include "common/logging/log.h" 6#include "common/logging/log.h"
6#include "core/core.h" 7#include "core/core.h"
7#include "core/memory.h" 8#include "core/memory.h"
@@ -11,9 +12,9 @@
11 12
12namespace Tegra::Engines { 13namespace Tegra::Engines {
13 14
14KeplerMemory::KeplerMemory(VideoCore::RasterizerInterface& rasterizer, 15KeplerMemory::KeplerMemory(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
15 MemoryManager& memory_manager) 16 MemoryManager& memory_manager)
16 : memory_manager(memory_manager), rasterizer{rasterizer} {} 17 : system{system}, memory_manager(memory_manager), rasterizer{rasterizer} {}
17 18
18KeplerMemory::~KeplerMemory() = default; 19KeplerMemory::~KeplerMemory() = default;
19 20
@@ -50,7 +51,7 @@ void KeplerMemory::ProcessData(u32 data) {
50 rasterizer.InvalidateRegion(*dest_address, sizeof(u32)); 51 rasterizer.InvalidateRegion(*dest_address, sizeof(u32));
51 52
52 Memory::Write32(*dest_address, data); 53 Memory::Write32(*dest_address, data);
53 Core::System::GetInstance().GPU().Maxwell3D().dirty_flags.OnMemoryWrite(); 54 system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite();
54 55
55 state.write_offset++; 56 state.write_offset++;
56} 57}
diff --git a/src/video_core/engines/kepler_memory.h b/src/video_core/engines/kepler_memory.h
index fe9ebc5b9..f680c2ad9 100644
--- a/src/video_core/engines/kepler_memory.h
+++ b/src/video_core/engines/kepler_memory.h
@@ -5,13 +5,16 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include "common/assert.h"
9#include "common/bit_field.h" 8#include "common/bit_field.h"
10#include "common/common_funcs.h" 9#include "common/common_funcs.h"
11#include "common/common_types.h" 10#include "common/common_types.h"
12#include "video_core/gpu.h" 11#include "video_core/gpu.h"
13#include "video_core/memory_manager.h" 12#include "video_core/memory_manager.h"
14 13
14namespace Core {
15class System;
16}
17
15namespace VideoCore { 18namespace VideoCore {
16class RasterizerInterface; 19class RasterizerInterface;
17} 20}
@@ -23,7 +26,8 @@ namespace Tegra::Engines {
23 26
24class KeplerMemory final { 27class KeplerMemory final {
25public: 28public:
26 KeplerMemory(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager); 29 KeplerMemory(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
30 MemoryManager& memory_manager);
27 ~KeplerMemory(); 31 ~KeplerMemory();
28 32
29 /// Write the value to the register identified by method. 33 /// Write the value to the register identified by method.
@@ -76,6 +80,7 @@ public:
76 } state{}; 80 } state{};
77 81
78private: 82private:
83 Core::System& system;
79 MemoryManager& memory_manager; 84 MemoryManager& memory_manager;
80 VideoCore::RasterizerInterface& rasterizer; 85 VideoCore::RasterizerInterface& rasterizer;
81 86
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 19b6b14b2..2d2136067 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -19,8 +19,10 @@ namespace Tegra::Engines {
19/// First register id that is actually a Macro call. 19/// First register id that is actually a Macro call.
20constexpr u32 MacroRegistersStart = 0xE00; 20constexpr u32 MacroRegistersStart = 0xE00;
21 21
22Maxwell3D::Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager) 22Maxwell3D::Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
23 : memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) { 23 MemoryManager& memory_manager)
24 : memory_manager(memory_manager), system{system}, rasterizer{rasterizer},
25 macro_interpreter(*this) {
24 InitializeRegisterDefaults(); 26 InitializeRegisterDefaults();
25} 27}
26 28
@@ -103,7 +105,7 @@ void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
103} 105}
104 106
105void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) { 107void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
106 auto debug_context = Core::System::GetInstance().GetGPUDebugContext(); 108 auto debug_context = system.GetGPUDebugContext();
107 109
108 // It is an error to write to a register other than the current macro's ARG register before it 110 // It is an error to write to a register other than the current macro's ARG register before it
109 // has finished execution. 111 // has finished execution.
@@ -317,7 +319,7 @@ void Maxwell3D::ProcessQueryGet() {
317 LongQueryResult query_result{}; 319 LongQueryResult query_result{};
318 query_result.value = result; 320 query_result.value = result;
319 // TODO(Subv): Generate a real GPU timestamp and write it here instead of CoreTiming 321 // TODO(Subv): Generate a real GPU timestamp and write it here instead of CoreTiming
320 query_result.timestamp = Core::Timing::GetTicks(); 322 query_result.timestamp = system.CoreTiming().GetTicks();
321 Memory::WriteBlock(*address, &query_result, sizeof(query_result)); 323 Memory::WriteBlock(*address, &query_result, sizeof(query_result));
322 } 324 }
323 dirty_flags.OnMemoryWrite(); 325 dirty_flags.OnMemoryWrite();
@@ -334,7 +336,7 @@ void Maxwell3D::DrawArrays() {
334 regs.vertex_buffer.count); 336 regs.vertex_buffer.count);
335 ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?"); 337 ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?");
336 338
337 auto debug_context = Core::System::GetInstance().GetGPUDebugContext(); 339 auto debug_context = system.GetGPUDebugContext();
338 340
339 if (debug_context) { 341 if (debug_context) {
340 debug_context->OnEvent(Tegra::DebugContext::Event::IncomingPrimitiveBatch, nullptr); 342 debug_context->OnEvent(Tegra::DebugContext::Event::IncomingPrimitiveBatch, nullptr);
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 1f76aa670..0e3873ffd 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -17,6 +17,10 @@
17#include "video_core/memory_manager.h" 17#include "video_core/memory_manager.h"
18#include "video_core/textures/texture.h" 18#include "video_core/textures/texture.h"
19 19
20namespace Core {
21class System;
22}
23
20namespace VideoCore { 24namespace VideoCore {
21class RasterizerInterface; 25class RasterizerInterface;
22} 26}
@@ -28,7 +32,8 @@ namespace Tegra::Engines {
28 32
29class Maxwell3D final { 33class Maxwell3D final {
30public: 34public:
31 explicit Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager); 35 explicit Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
36 MemoryManager& memory_manager);
32 ~Maxwell3D() = default; 37 ~Maxwell3D() = default;
33 38
34 /// Register structure of the Maxwell3D engine. 39 /// Register structure of the Maxwell3D engine.
@@ -1131,6 +1136,8 @@ public:
1131private: 1136private:
1132 void InitializeRegisterDefaults(); 1137 void InitializeRegisterDefaults();
1133 1138
1139 Core::System& system;
1140
1134 VideoCore::RasterizerInterface& rasterizer; 1141 VideoCore::RasterizerInterface& rasterizer;
1135 1142
1136 /// Start offsets of each macro in macro_memory 1143 /// Start offsets of each macro in macro_memory
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index d6c41a5ae..529a14ec7 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/assert.h"
5#include "core/core.h" 6#include "core/core.h"
6#include "core/memory.h" 7#include "core/memory.h"
7#include "video_core/engines/maxwell_3d.h" 8#include "video_core/engines/maxwell_3d.h"
@@ -11,8 +12,9 @@
11 12
12namespace Tegra::Engines { 13namespace Tegra::Engines {
13 14
14MaxwellDMA::MaxwellDMA(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager) 15MaxwellDMA::MaxwellDMA(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
15 : memory_manager(memory_manager), rasterizer{rasterizer} {} 16 MemoryManager& memory_manager)
17 : memory_manager(memory_manager), system{system}, rasterizer{rasterizer} {}
16 18
17void MaxwellDMA::CallMethod(const GPU::MethodCall& method_call) { 19void MaxwellDMA::CallMethod(const GPU::MethodCall& method_call) {
18 ASSERT_MSG(method_call.method < Regs::NUM_REGS, 20 ASSERT_MSG(method_call.method < Regs::NUM_REGS,
@@ -59,7 +61,7 @@ void MaxwellDMA::HandleCopy() {
59 } 61 }
60 62
61 // All copies here update the main memory, so mark all rasterizer states as invalid. 63 // All copies here update the main memory, so mark all rasterizer states as invalid.
62 Core::System::GetInstance().GPU().Maxwell3D().dirty_flags.OnMemoryWrite(); 64 system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite();
63 65
64 if (regs.exec.is_dst_linear && regs.exec.is_src_linear) { 66 if (regs.exec.is_dst_linear && regs.exec.is_src_linear) {
65 // When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D 67 // When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h
index 1f8cd65d2..cf75aeb12 100644
--- a/src/video_core/engines/maxwell_dma.h
+++ b/src/video_core/engines/maxwell_dma.h
@@ -5,13 +5,16 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include "common/assert.h"
9#include "common/bit_field.h" 8#include "common/bit_field.h"
10#include "common/common_funcs.h" 9#include "common/common_funcs.h"
11#include "common/common_types.h" 10#include "common/common_types.h"
12#include "video_core/gpu.h" 11#include "video_core/gpu.h"
13#include "video_core/memory_manager.h" 12#include "video_core/memory_manager.h"
14 13
14namespace Core {
15class System;
16}
17
15namespace VideoCore { 18namespace VideoCore {
16class RasterizerInterface; 19class RasterizerInterface;
17} 20}
@@ -20,7 +23,8 @@ namespace Tegra::Engines {
20 23
21class MaxwellDMA final { 24class MaxwellDMA final {
22public: 25public:
23 explicit MaxwellDMA(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager); 26 explicit MaxwellDMA(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
27 MemoryManager& memory_manager);
24 ~MaxwellDMA() = default; 28 ~MaxwellDMA() = default;
25 29
26 /// Write the value to the register identified by method. 30 /// Write the value to the register identified by method.
@@ -137,6 +141,8 @@ public:
137 MemoryManager& memory_manager; 141 MemoryManager& memory_manager;
138 142
139private: 143private:
144 Core::System& system;
145
140 VideoCore::RasterizerInterface& rasterizer; 146 VideoCore::RasterizerInterface& rasterizer;
141 147
142 /// Performs the copy from the source buffer to the destination buffer as configured in the 148 /// Performs the copy from the source buffer to the destination buffer as configured in the
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 3d00c308b..ac30d1a89 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/assert.h" 5#include "common/assert.h"
6#include "core/core.h"
6#include "core/core_timing.h" 7#include "core/core_timing.h"
7#include "core/memory.h" 8#include "core/memory.h"
8#include "video_core/engines/fermi_2d.h" 9#include "video_core/engines/fermi_2d.h"
@@ -27,14 +28,14 @@ u32 FramebufferConfig::BytesPerPixel(PixelFormat format) {
27 UNREACHABLE(); 28 UNREACHABLE();
28} 29}
29 30
30GPU::GPU(VideoCore::RasterizerInterface& rasterizer) { 31GPU::GPU(Core::System& system, VideoCore::RasterizerInterface& rasterizer) {
31 memory_manager = std::make_unique<Tegra::MemoryManager>(); 32 memory_manager = std::make_unique<Tegra::MemoryManager>();
32 dma_pusher = std::make_unique<Tegra::DmaPusher>(*this); 33 dma_pusher = std::make_unique<Tegra::DmaPusher>(*this);
33 maxwell_3d = std::make_unique<Engines::Maxwell3D>(rasterizer, *memory_manager); 34 maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager);
34 fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer, *memory_manager); 35 fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer, *memory_manager);
35 kepler_compute = std::make_unique<Engines::KeplerCompute>(*memory_manager); 36 kepler_compute = std::make_unique<Engines::KeplerCompute>(*memory_manager);
36 maxwell_dma = std::make_unique<Engines::MaxwellDMA>(rasterizer, *memory_manager); 37 maxwell_dma = std::make_unique<Engines::MaxwellDMA>(system, rasterizer, *memory_manager);
37 kepler_memory = std::make_unique<Engines::KeplerMemory>(rasterizer, *memory_manager); 38 kepler_memory = std::make_unique<Engines::KeplerMemory>(system, rasterizer, *memory_manager);
38} 39}
39 40
40GPU::~GPU() = default; 41GPU::~GPU() = default;
@@ -283,7 +284,7 @@ void GPU::ProcessSemaphoreTriggerMethod() {
283 block.sequence = regs.semaphore_sequence; 284 block.sequence = regs.semaphore_sequence;
284 // TODO(Kmather73): Generate a real GPU timestamp and write it here instead of 285 // TODO(Kmather73): Generate a real GPU timestamp and write it here instead of
285 // CoreTiming 286 // CoreTiming
286 block.timestamp = Core::Timing::GetTicks(); 287 block.timestamp = Core::System::GetInstance().CoreTiming().GetTicks();
287 Memory::WriteBlock(*address, &block, sizeof(block)); 288 Memory::WriteBlock(*address, &block, sizeof(block));
288 } else { 289 } else {
289 const auto address = 290 const auto address =
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index a482196ea..0f5bfdcbf 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -6,12 +6,15 @@
6 6
7#include <array> 7#include <array>
8#include <memory> 8#include <memory>
9#include <vector>
10#include "common/common_types.h" 9#include "common/common_types.h"
11#include "core/hle/service/nvflinger/buffer_queue.h" 10#include "core/hle/service/nvflinger/buffer_queue.h"
12#include "video_core/dma_pusher.h" 11#include "video_core/dma_pusher.h"
13#include "video_core/memory_manager.h" 12#include "video_core/memory_manager.h"
14 13
14namespace Core {
15class System;
16}
17
15namespace VideoCore { 18namespace VideoCore {
16class RasterizerInterface; 19class RasterizerInterface;
17} 20}
@@ -118,7 +121,7 @@ enum class EngineID {
118 121
119class GPU final { 122class GPU final {
120public: 123public:
121 explicit GPU(VideoCore::RasterizerInterface& rasterizer); 124 explicit GPU(Core::System& system, VideoCore::RasterizerInterface& rasterizer);
122 ~GPU(); 125 ~GPU();
123 126
124 struct MethodCall { 127 struct MethodCall {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 69f354648..e6d47ce41 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -423,7 +423,7 @@ void SwizzleFunc(const MortonSwizzleMode& mode, const SurfaceParams& params,
423 for (u32 i = 0; i < params.depth; i++) { 423 for (u32 i = 0; i < params.depth; i++) {
424 MortonSwizzle(mode, params.pixel_format, params.MipWidth(mip_level), 424 MortonSwizzle(mode, params.pixel_format, params.MipWidth(mip_level),
425 params.MipBlockHeight(mip_level), params.MipHeight(mip_level), 425 params.MipBlockHeight(mip_level), params.MipHeight(mip_level),
426 params.MipBlockDepth(mip_level), params.tile_width_spacing, 1, 426 params.MipBlockDepth(mip_level), 1, params.tile_width_spacing,
427 gl_buffer.data() + offset_gl, gl_size, params.addr + offset); 427 gl_buffer.data() + offset_gl, gl_size, params.addr + offset);
428 offset += layer_size; 428 offset += layer_size;
429 offset_gl += gl_size; 429 offset_gl += gl_size;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index b81882d04..89d733c50 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -36,7 +36,6 @@ using PixelFormat = VideoCore::Surface::PixelFormat;
36using ComponentType = VideoCore::Surface::ComponentType; 36using ComponentType = VideoCore::Surface::ComponentType;
37 37
38struct SurfaceParams { 38struct SurfaceParams {
39
40 enum class SurfaceClass { 39 enum class SurfaceClass {
41 Uploaded, 40 Uploaded,
42 RenderTarget, 41 RenderTarget,
@@ -169,20 +168,27 @@ struct SurfaceParams {
169 } 168 }
170 169
171 u32 MipBlockDepth(u32 mip_level) const { 170 u32 MipBlockDepth(u32 mip_level) const {
172 if (mip_level == 0) 171 if (mip_level == 0) {
173 return block_depth; 172 return block_depth;
174 if (is_layered) 173 }
174
175 if (is_layered) {
175 return 1; 176 return 1;
176 u32 depth = MipDepth(mip_level); 177 }
178
179 const u32 mip_depth = MipDepth(mip_level);
177 u32 bd = 32; 180 u32 bd = 32;
178 while (bd > 1 && depth * 2 <= bd) { 181 while (bd > 1 && mip_depth * 2 <= bd) {
179 bd >>= 1; 182 bd >>= 1;
180 } 183 }
184
181 if (bd == 32) { 185 if (bd == 32) {
182 u32 bh = MipBlockHeight(mip_level); 186 const u32 bh = MipBlockHeight(mip_level);
183 if (bh >= 4) 187 if (bh >= 4) {
184 return 16; 188 return 16;
189 }
185 } 190 }
191
186 return bd; 192 return bd;
187 } 193 }
188 194
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index b39bb4843..db18f4dbe 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -616,17 +616,8 @@ private:
616 616
617 std::string VisitOperand(Operation operation, std::size_t operand_index, Type type) { 617 std::string VisitOperand(Operation operation, std::size_t operand_index, Type type) {
618 std::string value = VisitOperand(operation, operand_index); 618 std::string value = VisitOperand(operation, operand_index);
619
620 switch (type) { 619 switch (type) {
621 case Type::Bool: 620 case Type::HalfFloat: {
622 case Type::Bool2:
623 case Type::Float:
624 return value;
625 case Type::Int:
626 return "ftoi(" + value + ')';
627 case Type::Uint:
628 return "ftou(" + value + ')';
629 case Type::HalfFloat:
630 const auto half_meta = std::get_if<MetaHalfArithmetic>(&operation.GetMeta()); 621 const auto half_meta = std::get_if<MetaHalfArithmetic>(&operation.GetMeta());
631 if (!half_meta) { 622 if (!half_meta) {
632 value = "toHalf2(" + value + ')'; 623 value = "toHalf2(" + value + ')';
@@ -643,6 +634,26 @@ private:
643 return "vec2(toHalf2(" + value + ")[1])"; 634 return "vec2(toHalf2(" + value + ")[1])";
644 } 635 }
645 } 636 }
637 default:
638 return CastOperand(value, type);
639 }
640 }
641
642 std::string CastOperand(const std::string& value, Type type) const {
643 switch (type) {
644 case Type::Bool:
645 case Type::Bool2:
646 case Type::Float:
647 return value;
648 case Type::Int:
649 return "ftoi(" + value + ')';
650 case Type::Uint:
651 return "ftou(" + value + ')';
652 case Type::HalfFloat:
653 // Can't be handled as a stand-alone value
654 UNREACHABLE();
655 return value;
656 }
646 UNREACHABLE(); 657 UNREACHABLE();
647 return value; 658 return value;
648 } 659 }
@@ -650,6 +661,7 @@ private:
650 std::string BitwiseCastResult(std::string value, Type type, bool needs_parenthesis = false) { 661 std::string BitwiseCastResult(std::string value, Type type, bool needs_parenthesis = false) {
651 switch (type) { 662 switch (type) {
652 case Type::Bool: 663 case Type::Bool:
664 case Type::Bool2:
653 case Type::Float: 665 case Type::Float:
654 if (needs_parenthesis) { 666 if (needs_parenthesis) {
655 return '(' + value + ')'; 667 return '(' + value + ')';
@@ -721,7 +733,7 @@ private:
721 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); 733 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
722 ASSERT(meta); 734 ASSERT(meta);
723 735
724 const auto count = static_cast<u32>(operation.GetOperandsCount()); 736 const std::size_t count = operation.GetOperandsCount();
725 const bool has_array = meta->sampler.IsArray(); 737 const bool has_array = meta->sampler.IsArray();
726 const bool has_shadow = meta->sampler.IsShadow(); 738 const bool has_shadow = meta->sampler.IsShadow();
727 739
@@ -732,10 +744,10 @@ private:
732 744
733 expr += coord_constructors.at(count + (has_array ? 1 : 0) + (has_shadow ? 1 : 0) - 1); 745 expr += coord_constructors.at(count + (has_array ? 1 : 0) + (has_shadow ? 1 : 0) - 1);
734 expr += '('; 746 expr += '(';
735 for (u32 i = 0; i < count; ++i) { 747 for (std::size_t i = 0; i < count; ++i) {
736 expr += Visit(operation[i]); 748 expr += Visit(operation[i]);
737 749
738 const u32 next = i + 1; 750 const std::size_t next = i + 1;
739 if (next < count || has_array || has_shadow) 751 if (next < count || has_array || has_shadow)
740 expr += ", "; 752 expr += ", ";
741 } 753 }
@@ -1206,25 +1218,26 @@ private:
1206 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); 1218 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
1207 ASSERT(meta); 1219 ASSERT(meta);
1208 UNIMPLEMENTED_IF(meta->sampler.IsArray()); 1220 UNIMPLEMENTED_IF(meta->sampler.IsArray());
1209 UNIMPLEMENTED_IF(!meta->extras.empty()); 1221 const std::size_t count = operation.GetOperandsCount();
1210
1211 const auto count = static_cast<u32>(operation.GetOperandsCount());
1212 1222
1213 std::string expr = "texelFetch("; 1223 std::string expr = "texelFetch(";
1214 expr += GetSampler(meta->sampler); 1224 expr += GetSampler(meta->sampler);
1215 expr += ", "; 1225 expr += ", ";
1216 1226
1217 expr += constructors.at(count - 1); 1227 expr += constructors.at(operation.GetOperandsCount() - 1);
1218 expr += '('; 1228 expr += '(';
1219 for (u32 i = 0; i < count; ++i) { 1229 for (std::size_t i = 0; i < count; ++i) {
1220 expr += VisitOperand(operation, i, Type::Int); 1230 expr += VisitOperand(operation, i, Type::Int);
1221 1231 const std::size_t next = i + 1;
1222 const u32 next = i + 1;
1223 if (next == count) 1232 if (next == count)
1224 expr += ')'; 1233 expr += ')';
1225 if (next < count) 1234 else if (next < count)
1226 expr += ", "; 1235 expr += ", ";
1227 } 1236 }
1237 for (std::size_t i = 0; i < meta->extras.size(); ++i) {
1238 expr += ", ";
1239 expr += CastOperand(Visit(meta->extras.at(i)), Type::Int);
1240 }
1228 expr += ')'; 1241 expr += ')';
1229 1242
1230 return expr + GetSwizzle(meta->element); 1243 return expr + GetSwizzle(meta->element);
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 81af803bc..219f08053 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -11,7 +11,9 @@
11namespace OpenGL { 11namespace OpenGL {
12 12
13OpenGLState OpenGLState::cur_state; 13OpenGLState OpenGLState::cur_state;
14
14bool OpenGLState::s_rgb_used; 15bool OpenGLState::s_rgb_used;
16
15OpenGLState::OpenGLState() { 17OpenGLState::OpenGLState() {
16 // These all match default OpenGL values 18 // These all match default OpenGL values
17 geometry_shaders.enabled = false; 19 geometry_shaders.enabled = false;
@@ -112,7 +114,6 @@ void OpenGLState::ApplyDefaultState() {
112} 114}
113 115
114void OpenGLState::ApplySRgb() const { 116void OpenGLState::ApplySRgb() const {
115 // sRGB
116 if (framebuffer_srgb.enabled != cur_state.framebuffer_srgb.enabled) { 117 if (framebuffer_srgb.enabled != cur_state.framebuffer_srgb.enabled) {
117 if (framebuffer_srgb.enabled) { 118 if (framebuffer_srgb.enabled) {
118 // Track if sRGB is used 119 // Track if sRGB is used
@@ -125,23 +126,20 @@ void OpenGLState::ApplySRgb() const {
125} 126}
126 127
127void OpenGLState::ApplyCulling() const { 128void OpenGLState::ApplyCulling() const {
128 // Culling 129 if (cull.enabled != cur_state.cull.enabled) {
129 const bool cull_changed = cull.enabled != cur_state.cull.enabled;
130 if (cull_changed) {
131 if (cull.enabled) { 130 if (cull.enabled) {
132 glEnable(GL_CULL_FACE); 131 glEnable(GL_CULL_FACE);
133 } else { 132 } else {
134 glDisable(GL_CULL_FACE); 133 glDisable(GL_CULL_FACE);
135 } 134 }
136 } 135 }
137 if (cull.enabled) {
138 if (cull_changed || cull.mode != cur_state.cull.mode) {
139 glCullFace(cull.mode);
140 }
141 136
142 if (cull_changed || cull.front_face != cur_state.cull.front_face) { 137 if (cull.mode != cur_state.cull.mode) {
143 glFrontFace(cull.front_face); 138 glCullFace(cull.mode);
144 } 139 }
140
141 if (cull.front_face != cur_state.cull.front_face) {
142 glFrontFace(cull.front_face);
145 } 143 }
146} 144}
147 145
@@ -172,72 +170,63 @@ void OpenGLState::ApplyColorMask() const {
172} 170}
173 171
174void OpenGLState::ApplyDepth() const { 172void OpenGLState::ApplyDepth() const {
175 // Depth test 173 if (depth.test_enabled != cur_state.depth.test_enabled) {
176 const bool depth_test_changed = depth.test_enabled != cur_state.depth.test_enabled;
177 if (depth_test_changed) {
178 if (depth.test_enabled) { 174 if (depth.test_enabled) {
179 glEnable(GL_DEPTH_TEST); 175 glEnable(GL_DEPTH_TEST);
180 } else { 176 } else {
181 glDisable(GL_DEPTH_TEST); 177 glDisable(GL_DEPTH_TEST);
182 } 178 }
183 } 179 }
184 if (depth.test_enabled && 180
185 (depth_test_changed || depth.test_func != cur_state.depth.test_func)) { 181 if (depth.test_func != cur_state.depth.test_func) {
186 glDepthFunc(depth.test_func); 182 glDepthFunc(depth.test_func);
187 } 183 }
188 // Depth mask 184
189 if (depth.write_mask != cur_state.depth.write_mask) { 185 if (depth.write_mask != cur_state.depth.write_mask) {
190 glDepthMask(depth.write_mask); 186 glDepthMask(depth.write_mask);
191 } 187 }
192} 188}
193 189
194void OpenGLState::ApplyPrimitiveRestart() const { 190void OpenGLState::ApplyPrimitiveRestart() const {
195 const bool primitive_restart_changed = 191 if (primitive_restart.enabled != cur_state.primitive_restart.enabled) {
196 primitive_restart.enabled != cur_state.primitive_restart.enabled;
197 if (primitive_restart_changed) {
198 if (primitive_restart.enabled) { 192 if (primitive_restart.enabled) {
199 glEnable(GL_PRIMITIVE_RESTART); 193 glEnable(GL_PRIMITIVE_RESTART);
200 } else { 194 } else {
201 glDisable(GL_PRIMITIVE_RESTART); 195 glDisable(GL_PRIMITIVE_RESTART);
202 } 196 }
203 } 197 }
204 if (primitive_restart_changed || 198
205 (primitive_restart.enabled && 199 if (primitive_restart.index != cur_state.primitive_restart.index) {
206 primitive_restart.index != cur_state.primitive_restart.index)) {
207 glPrimitiveRestartIndex(primitive_restart.index); 200 glPrimitiveRestartIndex(primitive_restart.index);
208 } 201 }
209} 202}
210 203
211void OpenGLState::ApplyStencilTest() const { 204void OpenGLState::ApplyStencilTest() const {
212 const bool stencil_test_changed = stencil.test_enabled != cur_state.stencil.test_enabled; 205 if (stencil.test_enabled != cur_state.stencil.test_enabled) {
213 if (stencil_test_changed) {
214 if (stencil.test_enabled) { 206 if (stencil.test_enabled) {
215 glEnable(GL_STENCIL_TEST); 207 glEnable(GL_STENCIL_TEST);
216 } else { 208 } else {
217 glDisable(GL_STENCIL_TEST); 209 glDisable(GL_STENCIL_TEST);
218 } 210 }
219 } 211 }
220 if (stencil.test_enabled) { 212
221 auto config_stencil = [stencil_test_changed](GLenum face, const auto& config, 213 const auto ConfigStencil = [](GLenum face, const auto& config, const auto& prev_config) {
222 const auto& prev_config) { 214 if (config.test_func != prev_config.test_func || config.test_ref != prev_config.test_ref ||
223 if (stencil_test_changed || config.test_func != prev_config.test_func || 215 config.test_mask != prev_config.test_mask) {
224 config.test_ref != prev_config.test_ref || 216 glStencilFuncSeparate(face, config.test_func, config.test_ref, config.test_mask);
225 config.test_mask != prev_config.test_mask) { 217 }
226 glStencilFuncSeparate(face, config.test_func, config.test_ref, config.test_mask); 218 if (config.action_depth_fail != prev_config.action_depth_fail ||
227 } 219 config.action_depth_pass != prev_config.action_depth_pass ||
228 if (stencil_test_changed || config.action_depth_fail != prev_config.action_depth_fail || 220 config.action_stencil_fail != prev_config.action_stencil_fail) {
229 config.action_depth_pass != prev_config.action_depth_pass || 221 glStencilOpSeparate(face, config.action_stencil_fail, config.action_depth_fail,
230 config.action_stencil_fail != prev_config.action_stencil_fail) { 222 config.action_depth_pass);
231 glStencilOpSeparate(face, config.action_stencil_fail, config.action_depth_fail, 223 }
232 config.action_depth_pass); 224 if (config.write_mask != prev_config.write_mask) {
233 } 225 glStencilMaskSeparate(face, config.write_mask);
234 if (config.write_mask != prev_config.write_mask) { 226 }
235 glStencilMaskSeparate(face, config.write_mask); 227 };
236 } 228 ConfigStencil(GL_FRONT, stencil.front, cur_state.stencil.front);
237 }; 229 ConfigStencil(GL_BACK, stencil.back, cur_state.stencil.back);
238 config_stencil(GL_FRONT, stencil.front, cur_state.stencil.front);
239 config_stencil(GL_BACK, stencil.back, cur_state.stencil.back);
240 }
241} 230}
242// Viewport does not affects glClearBuffer so emulate viewport using scissor test 231// Viewport does not affects glClearBuffer so emulate viewport using scissor test
243void OpenGLState::EmulateViewportWithScissor() { 232void OpenGLState::EmulateViewportWithScissor() {
@@ -278,19 +267,18 @@ void OpenGLState::ApplyViewport() const {
278 updated.depth_range_far != current.depth_range_far) { 267 updated.depth_range_far != current.depth_range_far) {
279 glDepthRangeIndexed(i, updated.depth_range_near, updated.depth_range_far); 268 glDepthRangeIndexed(i, updated.depth_range_near, updated.depth_range_far);
280 } 269 }
281 const bool scissor_changed = updated.scissor.enabled != current.scissor.enabled; 270
282 if (scissor_changed) { 271 if (updated.scissor.enabled != current.scissor.enabled) {
283 if (updated.scissor.enabled) { 272 if (updated.scissor.enabled) {
284 glEnablei(GL_SCISSOR_TEST, i); 273 glEnablei(GL_SCISSOR_TEST, i);
285 } else { 274 } else {
286 glDisablei(GL_SCISSOR_TEST, i); 275 glDisablei(GL_SCISSOR_TEST, i);
287 } 276 }
288 } 277 }
289 if (updated.scissor.enabled && 278
290 (scissor_changed || updated.scissor.x != current.scissor.x || 279 if (updated.scissor.x != current.scissor.x || updated.scissor.y != current.scissor.y ||
291 updated.scissor.y != current.scissor.y || 280 updated.scissor.width != current.scissor.width ||
292 updated.scissor.width != current.scissor.width || 281 updated.scissor.height != current.scissor.height) {
293 updated.scissor.height != current.scissor.height)) {
294 glScissorIndexed(i, updated.scissor.x, updated.scissor.y, updated.scissor.width, 282 glScissorIndexed(i, updated.scissor.x, updated.scissor.y, updated.scissor.width,
295 updated.scissor.height); 283 updated.scissor.height);
296 } 284 }
@@ -302,22 +290,23 @@ void OpenGLState::ApplyViewport() const {
302 updated.height != current.height) { 290 updated.height != current.height) {
303 glViewport(updated.x, updated.y, updated.width, updated.height); 291 glViewport(updated.x, updated.y, updated.width, updated.height);
304 } 292 }
293
305 if (updated.depth_range_near != current.depth_range_near || 294 if (updated.depth_range_near != current.depth_range_near ||
306 updated.depth_range_far != current.depth_range_far) { 295 updated.depth_range_far != current.depth_range_far) {
307 glDepthRange(updated.depth_range_near, updated.depth_range_far); 296 glDepthRange(updated.depth_range_near, updated.depth_range_far);
308 } 297 }
309 const bool scissor_changed = updated.scissor.enabled != current.scissor.enabled; 298
310 if (scissor_changed) { 299 if (updated.scissor.enabled != current.scissor.enabled) {
311 if (updated.scissor.enabled) { 300 if (updated.scissor.enabled) {
312 glEnable(GL_SCISSOR_TEST); 301 glEnable(GL_SCISSOR_TEST);
313 } else { 302 } else {
314 glDisable(GL_SCISSOR_TEST); 303 glDisable(GL_SCISSOR_TEST);
315 } 304 }
316 } 305 }
317 if (updated.scissor.enabled && (scissor_changed || updated.scissor.x != current.scissor.x || 306
318 updated.scissor.y != current.scissor.y || 307 if (updated.scissor.x != current.scissor.x || updated.scissor.y != current.scissor.y ||
319 updated.scissor.width != current.scissor.width || 308 updated.scissor.width != current.scissor.width ||
320 updated.scissor.height != current.scissor.height)) { 309 updated.scissor.height != current.scissor.height) {
321 glScissor(updated.scissor.x, updated.scissor.y, updated.scissor.width, 310 glScissor(updated.scissor.x, updated.scissor.y, updated.scissor.width,
322 updated.scissor.height); 311 updated.scissor.height);
323 } 312 }
@@ -327,8 +316,7 @@ void OpenGLState::ApplyViewport() const {
327void OpenGLState::ApplyGlobalBlending() const { 316void OpenGLState::ApplyGlobalBlending() const {
328 const Blend& current = cur_state.blend[0]; 317 const Blend& current = cur_state.blend[0];
329 const Blend& updated = blend[0]; 318 const Blend& updated = blend[0];
330 const bool blend_changed = updated.enabled != current.enabled; 319 if (updated.enabled != current.enabled) {
331 if (blend_changed) {
332 if (updated.enabled) { 320 if (updated.enabled) {
333 glEnable(GL_BLEND); 321 glEnable(GL_BLEND);
334 } else { 322 } else {
@@ -338,15 +326,14 @@ void OpenGLState::ApplyGlobalBlending() const {
338 if (!updated.enabled) { 326 if (!updated.enabled) {
339 return; 327 return;
340 } 328 }
341 if (blend_changed || updated.src_rgb_func != current.src_rgb_func || 329 if (updated.src_rgb_func != current.src_rgb_func ||
342 updated.dst_rgb_func != current.dst_rgb_func || updated.src_a_func != current.src_a_func || 330 updated.dst_rgb_func != current.dst_rgb_func || updated.src_a_func != current.src_a_func ||
343 updated.dst_a_func != current.dst_a_func) { 331 updated.dst_a_func != current.dst_a_func) {
344 glBlendFuncSeparate(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func, 332 glBlendFuncSeparate(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func,
345 updated.dst_a_func); 333 updated.dst_a_func);
346 } 334 }
347 335
348 if (blend_changed || updated.rgb_equation != current.rgb_equation || 336 if (updated.rgb_equation != current.rgb_equation || updated.a_equation != current.a_equation) {
349 updated.a_equation != current.a_equation) {
350 glBlendEquationSeparate(updated.rgb_equation, updated.a_equation); 337 glBlendEquationSeparate(updated.rgb_equation, updated.a_equation);
351 } 338 }
352} 339}
@@ -354,26 +341,22 @@ void OpenGLState::ApplyGlobalBlending() const {
354void OpenGLState::ApplyTargetBlending(std::size_t target, bool force) const { 341void OpenGLState::ApplyTargetBlending(std::size_t target, bool force) const {
355 const Blend& updated = blend[target]; 342 const Blend& updated = blend[target];
356 const Blend& current = cur_state.blend[target]; 343 const Blend& current = cur_state.blend[target];
357 const bool blend_changed = updated.enabled != current.enabled || force; 344 if (updated.enabled != current.enabled || force) {
358 if (blend_changed) {
359 if (updated.enabled) { 345 if (updated.enabled) {
360 glEnablei(GL_BLEND, static_cast<GLuint>(target)); 346 glEnablei(GL_BLEND, static_cast<GLuint>(target));
361 } else { 347 } else {
362 glDisablei(GL_BLEND, static_cast<GLuint>(target)); 348 glDisablei(GL_BLEND, static_cast<GLuint>(target));
363 } 349 }
364 } 350 }
365 if (!updated.enabled) { 351
366 return; 352 if (updated.src_rgb_func != current.src_rgb_func ||
367 }
368 if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
369 updated.dst_rgb_func != current.dst_rgb_func || updated.src_a_func != current.src_a_func || 353 updated.dst_rgb_func != current.dst_rgb_func || updated.src_a_func != current.src_a_func ||
370 updated.dst_a_func != current.dst_a_func) { 354 updated.dst_a_func != current.dst_a_func) {
371 glBlendFuncSeparatei(static_cast<GLuint>(target), updated.src_rgb_func, 355 glBlendFuncSeparatei(static_cast<GLuint>(target), updated.src_rgb_func,
372 updated.dst_rgb_func, updated.src_a_func, updated.dst_a_func); 356 updated.dst_rgb_func, updated.src_a_func, updated.dst_a_func);
373 } 357 }
374 358
375 if (blend_changed || updated.rgb_equation != current.rgb_equation || 359 if (updated.rgb_equation != current.rgb_equation || updated.a_equation != current.a_equation) {
376 updated.a_equation != current.a_equation) {
377 glBlendEquationSeparatei(static_cast<GLuint>(target), updated.rgb_equation, 360 glBlendEquationSeparatei(static_cast<GLuint>(target), updated.rgb_equation,
378 updated.a_equation); 361 updated.a_equation);
379 } 362 }
@@ -397,8 +380,7 @@ void OpenGLState::ApplyBlending() const {
397} 380}
398 381
399void OpenGLState::ApplyLogicOp() const { 382void OpenGLState::ApplyLogicOp() const {
400 const bool logic_op_changed = logic_op.enabled != cur_state.logic_op.enabled; 383 if (logic_op.enabled != cur_state.logic_op.enabled) {
401 if (logic_op_changed) {
402 if (logic_op.enabled) { 384 if (logic_op.enabled) {
403 glEnable(GL_COLOR_LOGIC_OP); 385 glEnable(GL_COLOR_LOGIC_OP);
404 } else { 386 } else {
@@ -406,14 +388,12 @@ void OpenGLState::ApplyLogicOp() const {
406 } 388 }
407 } 389 }
408 390
409 if (logic_op.enabled && 391 if (logic_op.operation != cur_state.logic_op.operation) {
410 (logic_op_changed || logic_op.operation != cur_state.logic_op.operation)) {
411 glLogicOp(logic_op.operation); 392 glLogicOp(logic_op.operation);
412 } 393 }
413} 394}
414 395
415void OpenGLState::ApplyPolygonOffset() const { 396void OpenGLState::ApplyPolygonOffset() const {
416
417 const bool fill_enable_changed = 397 const bool fill_enable_changed =
418 polygon_offset.fill_enable != cur_state.polygon_offset.fill_enable; 398 polygon_offset.fill_enable != cur_state.polygon_offset.fill_enable;
419 const bool line_enable_changed = 399 const bool line_enable_changed =
@@ -448,9 +428,7 @@ void OpenGLState::ApplyPolygonOffset() const {
448 } 428 }
449 } 429 }
450 430
451 if ((polygon_offset.fill_enable || polygon_offset.line_enable || polygon_offset.point_enable) && 431 if (factor_changed || units_changed || clamp_changed) {
452 (factor_changed || units_changed || clamp_changed)) {
453
454 if (GLAD_GL_EXT_polygon_offset_clamp && polygon_offset.clamp != 0) { 432 if (GLAD_GL_EXT_polygon_offset_clamp && polygon_offset.clamp != 0) {
455 glPolygonOffsetClamp(polygon_offset.factor, polygon_offset.units, polygon_offset.clamp); 433 glPolygonOffsetClamp(polygon_offset.factor, polygon_offset.units, polygon_offset.clamp);
456 } else { 434 } else {
@@ -528,9 +506,9 @@ void OpenGLState::ApplyDepthClamp() const {
528 depth_clamp.near_plane == cur_state.depth_clamp.near_plane) { 506 depth_clamp.near_plane == cur_state.depth_clamp.near_plane) {
529 return; 507 return;
530 } 508 }
531 if (depth_clamp.far_plane != depth_clamp.near_plane) { 509 UNIMPLEMENTED_IF_MSG(depth_clamp.far_plane != depth_clamp.near_plane,
532 UNIMPLEMENTED_MSG("Unimplemented Depth Clamp Separation!"); 510 "Unimplemented Depth Clamp Separation!");
533 } 511
534 if (depth_clamp.far_plane || depth_clamp.near_plane) { 512 if (depth_clamp.far_plane || depth_clamp.near_plane) {
535 glEnable(GL_DEPTH_CLAMP); 513 glEnable(GL_DEPTH_CLAMP);
536 } else { 514 } else {
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index cca2ed708..272fc2e8e 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -137,7 +137,7 @@ void RendererOpenGL::SwapBuffers(
137 137
138 render_window.PollEvents(); 138 render_window.PollEvents();
139 139
140 system.FrameLimiter().DoFrameLimiting(Core::Timing::GetGlobalTimeUs()); 140 system.FrameLimiter().DoFrameLimiting(system.CoreTiming().GetGlobalTimeUs());
141 system.GetPerfStats().BeginSystemFrame(); 141 system.GetPerfStats().BeginSystemFrame();
142 142
143 // Restore the rasterizer state 143 // Restore the rasterizer state
@@ -380,7 +380,8 @@ void RendererOpenGL::CaptureScreenshot() {
380 GLuint renderbuffer; 380 GLuint renderbuffer;
381 glGenRenderbuffers(1, &renderbuffer); 381 glGenRenderbuffers(1, &renderbuffer);
382 glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); 382 glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
383 glRenderbufferStorage(GL_RENDERBUFFER, GL_RGB8, layout.width, layout.height); 383 glRenderbufferStorage(GL_RENDERBUFFER, state.GetsRGBUsed() ? GL_SRGB8 : GL_RGB8, layout.width,
384 layout.height);
384 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer); 385 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
385 386
386 DrawScreen(layout); 387 DrawScreen(layout);
diff --git a/src/video_core/renderer_vulkan/declarations.h b/src/video_core/renderer_vulkan/declarations.h
new file mode 100644
index 000000000..ba25b5bc7
--- /dev/null
+++ b/src/video_core/renderer_vulkan/declarations.h
@@ -0,0 +1,45 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <vulkan/vulkan.hpp>
8
9namespace Vulkan {
10
11// vulkan.hpp unique handlers use DispatchLoaderStatic
12template <typename T>
13using UniqueHandle = vk::UniqueHandle<T, vk::DispatchLoaderDynamic>;
14
15using UniqueAccelerationStructureNV = UniqueHandle<vk::AccelerationStructureNV>;
16using UniqueBuffer = UniqueHandle<vk::Buffer>;
17using UniqueBufferView = UniqueHandle<vk::BufferView>;
18using UniqueCommandBuffer = UniqueHandle<vk::CommandBuffer>;
19using UniqueCommandPool = UniqueHandle<vk::CommandPool>;
20using UniqueDescriptorPool = UniqueHandle<vk::DescriptorPool>;
21using UniqueDescriptorSet = UniqueHandle<vk::DescriptorSet>;
22using UniqueDescriptorSetLayout = UniqueHandle<vk::DescriptorSetLayout>;
23using UniqueDescriptorUpdateTemplate = UniqueHandle<vk::DescriptorUpdateTemplate>;
24using UniqueDevice = UniqueHandle<vk::Device>;
25using UniqueDeviceMemory = UniqueHandle<vk::DeviceMemory>;
26using UniqueEvent = UniqueHandle<vk::Event>;
27using UniqueFence = UniqueHandle<vk::Fence>;
28using UniqueFramebuffer = UniqueHandle<vk::Framebuffer>;
29using UniqueImage = UniqueHandle<vk::Image>;
30using UniqueImageView = UniqueHandle<vk::ImageView>;
31using UniqueIndirectCommandsLayoutNVX = UniqueHandle<vk::IndirectCommandsLayoutNVX>;
32using UniqueObjectTableNVX = UniqueHandle<vk::ObjectTableNVX>;
33using UniquePipeline = UniqueHandle<vk::Pipeline>;
34using UniquePipelineCache = UniqueHandle<vk::PipelineCache>;
35using UniquePipelineLayout = UniqueHandle<vk::PipelineLayout>;
36using UniqueQueryPool = UniqueHandle<vk::QueryPool>;
37using UniqueRenderPass = UniqueHandle<vk::RenderPass>;
38using UniqueSampler = UniqueHandle<vk::Sampler>;
39using UniqueSamplerYcbcrConversion = UniqueHandle<vk::SamplerYcbcrConversion>;
40using UniqueSemaphore = UniqueHandle<vk::Semaphore>;
41using UniqueShaderModule = UniqueHandle<vk::ShaderModule>;
42using UniqueSwapchainKHR = UniqueHandle<vk::SwapchainKHR>;
43using UniqueValidationCacheEXT = UniqueHandle<vk::ValidationCacheEXT>;
44
45} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp
new file mode 100644
index 000000000..78a4e5f0e
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_device.cpp
@@ -0,0 +1,231 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <map>
6#include <optional>
7#include <set>
8#include <vector>
9#include "common/assert.h"
10#include "video_core/renderer_vulkan/declarations.h"
11#include "video_core/renderer_vulkan/vk_device.h"
12
13namespace Vulkan {
14
15namespace Alternatives {
16
17constexpr std::array<vk::Format, 3> Depth24UnormS8Uint = {
18 vk::Format::eD32SfloatS8Uint, vk::Format::eD16UnormS8Uint, {}};
19constexpr std::array<vk::Format, 3> Depth16UnormS8Uint = {
20 vk::Format::eD24UnormS8Uint, vk::Format::eD32SfloatS8Uint, {}};
21
22} // namespace Alternatives
23
24constexpr const vk::Format* GetFormatAlternatives(vk::Format format) {
25 switch (format) {
26 case vk::Format::eD24UnormS8Uint:
27 return Alternatives::Depth24UnormS8Uint.data();
28 case vk::Format::eD16UnormS8Uint:
29 return Alternatives::Depth16UnormS8Uint.data();
30 default:
31 return nullptr;
32 }
33}
34
35constexpr vk::FormatFeatureFlags GetFormatFeatures(vk::FormatProperties properties,
36 FormatType format_type) {
37 switch (format_type) {
38 case FormatType::Linear:
39 return properties.linearTilingFeatures;
40 case FormatType::Optimal:
41 return properties.optimalTilingFeatures;
42 case FormatType::Buffer:
43 return properties.bufferFeatures;
44 default:
45 return {};
46 }
47}
48
49VKDevice::VKDevice(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical,
50 vk::SurfaceKHR surface)
51 : physical{physical}, format_properties{GetFormatProperties(dldi, physical)} {
52 SetupFamilies(dldi, surface);
53 SetupProperties(dldi);
54}
55
56VKDevice::~VKDevice() = default;
57
58bool VKDevice::Create(const vk::DispatchLoaderDynamic& dldi, vk::Instance instance) {
59 const auto queue_cis = GetDeviceQueueCreateInfos();
60 vk::PhysicalDeviceFeatures device_features{};
61
62 const std::vector<const char*> extensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
63 const vk::DeviceCreateInfo device_ci({}, static_cast<u32>(queue_cis.size()), queue_cis.data(),
64 0, nullptr, static_cast<u32>(extensions.size()),
65 extensions.data(), &device_features);
66 vk::Device dummy_logical;
67 if (physical.createDevice(&device_ci, nullptr, &dummy_logical, dldi) != vk::Result::eSuccess) {
68 LOG_CRITICAL(Render_Vulkan, "Logical device failed to be created!");
69 return false;
70 }
71
72 dld.init(instance, dldi.vkGetInstanceProcAddr, dummy_logical, dldi.vkGetDeviceProcAddr);
73 logical = UniqueDevice(
74 dummy_logical, vk::ObjectDestroy<vk::NoParent, vk::DispatchLoaderDynamic>(nullptr, dld));
75
76 graphics_queue = logical->getQueue(graphics_family, 0, dld);
77 present_queue = logical->getQueue(present_family, 0, dld);
78 return true;
79}
80
81vk::Format VKDevice::GetSupportedFormat(vk::Format wanted_format,
82 vk::FormatFeatureFlags wanted_usage,
83 FormatType format_type) const {
84 if (IsFormatSupported(wanted_format, wanted_usage, format_type)) {
85 return wanted_format;
86 }
87 // The wanted format is not supported by hardware, search for alternatives
88 const vk::Format* alternatives = GetFormatAlternatives(wanted_format);
89 if (alternatives == nullptr) {
90 LOG_CRITICAL(Render_Vulkan,
91 "Format={} with usage={} and type={} has no defined alternatives and host "
92 "hardware does not support it",
93 static_cast<u32>(wanted_format), static_cast<u32>(wanted_usage),
94 static_cast<u32>(format_type));
95 UNREACHABLE();
96 return wanted_format;
97 }
98
99 std::size_t i = 0;
100 for (vk::Format alternative = alternatives[0]; alternative != vk::Format{};
101 alternative = alternatives[++i]) {
102 if (!IsFormatSupported(alternative, wanted_usage, format_type))
103 continue;
104 LOG_WARNING(Render_Vulkan,
105 "Emulating format={} with alternative format={} with usage={} and type={}",
106 static_cast<u32>(wanted_format), static_cast<u32>(alternative),
107 static_cast<u32>(wanted_usage), static_cast<u32>(format_type));
108 return alternative;
109 }
110
111 // No alternatives found, panic
112 LOG_CRITICAL(Render_Vulkan,
113 "Format={} with usage={} and type={} is not supported by the host hardware and "
114 "doesn't support any of the alternatives",
115 static_cast<u32>(wanted_format), static_cast<u32>(wanted_usage),
116 static_cast<u32>(format_type));
117 UNREACHABLE();
118 return wanted_format;
119}
120
121bool VKDevice::IsFormatSupported(vk::Format wanted_format, vk::FormatFeatureFlags wanted_usage,
122 FormatType format_type) const {
123 const auto it = format_properties.find(wanted_format);
124 if (it == format_properties.end()) {
125 LOG_CRITICAL(Render_Vulkan, "Unimplemented format query={}",
126 static_cast<u32>(wanted_format));
127 UNREACHABLE();
128 return true;
129 }
130 const vk::FormatFeatureFlags supported_usage = GetFormatFeatures(it->second, format_type);
131 return (supported_usage & wanted_usage) == wanted_usage;
132}
133
134bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical,
135 vk::SurfaceKHR surface) {
136 const std::string swapchain_extension = VK_KHR_SWAPCHAIN_EXTENSION_NAME;
137
138 bool has_swapchain{};
139 for (const auto& prop : physical.enumerateDeviceExtensionProperties(nullptr, dldi)) {
140 has_swapchain |= prop.extensionName == swapchain_extension;
141 }
142 if (!has_swapchain) {
143 // The device doesn't support creating swapchains.
144 return false;
145 }
146
147 bool has_graphics{}, has_present{};
148 const auto queue_family_properties = physical.getQueueFamilyProperties(dldi);
149 for (u32 i = 0; i < static_cast<u32>(queue_family_properties.size()); ++i) {
150 const auto& family = queue_family_properties[i];
151 if (family.queueCount == 0)
152 continue;
153
154 has_graphics |=
155 (family.queueFlags & vk::QueueFlagBits::eGraphics) != static_cast<vk::QueueFlagBits>(0);
156 has_present |= physical.getSurfaceSupportKHR(i, surface, dldi) != 0;
157 }
158 if (!has_graphics || !has_present) {
159 // The device doesn't have a graphics and present queue.
160 return false;
161 }
162
163 // TODO(Rodrigo): Check if the device matches all requeriments.
164 const vk::PhysicalDeviceProperties props = physical.getProperties(dldi);
165 if (props.limits.maxUniformBufferRange < 65536) {
166 return false;
167 }
168
169 // Device is suitable.
170 return true;
171}
172
173void VKDevice::SetupFamilies(const vk::DispatchLoaderDynamic& dldi, vk::SurfaceKHR surface) {
174 std::optional<u32> graphics_family_, present_family_;
175
176 const auto queue_family_properties = physical.getQueueFamilyProperties(dldi);
177 for (u32 i = 0; i < static_cast<u32>(queue_family_properties.size()); ++i) {
178 if (graphics_family_ && present_family_)
179 break;
180
181 const auto& queue_family = queue_family_properties[i];
182 if (queue_family.queueCount == 0)
183 continue;
184
185 if (queue_family.queueFlags & vk::QueueFlagBits::eGraphics)
186 graphics_family_ = i;
187 if (physical.getSurfaceSupportKHR(i, surface, dldi))
188 present_family_ = i;
189 }
190 ASSERT(graphics_family_ && present_family_);
191
192 graphics_family = *graphics_family_;
193 present_family = *present_family_;
194}
195
196void VKDevice::SetupProperties(const vk::DispatchLoaderDynamic& dldi) {
197 const vk::PhysicalDeviceProperties props = physical.getProperties(dldi);
198 device_type = props.deviceType;
199 uniform_buffer_alignment = static_cast<u64>(props.limits.minUniformBufferOffsetAlignment);
200}
201
202std::vector<vk::DeviceQueueCreateInfo> VKDevice::GetDeviceQueueCreateInfos() const {
203 static const float QUEUE_PRIORITY = 1.f;
204
205 std::set<u32> unique_queue_families = {graphics_family, present_family};
206 std::vector<vk::DeviceQueueCreateInfo> queue_cis;
207
208 for (u32 queue_family : unique_queue_families)
209 queue_cis.push_back({{}, queue_family, 1, &QUEUE_PRIORITY});
210
211 return queue_cis;
212}
213
214std::map<vk::Format, vk::FormatProperties> VKDevice::GetFormatProperties(
215 const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical) {
216 std::map<vk::Format, vk::FormatProperties> format_properties;
217
218 const auto AddFormatQuery = [&format_properties, &dldi, physical](vk::Format format) {
219 format_properties.emplace(format, physical.getFormatProperties(format, dldi));
220 };
221 AddFormatQuery(vk::Format::eA8B8G8R8UnormPack32);
222 AddFormatQuery(vk::Format::eR5G6B5UnormPack16);
223 AddFormatQuery(vk::Format::eD32Sfloat);
224 AddFormatQuery(vk::Format::eD16UnormS8Uint);
225 AddFormatQuery(vk::Format::eD24UnormS8Uint);
226 AddFormatQuery(vk::Format::eD32SfloatS8Uint);
227
228 return format_properties;
229}
230
231} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_device.h b/src/video_core/renderer_vulkan/vk_device.h
new file mode 100644
index 000000000..e87c7a508
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_device.h
@@ -0,0 +1,116 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <map>
8#include <vector>
9#include "common/common_types.h"
10#include "video_core/renderer_vulkan/declarations.h"
11
12namespace Vulkan {
13
14/// Format usage descriptor
15enum class FormatType { Linear, Optimal, Buffer };
16
17/// Handles data specific to a physical device.
18class VKDevice final {
19public:
20 explicit VKDevice(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical,
21 vk::SurfaceKHR surface);
22 ~VKDevice();
23
24 /// Initializes the device. Returns true on success.
25 bool Create(const vk::DispatchLoaderDynamic& dldi, vk::Instance instance);
26
27 /**
28 * Returns a format supported by the device for the passed requeriments.
29 * @param wanted_format The ideal format to be returned. It may not be the returned format.
30 * @param wanted_usage The usage that must be fulfilled even if the format is not supported.
31 * @param format_type Format type usage.
32 * @returns A format supported by the device.
33 */
34 vk::Format GetSupportedFormat(vk::Format wanted_format, vk::FormatFeatureFlags wanted_usage,
35 FormatType format_type) const;
36
37 /// Returns the dispatch loader with direct function pointers of the device
38 const vk::DispatchLoaderDynamic& GetDispatchLoader() const {
39 return dld;
40 }
41
42 /// Returns the logical device
43 vk::Device GetLogical() const {
44 return logical.get();
45 }
46
47 /// Returns the physical device.
48 vk::PhysicalDevice GetPhysical() const {
49 return physical;
50 }
51
52 /// Returns the main graphics queue.
53 vk::Queue GetGraphicsQueue() const {
54 return graphics_queue;
55 }
56
57 /// Returns the main present queue.
58 vk::Queue GetPresentQueue() const {
59 return present_queue;
60 }
61
62 /// Returns main graphics queue family index.
63 u32 GetGraphicsFamily() const {
64 return graphics_family;
65 }
66
67 /// Returns main present queue family index.
68 u32 GetPresentFamily() const {
69 return present_family;
70 }
71
72 /// Returns if the device is integrated with the host CPU
73 bool IsIntegrated() const {
74 return device_type == vk::PhysicalDeviceType::eIntegratedGpu;
75 }
76
77 /// Returns uniform buffer alignment requeriment
78 u64 GetUniformBufferAlignment() const {
79 return uniform_buffer_alignment;
80 }
81
82 /// Checks if the physical device is suitable.
83 static bool IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical,
84 vk::SurfaceKHR surface);
85
86private:
87 /// Sets up queue families.
88 void SetupFamilies(const vk::DispatchLoaderDynamic& dldi, vk::SurfaceKHR surface);
89
90 /// Sets up device properties.
91 void SetupProperties(const vk::DispatchLoaderDynamic& dldi);
92
93 /// Returns a list of queue initialization descriptors.
94 std::vector<vk::DeviceQueueCreateInfo> GetDeviceQueueCreateInfos() const;
95
96 /// Returns true if a format is supported.
97 bool IsFormatSupported(vk::Format wanted_format, vk::FormatFeatureFlags wanted_usage,
98 FormatType format_type) const;
99
100 /// Returns the device properties for Vulkan formats.
101 static std::map<vk::Format, vk::FormatProperties> GetFormatProperties(
102 const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical);
103
104 const vk::PhysicalDevice physical; ///< Physical device
105 vk::DispatchLoaderDynamic dld; ///< Device function pointers
106 UniqueDevice logical; ///< Logical device
107 vk::Queue graphics_queue; ///< Main graphics queue
108 vk::Queue present_queue; ///< Main present queue
109 u32 graphics_family{}; ///< Main graphics queue family index
110 u32 present_family{}; ///< Main present queue family index
111 vk::PhysicalDeviceType device_type; ///< Physical device type
112 u64 uniform_buffer_alignment{}; ///< Uniform buffer alignment requeriment
113 std::map<vk::Format, vk::FormatProperties> format_properties; ///< Format properties dictionary
114};
115
116} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_memory_manager.cpp b/src/video_core/renderer_vulkan/vk_memory_manager.cpp
new file mode 100644
index 000000000..17ee93b91
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_memory_manager.cpp
@@ -0,0 +1,252 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <optional>
7#include <tuple>
8#include <vector>
9#include "common/alignment.h"
10#include "common/assert.h"
11#include "common/common_types.h"
12#include "common/logging/log.h"
13#include "video_core/renderer_vulkan/declarations.h"
14#include "video_core/renderer_vulkan/vk_device.h"
15#include "video_core/renderer_vulkan/vk_memory_manager.h"
16
17namespace Vulkan {
18
19// TODO(Rodrigo): Fine tune this number
20constexpr u64 ALLOC_CHUNK_SIZE = 64 * 1024 * 1024;
21
22class VKMemoryAllocation final {
23public:
24 explicit VKMemoryAllocation(const VKDevice& device, vk::DeviceMemory memory,
25 vk::MemoryPropertyFlags properties, u64 alloc_size, u32 type)
26 : device{device}, memory{memory}, properties{properties}, alloc_size{alloc_size},
27 shifted_type{ShiftType(type)}, is_mappable{properties &
28 vk::MemoryPropertyFlagBits::eHostVisible} {
29 if (is_mappable) {
30 const auto dev = device.GetLogical();
31 const auto& dld = device.GetDispatchLoader();
32 base_address = static_cast<u8*>(dev.mapMemory(memory, 0, alloc_size, {}, dld));
33 }
34 }
35
36 ~VKMemoryAllocation() {
37 const auto dev = device.GetLogical();
38 const auto& dld = device.GetDispatchLoader();
39 if (is_mappable)
40 dev.unmapMemory(memory, dld);
41 dev.free(memory, nullptr, dld);
42 }
43
44 VKMemoryCommit Commit(vk::DeviceSize commit_size, vk::DeviceSize alignment) {
45 auto found = TryFindFreeSection(free_iterator, alloc_size, static_cast<u64>(commit_size),
46 static_cast<u64>(alignment));
47 if (!found) {
48 found = TryFindFreeSection(0, free_iterator, static_cast<u64>(commit_size),
49 static_cast<u64>(alignment));
50 if (!found) {
51 // Signal out of memory, it'll try to do more allocations.
52 return nullptr;
53 }
54 }
55 u8* address = is_mappable ? base_address + *found : nullptr;
56 auto commit = std::make_unique<VKMemoryCommitImpl>(this, memory, address, *found,
57 *found + commit_size);
58 commits.push_back(commit.get());
59
60 // Last commit's address is highly probable to be free.
61 free_iterator = *found + commit_size;
62
63 return commit;
64 }
65
66 void Free(const VKMemoryCommitImpl* commit) {
67 ASSERT(commit);
68 const auto it =
69 std::find_if(commits.begin(), commits.end(),
70 [&](const auto& stored_commit) { return stored_commit == commit; });
71 if (it == commits.end()) {
72 LOG_CRITICAL(Render_Vulkan, "Freeing unallocated commit!");
73 UNREACHABLE();
74 return;
75 }
76 commits.erase(it);
77 }
78
79 /// Returns whether this allocation is compatible with the arguments.
80 bool IsCompatible(vk::MemoryPropertyFlags wanted_properties, u32 type_mask) const {
81 return (wanted_properties & properties) != vk::MemoryPropertyFlagBits(0) &&
82 (type_mask & shifted_type) != 0;
83 }
84
85private:
86 static constexpr u32 ShiftType(u32 type) {
87 return 1U << type;
88 }
89
90 /// A memory allocator, it may return a free region between "start" and "end" with the solicited
91 /// requeriments.
92 std::optional<u64> TryFindFreeSection(u64 start, u64 end, u64 size, u64 alignment) const {
93 u64 iterator = start;
94 while (iterator + size < end) {
95 const u64 try_left = Common::AlignUp(iterator, alignment);
96 const u64 try_right = try_left + size;
97
98 bool overlap = false;
99 for (const auto& commit : commits) {
100 const auto [commit_left, commit_right] = commit->interval;
101 if (try_left < commit_right && commit_left < try_right) {
102 // There's an overlap, continue the search where the overlapping commit ends.
103 iterator = commit_right;
104 overlap = true;
105 break;
106 }
107 }
108 if (!overlap) {
109 // A free address has been found.
110 return try_left;
111 }
112 }
113 // No free regions where found, return an empty optional.
114 return std::nullopt;
115 }
116
117 const VKDevice& device; ///< Vulkan device.
118 const vk::DeviceMemory memory; ///< Vulkan memory allocation handler.
119 const vk::MemoryPropertyFlags properties; ///< Vulkan properties.
120 const u64 alloc_size; ///< Size of this allocation.
121 const u32 shifted_type; ///< Stored Vulkan type of this allocation, shifted.
122 const bool is_mappable; ///< Whether the allocation is mappable.
123
124 /// Base address of the mapped pointer.
125 u8* base_address{};
126
127 /// Hints where the next free region is likely going to be.
128 u64 free_iterator{};
129
130 /// Stores all commits done from this allocation.
131 std::vector<const VKMemoryCommitImpl*> commits;
132};
133
134VKMemoryManager::VKMemoryManager(const VKDevice& device)
135 : device{device}, props{device.GetPhysical().getMemoryProperties(device.GetDispatchLoader())},
136 is_memory_unified{GetMemoryUnified(props)} {}
137
138VKMemoryManager::~VKMemoryManager() = default;
139
140VKMemoryCommit VKMemoryManager::Commit(const vk::MemoryRequirements& reqs, bool host_visible) {
141 ASSERT(reqs.size < ALLOC_CHUNK_SIZE);
142
143 // When a host visible commit is asked, search for host visible and coherent, otherwise search
144 // for a fast device local type.
145 const vk::MemoryPropertyFlags wanted_properties =
146 host_visible
147 ? vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent
148 : vk::MemoryPropertyFlagBits::eDeviceLocal;
149
150 const auto TryCommit = [&]() -> VKMemoryCommit {
151 for (auto& alloc : allocs) {
152 if (!alloc->IsCompatible(wanted_properties, reqs.memoryTypeBits))
153 continue;
154
155 if (auto commit = alloc->Commit(reqs.size, reqs.alignment); commit) {
156 return commit;
157 }
158 }
159 return {};
160 };
161
162 if (auto commit = TryCommit(); commit) {
163 return commit;
164 }
165
166 // Commit has failed, allocate more memory.
167 if (!AllocMemory(wanted_properties, reqs.memoryTypeBits, ALLOC_CHUNK_SIZE)) {
168 // TODO(Rodrigo): Try to use host memory.
169 LOG_CRITICAL(Render_Vulkan, "Ran out of memory!");
170 UNREACHABLE();
171 }
172
173 // Commit again, this time it won't fail since there's a fresh allocation above. If it does,
174 // there's a bug.
175 auto commit = TryCommit();
176 ASSERT(commit);
177 return commit;
178}
179
180VKMemoryCommit VKMemoryManager::Commit(vk::Buffer buffer, bool host_visible) {
181 const auto dev = device.GetLogical();
182 const auto& dld = device.GetDispatchLoader();
183 const auto requeriments = dev.getBufferMemoryRequirements(buffer, dld);
184 auto commit = Commit(requeriments, host_visible);
185 dev.bindBufferMemory(buffer, commit->GetMemory(), commit->GetOffset(), dld);
186 return commit;
187}
188
189VKMemoryCommit VKMemoryManager::Commit(vk::Image image, bool host_visible) {
190 const auto dev = device.GetLogical();
191 const auto& dld = device.GetDispatchLoader();
192 const auto requeriments = dev.getImageMemoryRequirements(image, dld);
193 auto commit = Commit(requeriments, host_visible);
194 dev.bindImageMemory(image, commit->GetMemory(), commit->GetOffset(), dld);
195 return commit;
196}
197
198bool VKMemoryManager::AllocMemory(vk::MemoryPropertyFlags wanted_properties, u32 type_mask,
199 u64 size) {
200 const u32 type = [&]() {
201 for (u32 type_index = 0; type_index < props.memoryTypeCount; ++type_index) {
202 const auto flags = props.memoryTypes[type_index].propertyFlags;
203 if ((type_mask & (1U << type_index)) && (flags & wanted_properties)) {
204 // The type matches in type and in the wanted properties.
205 return type_index;
206 }
207 }
208 LOG_CRITICAL(Render_Vulkan, "Couldn't find a compatible memory type!");
209 UNREACHABLE();
210 return 0u;
211 }();
212
213 const auto dev = device.GetLogical();
214 const auto& dld = device.GetDispatchLoader();
215
216 // Try to allocate found type.
217 const vk::MemoryAllocateInfo memory_ai(size, type);
218 vk::DeviceMemory memory;
219 if (const vk::Result res = dev.allocateMemory(&memory_ai, nullptr, &memory, dld);
220 res != vk::Result::eSuccess) {
221 LOG_CRITICAL(Render_Vulkan, "Device allocation failed with code {}!", vk::to_string(res));
222 return false;
223 }
224 allocs.push_back(
225 std::make_unique<VKMemoryAllocation>(device, memory, wanted_properties, size, type));
226 return true;
227}
228
229/*static*/ bool VKMemoryManager::GetMemoryUnified(const vk::PhysicalDeviceMemoryProperties& props) {
230 for (u32 heap_index = 0; heap_index < props.memoryHeapCount; ++heap_index) {
231 if (!(props.memoryHeaps[heap_index].flags & vk::MemoryHeapFlagBits::eDeviceLocal)) {
232 // Memory is considered unified when heaps are device local only.
233 return false;
234 }
235 }
236 return true;
237}
238
239VKMemoryCommitImpl::VKMemoryCommitImpl(VKMemoryAllocation* allocation, vk::DeviceMemory memory,
240 u8* data, u64 begin, u64 end)
241 : allocation{allocation}, memory{memory}, data{data}, interval(std::make_pair(begin, end)) {}
242
243VKMemoryCommitImpl::~VKMemoryCommitImpl() {
244 allocation->Free(this);
245}
246
247u8* VKMemoryCommitImpl::GetData() const {
248 ASSERT_MSG(data != nullptr, "Trying to access an unmapped commit.");
249 return data;
250}
251
252} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_memory_manager.h b/src/video_core/renderer_vulkan/vk_memory_manager.h
new file mode 100644
index 000000000..073597b35
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_memory_manager.h
@@ -0,0 +1,87 @@
1// Copyright 2019 yuzu Emulator Project
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 <utility>
9#include <vector>
10#include "common/common_types.h"
11#include "video_core/renderer_vulkan/declarations.h"
12
13namespace Vulkan {
14
15class VKDevice;
16class VKMemoryAllocation;
17class VKMemoryCommitImpl;
18
19using VKMemoryCommit = std::unique_ptr<VKMemoryCommitImpl>;
20
21class VKMemoryManager final {
22public:
23 explicit VKMemoryManager(const VKDevice& device);
24 ~VKMemoryManager();
25
26 /**
27 * Commits a memory with the specified requeriments.
28 * @param reqs Requeriments returned from a Vulkan call.
29 * @param host_visible Signals the allocator that it *must* use host visible and coherent
30 * memory. When passing false, it will try to allocate device local memory.
31 * @returns A memory commit.
32 */
33 VKMemoryCommit Commit(const vk::MemoryRequirements& reqs, bool host_visible);
34
35 /// Commits memory required by the buffer and binds it.
36 VKMemoryCommit Commit(vk::Buffer buffer, bool host_visible);
37
38 /// Commits memory required by the image and binds it.
39 VKMemoryCommit Commit(vk::Image image, bool host_visible);
40
41 /// Returns true if the memory allocations are done always in host visible and coherent memory.
42 bool IsMemoryUnified() const {
43 return is_memory_unified;
44 }
45
46private:
47 /// Allocates a chunk of memory.
48 bool AllocMemory(vk::MemoryPropertyFlags wanted_properties, u32 type_mask, u64 size);
49
50 /// Returns true if the device uses an unified memory model.
51 static bool GetMemoryUnified(const vk::PhysicalDeviceMemoryProperties& props);
52
53 const VKDevice& device; ///< Device handler.
54 const vk::PhysicalDeviceMemoryProperties props; ///< Physical device properties.
55 const bool is_memory_unified; ///< True if memory model is unified.
56 std::vector<std::unique_ptr<VKMemoryAllocation>> allocs; ///< Current allocations.
57};
58
59class VKMemoryCommitImpl final {
60 friend VKMemoryAllocation;
61
62public:
63 explicit VKMemoryCommitImpl(VKMemoryAllocation* allocation, vk::DeviceMemory memory, u8* data,
64 u64 begin, u64 end);
65 ~VKMemoryCommitImpl();
66
67 /// Returns the writeable memory map. The commit has to be mappable.
68 u8* GetData() const;
69
70 /// Returns the Vulkan memory handler.
71 vk::DeviceMemory GetMemory() const {
72 return memory;
73 }
74
75 /// Returns the start position of the commit relative to the allocation.
76 vk::DeviceSize GetOffset() const {
77 return static_cast<vk::DeviceSize>(interval.first);
78 }
79
80private:
81 std::pair<u64, u64> interval{}; ///< Interval where the commit exists.
82 vk::DeviceMemory memory; ///< Vulkan device memory handler.
83 VKMemoryAllocation* allocation{}; ///< Pointer to the large memory allocation.
84 u8* data{}; ///< Pointer to the host mapped memory, it has the commit offset included.
85};
86
87} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_resource_manager.cpp b/src/video_core/renderer_vulkan/vk_resource_manager.cpp
new file mode 100644
index 000000000..1678463c7
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_resource_manager.cpp
@@ -0,0 +1,285 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <optional>
7#include "common/assert.h"
8#include "common/logging/log.h"
9#include "video_core/renderer_vulkan/declarations.h"
10#include "video_core/renderer_vulkan/vk_device.h"
11#include "video_core/renderer_vulkan/vk_resource_manager.h"
12
13namespace Vulkan {
14
15// TODO(Rodrigo): Fine tune these numbers.
16constexpr std::size_t COMMAND_BUFFER_POOL_SIZE = 0x1000;
17constexpr std::size_t FENCES_GROW_STEP = 0x40;
18
19class CommandBufferPool final : public VKFencedPool {
20public:
21 CommandBufferPool(const VKDevice& device)
22 : VKFencedPool(COMMAND_BUFFER_POOL_SIZE), device{device} {}
23
24 void Allocate(std::size_t begin, std::size_t end) {
25 const auto dev = device.GetLogical();
26 const auto& dld = device.GetDispatchLoader();
27 const u32 graphics_family = device.GetGraphicsFamily();
28
29 auto pool = std::make_unique<Pool>();
30
31 // Command buffers are going to be commited, recorded, executed every single usage cycle.
32 // They are also going to be reseted when commited.
33 const auto pool_flags = vk::CommandPoolCreateFlagBits::eTransient |
34 vk::CommandPoolCreateFlagBits::eResetCommandBuffer;
35 const vk::CommandPoolCreateInfo cmdbuf_pool_ci(pool_flags, graphics_family);
36 pool->handle = dev.createCommandPoolUnique(cmdbuf_pool_ci, nullptr, dld);
37
38 const vk::CommandBufferAllocateInfo cmdbuf_ai(*pool->handle,
39 vk::CommandBufferLevel::ePrimary,
40 static_cast<u32>(COMMAND_BUFFER_POOL_SIZE));
41 pool->cmdbufs =
42 dev.allocateCommandBuffersUnique<std::allocator<UniqueCommandBuffer>>(cmdbuf_ai, dld);
43
44 pools.push_back(std::move(pool));
45 }
46
47 vk::CommandBuffer Commit(VKFence& fence) {
48 const std::size_t index = CommitResource(fence);
49 const auto pool_index = index / COMMAND_BUFFER_POOL_SIZE;
50 const auto sub_index = index % COMMAND_BUFFER_POOL_SIZE;
51 return *pools[pool_index]->cmdbufs[sub_index];
52 }
53
54private:
55 struct Pool {
56 UniqueCommandPool handle;
57 std::vector<UniqueCommandBuffer> cmdbufs;
58 };
59
60 const VKDevice& device;
61
62 std::vector<std::unique_ptr<Pool>> pools;
63};
64
65VKResource::VKResource() = default;
66
67VKResource::~VKResource() = default;
68
69VKFence::VKFence(const VKDevice& device, UniqueFence handle)
70 : device{device}, handle{std::move(handle)} {}
71
72VKFence::~VKFence() = default;
73
74void VKFence::Wait() {
75 const auto dev = device.GetLogical();
76 const auto& dld = device.GetDispatchLoader();
77 dev.waitForFences({*handle}, true, std::numeric_limits<u64>::max(), dld);
78}
79
80void VKFence::Release() {
81 is_owned = false;
82}
83
84void VKFence::Commit() {
85 is_owned = true;
86 is_used = true;
87}
88
89bool VKFence::Tick(bool gpu_wait, bool owner_wait) {
90 if (!is_used) {
91 // If a fence is not used it's always free.
92 return true;
93 }
94 if (is_owned && !owner_wait) {
95 // The fence is still being owned (Release has not been called) and ownership wait has
96 // not been asked.
97 return false;
98 }
99
100 const auto dev = device.GetLogical();
101 const auto& dld = device.GetDispatchLoader();
102 if (gpu_wait) {
103 // Wait for the fence if it has been requested.
104 dev.waitForFences({*handle}, true, std::numeric_limits<u64>::max(), dld);
105 } else {
106 if (dev.getFenceStatus(*handle, dld) != vk::Result::eSuccess) {
107 // Vulkan fence is not ready, not much it can do here
108 return false;
109 }
110 }
111
112 // Broadcast resources their free state.
113 for (auto* resource : protected_resources) {
114 resource->OnFenceRemoval(this);
115 }
116 protected_resources.clear();
117
118 // Prepare fence for reusage.
119 dev.resetFences({*handle}, dld);
120 is_used = false;
121 return true;
122}
123
124void VKFence::Protect(VKResource* resource) {
125 protected_resources.push_back(resource);
126}
127
128void VKFence::Unprotect(const VKResource* resource) {
129 const auto it = std::find(protected_resources.begin(), protected_resources.end(), resource);
130 if (it != protected_resources.end()) {
131 protected_resources.erase(it);
132 }
133}
134
135VKFenceWatch::VKFenceWatch() = default;
136
137VKFenceWatch::~VKFenceWatch() {
138 if (fence) {
139 fence->Unprotect(this);
140 }
141}
142
143void VKFenceWatch::Wait() {
144 if (!fence) {
145 return;
146 }
147 fence->Wait();
148 fence->Unprotect(this);
149 fence = nullptr;
150}
151
152void VKFenceWatch::Watch(VKFence& new_fence) {
153 Wait();
154 fence = &new_fence;
155 fence->Protect(this);
156}
157
158bool VKFenceWatch::TryWatch(VKFence& new_fence) {
159 if (fence) {
160 return false;
161 }
162 fence = &new_fence;
163 fence->Protect(this);
164 return true;
165}
166
167void VKFenceWatch::OnFenceRemoval(VKFence* signaling_fence) {
168 ASSERT_MSG(signaling_fence == fence, "Removing the wrong fence");
169 fence = nullptr;
170}
171
172VKFencedPool::VKFencedPool(std::size_t grow_step) : grow_step{grow_step} {}
173
174VKFencedPool::~VKFencedPool() = default;
175
176std::size_t VKFencedPool::CommitResource(VKFence& fence) {
177 const auto Search = [&](std::size_t begin, std::size_t end) -> std::optional<std::size_t> {
178 for (std::size_t iterator = begin; iterator < end; ++iterator) {
179 if (watches[iterator]->TryWatch(fence)) {
180 // The resource is now being watched, a free resource was successfully found.
181 return iterator;
182 }
183 }
184 return {};
185 };
186 // Try to find a free resource from the hinted position to the end.
187 auto found = Search(free_iterator, watches.size());
188 if (!found) {
189 // Search from beginning to the hinted position.
190 found = Search(0, free_iterator);
191 if (!found) {
192 // Both searches failed, the pool is full; handle it.
193 const std::size_t free_resource = ManageOverflow();
194
195 // Watch will wait for the resource to be free.
196 watches[free_resource]->Watch(fence);
197 found = free_resource;
198 }
199 }
200 // Free iterator is hinted to the resource after the one that's been commited.
201 free_iterator = (*found + 1) % watches.size();
202 return *found;
203}
204
205std::size_t VKFencedPool::ManageOverflow() {
206 const std::size_t old_capacity = watches.size();
207 Grow();
208
209 // The last entry is guaranted to be free, since it's the first element of the freshly
210 // allocated resources.
211 return old_capacity;
212}
213
214void VKFencedPool::Grow() {
215 const std::size_t old_capacity = watches.size();
216 watches.resize(old_capacity + grow_step);
217 std::generate(watches.begin() + old_capacity, watches.end(),
218 []() { return std::make_unique<VKFenceWatch>(); });
219 Allocate(old_capacity, old_capacity + grow_step);
220}
221
222VKResourceManager::VKResourceManager(const VKDevice& device) : device{device} {
223 GrowFences(FENCES_GROW_STEP);
224 command_buffer_pool = std::make_unique<CommandBufferPool>(device);
225}
226
227VKResourceManager::~VKResourceManager() = default;
228
229VKFence& VKResourceManager::CommitFence() {
230 const auto StepFences = [&](bool gpu_wait, bool owner_wait) -> VKFence* {
231 const auto Tick = [=](auto& fence) { return fence->Tick(gpu_wait, owner_wait); };
232 const auto hinted = fences.begin() + fences_iterator;
233
234 auto it = std::find_if(hinted, fences.end(), Tick);
235 if (it == fences.end()) {
236 it = std::find_if(fences.begin(), hinted, Tick);
237 if (it == hinted) {
238 return nullptr;
239 }
240 }
241 fences_iterator = std::distance(fences.begin(), it) + 1;
242 if (fences_iterator >= fences.size())
243 fences_iterator = 0;
244
245 auto& fence = *it;
246 fence->Commit();
247 return fence.get();
248 };
249
250 VKFence* found_fence = StepFences(false, false);
251 if (!found_fence) {
252 // Try again, this time waiting.
253 found_fence = StepFences(true, false);
254
255 if (!found_fence) {
256 // Allocate new fences and try again.
257 LOG_INFO(Render_Vulkan, "Allocating new fences {} -> {}", fences.size(),
258 fences.size() + FENCES_GROW_STEP);
259
260 GrowFences(FENCES_GROW_STEP);
261 found_fence = StepFences(true, false);
262 ASSERT(found_fence != nullptr);
263 }
264 }
265 return *found_fence;
266}
267
268vk::CommandBuffer VKResourceManager::CommitCommandBuffer(VKFence& fence) {
269 return command_buffer_pool->Commit(fence);
270}
271
272void VKResourceManager::GrowFences(std::size_t new_fences_count) {
273 const auto dev = device.GetLogical();
274 const auto& dld = device.GetDispatchLoader();
275 const vk::FenceCreateInfo fence_ci;
276
277 const std::size_t previous_size = fences.size();
278 fences.resize(previous_size + new_fences_count);
279
280 std::generate(fences.begin() + previous_size, fences.end(), [&]() {
281 return std::make_unique<VKFence>(device, dev.createFenceUnique(fence_ci, nullptr, dld));
282 });
283}
284
285} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_resource_manager.h b/src/video_core/renderer_vulkan/vk_resource_manager.h
new file mode 100644
index 000000000..5018dfa44
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_resource_manager.h
@@ -0,0 +1,180 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <cstddef>
8#include <memory>
9#include <vector>
10#include "video_core/renderer_vulkan/declarations.h"
11
12namespace Vulkan {
13
14class VKDevice;
15class VKFence;
16class VKResourceManager;
17
18class CommandBufferPool;
19
20/// Interface for a Vulkan resource
21class VKResource {
22public:
23 explicit VKResource();
24 virtual ~VKResource();
25
26 /**
27 * Signals the object that an owning fence has been signaled.
28 * @param signaling_fence Fence that signals its usage end.
29 */
30 virtual void OnFenceRemoval(VKFence* signaling_fence) = 0;
31};
32
33/**
34 * Fences take ownership of objects, protecting them from GPU-side or driver-side concurrent access.
35 * They must be commited from the resource manager. Their usage flow is: commit the fence from the
36 * resource manager, protect resources with it and use them, send the fence to an execution queue
37 * and Wait for it if needed and then call Release. Used resources will automatically be signaled
38 * when they are free to be reused.
39 * @brief Protects resources for concurrent usage and signals its release.
40 */
41class VKFence {
42 friend class VKResourceManager;
43
44public:
45 explicit VKFence(const VKDevice& device, UniqueFence handle);
46 ~VKFence();
47
48 /**
49 * Waits for the fence to be signaled.
50 * @warning You must have ownership of the fence and it has to be previously sent to a queue to
51 * call this function.
52 */
53 void Wait();
54
55 /**
56 * Releases ownership of the fence. Pass after it has been sent to an execution queue.
57 * Unmanaged usage of the fence after the call will result in undefined behavior because it may
58 * be being used for something else.
59 */
60 void Release();
61
62 /// Protects a resource with this fence.
63 void Protect(VKResource* resource);
64
65 /// Removes protection for a resource.
66 void Unprotect(const VKResource* resource);
67
68 /// Retreives the fence.
69 operator vk::Fence() const {
70 return *handle;
71 }
72
73private:
74 /// Take ownership of the fence.
75 void Commit();
76
77 /**
78 * Updates the fence status.
79 * @warning Waiting for the owner might soft lock the execution.
80 * @param gpu_wait Wait for the fence to be signaled by the driver.
81 * @param owner_wait Wait for the owner to signal its freedom.
82 * @returns True if the fence is free. Waiting for gpu and owner will always return true.
83 */
84 bool Tick(bool gpu_wait, bool owner_wait);
85
86 const VKDevice& device; ///< Device handler
87 UniqueFence handle; ///< Vulkan fence
88 std::vector<VKResource*> protected_resources; ///< List of resources protected by this fence
89 bool is_owned = false; ///< The fence has been commited but not released yet.
90 bool is_used = false; ///< The fence has been commited but it has not been checked to be free.
91};
92
93/**
94 * A fence watch is used to keep track of the usage of a fence and protect a resource or set of
95 * resources without having to inherit VKResource from their handlers.
96 */
97class VKFenceWatch final : public VKResource {
98public:
99 explicit VKFenceWatch();
100 ~VKFenceWatch();
101
102 /// Waits for the fence to be released.
103 void Wait();
104
105 /**
106 * Waits for a previous fence and watches a new one.
107 * @param new_fence New fence to wait to.
108 */
109 void Watch(VKFence& new_fence);
110
111 /**
112 * Checks if it's currently being watched and starts watching it if it's available.
113 * @returns True if a watch has started, false if it's being watched.
114 */
115 bool TryWatch(VKFence& new_fence);
116
117 void OnFenceRemoval(VKFence* signaling_fence) override;
118
119private:
120 VKFence* fence{}; ///< Fence watching this resource. nullptr when the watch is free.
121};
122
123/**
124 * Handles a pool of resources protected by fences. Manages resource overflow allocating more
125 * resources.
126 */
127class VKFencedPool {
128public:
129 explicit VKFencedPool(std::size_t grow_step);
130 virtual ~VKFencedPool();
131
132protected:
133 /**
134 * Commits a free resource and protects it with a fence. It may allocate new resources.
135 * @param fence Fence that protects the commited resource.
136 * @returns Index of the resource commited.
137 */
138 std::size_t CommitResource(VKFence& fence);
139
140 /// Called when a chunk of resources have to be allocated.
141 virtual void Allocate(std::size_t begin, std::size_t end) = 0;
142
143private:
144 /// Manages pool overflow allocating new resources.
145 std::size_t ManageOverflow();
146
147 /// Allocates a new page of resources.
148 void Grow();
149
150 std::size_t grow_step = 0; ///< Number of new resources created after an overflow
151 std::size_t free_iterator = 0; ///< Hint to where the next free resources is likely to be found
152 std::vector<std::unique_ptr<VKFenceWatch>> watches; ///< Set of watched resources
153};
154
155/**
156 * The resource manager handles all resources that can be protected with a fence avoiding
157 * driver-side or GPU-side concurrent usage. Usage is documented in VKFence.
158 */
159class VKResourceManager final {
160public:
161 explicit VKResourceManager(const VKDevice& device);
162 ~VKResourceManager();
163
164 /// Commits a fence. It has to be sent to a queue and released.
165 VKFence& CommitFence();
166
167 /// Commits an unused command buffer and protects it with a fence.
168 vk::CommandBuffer CommitCommandBuffer(VKFence& fence);
169
170private:
171 /// Allocates new fences.
172 void GrowFences(std::size_t new_fences_count);
173
174 const VKDevice& device; ///< Device handler.
175 std::size_t fences_iterator = 0; ///< Index where a free fence is likely to be found.
176 std::vector<std::unique_ptr<VKFence>> fences; ///< Pool of fences.
177 std::unique_ptr<CommandBufferPool> command_buffer_pool; ///< Pool of command buffers.
178};
179
180} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
new file mode 100644
index 000000000..f1fea1871
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -0,0 +1,60 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "common/logging/log.h"
7#include "video_core/renderer_vulkan/declarations.h"
8#include "video_core/renderer_vulkan/vk_device.h"
9#include "video_core/renderer_vulkan/vk_resource_manager.h"
10#include "video_core/renderer_vulkan/vk_scheduler.h"
11
12namespace Vulkan {
13
14VKScheduler::VKScheduler(const VKDevice& device, VKResourceManager& resource_manager)
15 : device{device}, resource_manager{resource_manager} {
16 next_fence = &resource_manager.CommitFence();
17 AllocateNewContext();
18}
19
20VKScheduler::~VKScheduler() = default;
21
22VKExecutionContext VKScheduler::GetExecutionContext() const {
23 return VKExecutionContext(current_fence, current_cmdbuf);
24}
25
26VKExecutionContext VKScheduler::Flush(vk::Semaphore semaphore) {
27 SubmitExecution(semaphore);
28 current_fence->Release();
29 AllocateNewContext();
30 return GetExecutionContext();
31}
32
33VKExecutionContext VKScheduler::Finish(vk::Semaphore semaphore) {
34 SubmitExecution(semaphore);
35 current_fence->Wait();
36 current_fence->Release();
37 AllocateNewContext();
38 return GetExecutionContext();
39}
40
41void VKScheduler::SubmitExecution(vk::Semaphore semaphore) {
42 const auto& dld = device.GetDispatchLoader();
43 current_cmdbuf.end(dld);
44
45 const auto queue = device.GetGraphicsQueue();
46 const vk::SubmitInfo submit_info(0, nullptr, nullptr, 1, &current_cmdbuf, semaphore ? 1u : 0u,
47 &semaphore);
48 queue.submit({submit_info}, *current_fence, dld);
49}
50
51void VKScheduler::AllocateNewContext() {
52 current_fence = next_fence;
53 current_cmdbuf = resource_manager.CommitCommandBuffer(*current_fence);
54 next_fence = &resource_manager.CommitFence();
55
56 const auto& dld = device.GetDispatchLoader();
57 current_cmdbuf.begin({vk::CommandBufferUsageFlagBits::eOneTimeSubmit}, dld);
58}
59
60} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h
new file mode 100644
index 000000000..cfaf5376f
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_scheduler.h
@@ -0,0 +1,69 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "video_core/renderer_vulkan/declarations.h"
9
10namespace Vulkan {
11
12class VKDevice;
13class VKExecutionContext;
14class VKFence;
15class VKResourceManager;
16
17/// The scheduler abstracts command buffer and fence management with an interface that's able to do
18/// OpenGL-like operations on Vulkan command buffers.
19class VKScheduler {
20public:
21 explicit VKScheduler(const VKDevice& device, VKResourceManager& resource_manager);
22 ~VKScheduler();
23
24 /// Gets the current execution context.
25 [[nodiscard]] VKExecutionContext GetExecutionContext() const;
26
27 /// Sends the current execution context to the GPU. It invalidates the current execution context
28 /// and returns a new one.
29 VKExecutionContext Flush(vk::Semaphore semaphore = nullptr);
30
31 /// Sends the current execution context to the GPU and waits for it to complete. It invalidates
32 /// the current execution context and returns a new one.
33 VKExecutionContext Finish(vk::Semaphore semaphore = nullptr);
34
35private:
36 void SubmitExecution(vk::Semaphore semaphore);
37
38 void AllocateNewContext();
39
40 const VKDevice& device;
41 VKResourceManager& resource_manager;
42 vk::CommandBuffer current_cmdbuf;
43 VKFence* current_fence = nullptr;
44 VKFence* next_fence = nullptr;
45};
46
47class VKExecutionContext {
48 friend class VKScheduler;
49
50public:
51 VKExecutionContext() = default;
52
53 VKFence& GetFence() const {
54 return *fence;
55 }
56
57 vk::CommandBuffer GetCommandBuffer() const {
58 return cmdbuf;
59 }
60
61private:
62 explicit VKExecutionContext(VKFence* fence, vk::CommandBuffer cmdbuf)
63 : fence{fence}, cmdbuf{cmdbuf} {}
64
65 VKFence* fence{};
66 vk::CommandBuffer cmdbuf;
67};
68
69} // namespace Vulkan
diff --git a/src/video_core/shader/decode/memory.cpp b/src/video_core/shader/decode/memory.cpp
index 523421794..55ec601ff 100644
--- a/src/video_core/shader/decode/memory.cpp
+++ b/src/video_core/shader/decode/memory.cpp
@@ -429,7 +429,7 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
429 UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(TextureMiscMode::MZ), "MZ is not implemented"); 429 UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(TextureMiscMode::MZ), "MZ is not implemented");
430 430
431 if (instr.tlds.UsesMiscMode(TextureMiscMode::NODEP)) { 431 if (instr.tlds.UsesMiscMode(TextureMiscMode::NODEP)) {
432 LOG_WARNING(HW_GPU, "TMML.NODEP implementation is incomplete"); 432 LOG_WARNING(HW_GPU, "TLDS.NODEP implementation is incomplete");
433 } 433 }
434 434
435 WriteTexsInstructionFloat(bb, instr, GetTldsCode(instr, texture_type, is_array)); 435 WriteTexsInstructionFloat(bb, instr, GetTldsCode(instr, texture_type, is_array));