diff options
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 | ||
| 24 | option(ENABLE_CUBEB "Enables the cubeb audio backend" ON) | 24 | option(ENABLE_CUBEB "Enables the cubeb audio backend" ON) |
| 25 | 25 | ||
| 26 | option(ENABLE_VULKAN "Enables Vulkan backend" ON) | ||
| 27 | |||
| 26 | option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF) | 28 | option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF) |
| 27 | 29 | ||
| 28 | if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/hooks/pre-commit) | 30 | if(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 | ||
| 29 | StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&& name, | 29 | StreamPtr 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 | ||
| 16 | namespace Core::Timing { | ||
| 17 | class CoreTiming; | ||
| 18 | } | ||
| 19 | |||
| 16 | namespace AudioCore { | 20 | namespace AudioCore { |
| 17 | 21 | ||
| 18 | /** | 22 | /** |
| @@ -21,8 +25,8 @@ namespace AudioCore { | |||
| 21 | class AudioOut { | 25 | class AudioOut { |
| 22 | public: | 26 | public: |
| 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 | }; |
| 74 | AudioRenderer::AudioRenderer(AudioRendererParameter params, | 75 | AudioRenderer::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 | ||
| 17 | namespace Core::Timing { | ||
| 18 | class CoreTiming; | ||
| 19 | } | ||
| 20 | |||
| 17 | namespace Kernel { | 21 | namespace Kernel { |
| 18 | class WritableEvent; | 22 | class WritableEvent; |
| 19 | } | 23 | } |
| @@ -208,7 +212,7 @@ static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size | |||
| 208 | 212 | ||
| 209 | class AudioRenderer { | 213 | class AudioRenderer { |
| 210 | public: | 214 | public: |
| 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 | ||
| 35 | Stream::Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback, | 35 | Stream::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 | ||
| 106 | void Stream::ReleaseActiveBuffer() { | 105 | void 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 | ||
| 16 | namespace Core::Timing { | 16 | namespace Core::Timing { |
| 17 | class CoreTiming; | ||
| 17 | struct EventType; | 18 | struct EventType; |
| 18 | } | 19 | } // namespace Core::Timing |
| 19 | 20 | ||
| 20 | namespace AudioCore { | 21 | namespace 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 | ||
| 16 | namespace Common { | 16 | namespace Common { |
| 17 | template <typename T, bool NeedSize = true> | 17 | template <typename T> |
| 18 | class SPSCQueue { | 18 | class SPSCQueue { |
| 19 | public: | 19 | public: |
| 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 | ||
| 112 | template <typename T, bool NeedSize = true> | 123 | template <typename T> |
| 113 | class MPSCQueue { | 124 | class MPSCQueue { |
| 114 | public: | 125 | public: |
| 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 | ||
| 146 | private: | 161 | private: |
| 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 | ||
| 175 | ARM_Dynarmic::ARM_Dynarmic(ExclusiveMonitor& exclusive_monitor, std::size_t core_index) | 175 | ARM_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 { | |||
| 16 | struct PageTable; | 16 | struct PageTable; |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | namespace Core::Timing { | ||
| 20 | class CoreTiming; | ||
| 21 | } | ||
| 22 | |||
| 19 | namespace Core { | 23 | namespace Core { |
| 20 | 24 | ||
| 21 | class ARM_Dynarmic_Callbacks; | 25 | class ARM_Dynarmic_Callbacks; |
| @@ -23,7 +27,8 @@ class DynarmicExclusiveMonitor; | |||
| 23 | 27 | ||
| 24 | class ARM_Dynarmic final : public ARM_Interface { | 28 | class ARM_Dynarmic final : public ARM_Interface { |
| 25 | public: | 29 | public: |
| 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 | ||
| 75 | ARM_Unicorn::ARM_Unicorn() { | 75 | ARM_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)); | |||
| 190 | void ARM_Unicorn::ExecuteInstructions(int num_instructions) { | 190 | void 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 | ||
| 12 | namespace Core::Timing { | ||
| 13 | class CoreTiming; | ||
| 14 | } | ||
| 15 | |||
| 12 | namespace Core { | 16 | namespace Core { |
| 13 | 17 | ||
| 14 | class ARM_Unicorn final : public ARM_Interface { | 18 | class ARM_Unicorn final : public ARM_Interface { |
| 15 | public: | 19 | public: |
| 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 | ||
| 44 | private: | 49 | private: |
| 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 | ||
| 400 | Timing::CoreTiming& System::CoreTiming() { | ||
| 401 | return impl->core_timing; | ||
| 402 | } | ||
| 403 | |||
| 404 | const Timing::CoreTiming& System::CoreTiming() const { | ||
| 405 | return impl->core_timing; | ||
| 406 | } | ||
| 407 | |||
| 399 | Core::PerfStats& System::GetPerfStats() { | 408 | Core::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 { | |||
| 47 | class RendererBase; | 47 | class RendererBase; |
| 48 | } // namespace VideoCore | 48 | } // namespace VideoCore |
| 49 | 49 | ||
| 50 | namespace Core::Timing { | ||
| 51 | class CoreTiming; | ||
| 52 | } | ||
| 53 | |||
| 50 | namespace Core { | 54 | namespace Core { |
| 51 | 55 | ||
| 52 | class ARM_Interface; | 56 | class 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 | ||
| 52 | Cpu::Cpu(ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, std::size_t core_index) | 52 | Cpu::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 { | |||
| 15 | class Scheduler; | 15 | class Scheduler; |
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | namespace Core::Timing { | ||
| 19 | class CoreTiming; | ||
| 20 | } | ||
| 21 | |||
| 18 | namespace Core { | 22 | namespace Core { |
| 19 | 23 | ||
| 20 | class ARM_Interface; | 24 | class ARM_Interface; |
| @@ -41,7 +45,8 @@ private: | |||
| 41 | 45 | ||
| 42 | class Cpu { | 46 | class Cpu { |
| 43 | public: | 47 | public: |
| 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 | ||
| 18 | namespace Core::Timing { | 16 | namespace Core::Timing { |
| 19 | 17 | ||
| 20 | static s64 global_timer; | 18 | constexpr int MAX_SLICE_LENGTH = 20000; |
| 21 | static int slice_length; | ||
| 22 | static int downcount; | ||
| 23 | |||
| 24 | struct EventType { | ||
| 25 | TimedCallback callback; | ||
| 26 | const std::string* name; | ||
| 27 | }; | ||
| 28 | 19 | ||
| 29 | struct Event { | 20 | struct 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 | ||
| 37 | static 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 | |||
| 41 | static 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. | ||
| 47 | static 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); |
| 53 | static std::vector<Event> event_queue; | 30 | } |
| 54 | static 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 | ||
| 57 | static Common::MPSCQueue<Event, false> ts_queue; | ||
| 58 | |||
| 59 | // the queue for unscheduling the events from other threads threadsafe | ||
| 60 | static Common::MPSCQueue<std::pair<const EventType*, u64>, false> unschedule_queue; | ||
| 61 | |||
| 62 | constexpr int MAX_SLICE_LENGTH = 20000; | ||
| 63 | |||
| 64 | static 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. | ||
| 69 | static bool is_global_timer_sane; | ||
| 70 | |||
| 71 | static EventType* ev_lost = nullptr; | ||
| 72 | |||
| 73 | static void EmptyTimedCallback(u64 userdata, s64 cyclesLate) {} | ||
| 74 | |||
| 75 | EventType* 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 | ||
| 89 | void UnregisterAllEvents() { | 37 | CoreTiming::CoreTiming() = default; |
| 90 | ASSERT_MSG(event_queue.empty(), "Cannot unregister events with events pending"); | 38 | CoreTiming::~CoreTiming() = default; |
| 91 | event_types.clear(); | ||
| 92 | } | ||
| 93 | 39 | ||
| 94 | void Init() { | 40 | void 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 | ||
| 110 | void Shutdown() { | 58 | void 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 | 64 | EventType* 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. |
| 118 | u64 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 | |||
| 126 | void AddTicks(u64 ticks) { | ||
| 127 | downcount -= static_cast<int>(ticks); | ||
| 128 | } | ||
| 129 | 71 | ||
| 130 | u64 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 | ||
| 134 | void ClearPendingEvents() { | 78 | void 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 | ||
| 138 | void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) { | 83 | void 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 | ||
| 148 | void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata) { | 96 | void 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 | ||
| 152 | void UnscheduleEvent(const EventType* event_type, u64 userdata) { | 101 | void 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 | ||
| 164 | void UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata) { | 113 | void 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 | ||
| 168 | void RemoveEvent(const EventType* event_type) { | 117 | u64 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 | |||
| 125 | u64 CoreTiming::GetIdleTicks() const { | ||
| 126 | return static_cast<u64>(idled_cycles); | ||
| 127 | } | ||
| 128 | |||
| 129 | void CoreTiming::AddTicks(u64 ticks) { | ||
| 130 | downcount -= static_cast<int>(ticks); | ||
| 131 | } | ||
| 132 | |||
| 133 | void CoreTiming::ClearPendingEvents() { | ||
| 134 | event_queue.clear(); | ||
| 135 | } | ||
| 136 | |||
| 137 | void 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 | ||
| 179 | void RemoveNormalAndThreadsafeEvent(const EventType* event_type) { | 148 | void CoreTiming::RemoveNormalAndThreadsafeEvent(const EventType* event_type) { |
| 180 | MoveEvents(); | 149 | MoveEvents(); |
| 181 | RemoveEvent(event_type); | 150 | RemoveEvent(event_type); |
| 182 | } | 151 | } |
| 183 | 152 | ||
| 184 | void ForceExceptionCheck(s64 cycles) { | 153 | void 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 | ||
| 194 | void MoveEvents() { | 165 | void 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 | ||
| 202 | void Advance() { | 173 | void 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 | ||
| 232 | void Idle() { | 203 | void CoreTiming::Idle() { |
| 233 | idled_cycles += downcount; | 204 | idled_cycles += downcount; |
| 234 | downcount = 0; | 205 | downcount = 0; |
| 235 | } | 206 | } |
| 236 | 207 | ||
| 237 | std::chrono::microseconds GetGlobalTimeUs() { | 208 | std::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 | ||
| 241 | int GetDowncount() { | 212 | int 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 | ||
| 25 | namespace Core::Timing { | 15 | namespace Core::Timing { |
| 26 | 16 | ||
| 27 | struct EventType; | 17 | /// A callback that may be scheduled for a particular core timing event. |
| 28 | |||
| 29 | using TimedCallback = std::function<void(u64 userdata, int cycles_late)>; | 18 | using 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 | 21 | struct 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; |
| 35 | void Init(); | 24 | /// A pointer to the name of the event. |
| 36 | void 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 | */ | ||
| 42 | u64 GetTicks(); | ||
| 43 | u64 GetIdleTicks(); | ||
| 44 | void AddTicks(u64 ticks); | ||
| 45 | |||
| 46 | /** | ||
| 47 | * Returns the event_type identifier. if name is not unique, it will assert. | ||
| 48 | */ | ||
| 49 | EventType* RegisterEvent(const std::string& name, TimedCallback callback); | ||
| 50 | void 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 | */ | ||
| 57 | void 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. |
| 65 | void 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. | |
| 67 | void UnscheduleEvent(const EventType* event_type, u64 userdata); | 36 | * So to schedule a new event on a regular basis: |
| 68 | void 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. | ||
| 71 | void RemoveEvent(const EventType* event_type); | ||
| 72 | void 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 | */ |
| 80 | void Advance(); | 40 | class CoreTiming { |
| 81 | void MoveEvents(); | 41 | public: |
| 82 | 42 | CoreTiming(); | |
| 83 | /// Pretend that the main CPU has executed enough cycles to reach the next event. | 43 | ~CoreTiming(); |
| 84 | void Idle(); | 44 | |
| 85 | 45 | CoreTiming(const CoreTiming&) = delete; | |
| 86 | /// Clear all pending events. This should ONLY be done on exit. | 46 | CoreTiming(CoreTiming&&) = delete; |
| 87 | void ClearPendingEvents(); | 47 | |
| 88 | 48 | CoreTiming& operator=(const CoreTiming&) = delete; | |
| 89 | void ForceExceptionCheck(s64 cycles); | 49 | CoreTiming& operator=(CoreTiming&&) = delete; |
| 90 | 50 | ||
| 91 | std::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. | |
| 93 | int 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 | |||
| 118 | private: | ||
| 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 | ||
| 400 | void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) { | 400 | void 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 | ||
| 20 | namespace Kernel { | 20 | namespace Kernel::AddressArbiter { |
| 21 | namespace AddressArbiter { | ||
| 22 | 21 | ||
| 23 | // Performs actual address waiting logic. | 22 | // Performs actual address waiting logic. |
| 24 | static ResultCode WaitForAddress(VAddr address, s64 timeout) { | 23 | static 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 | ||
| 9 | union ResultCode; | 9 | union ResultCode; |
| 10 | 10 | ||
| 11 | namespace Kernel { | 11 | namespace Kernel::AddressArbiter { |
| 12 | 12 | ||
| 13 | namespace AddressArbiter { | ||
| 14 | enum class ArbitrationType { | 13 | enum 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 | ||
| 30 | ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement); | 29 | ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement); |
| 31 | ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout); | 30 | ResultCode 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 | ||
| 88 | struct KernelCore::Impl { | 88 | struct 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 | ||
| 155 | void KernelCore::Initialize() { | 155 | void KernelCore::Initialize(Core::Timing::CoreTiming& core_timing) { |
| 156 | impl->Initialize(*this); | 156 | impl->Initialize(*this, core_timing); |
| 157 | } | 157 | } |
| 158 | 158 | ||
| 159 | void KernelCore::Shutdown() { | 159 | void 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> | |||
| 12 | class ResultVal; | 12 | class ResultVal; |
| 13 | 13 | ||
| 14 | namespace Core::Timing { | 14 | namespace Core::Timing { |
| 15 | class CoreTiming; | ||
| 15 | struct EventType; | 16 | struct EventType; |
| 16 | } | 17 | } // namespace Core::Timing |
| 17 | 18 | ||
| 18 | namespace Kernel { | 19 | namespace 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 | ||
| 112 | void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) { | 112 | void 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 | |||
| 1546 | static u64 GetSystemTick() { | 1547 | static 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 | ||
| 44 | void Thread::Stop() { | 44 | void 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 | ||
| 92 | void Thread::CancelWakeupTimer() { | 94 | void 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 | ||
| 97 | static std::optional<s32> GetNextProcessorId(u64 mask) { | 99 | static 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 | ||
| 51 | private: | 52 | private: |
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 | ||
| 10 | namespace Core::Timing { | ||
| 11 | class CoreTiming; | ||
| 12 | } | ||
| 13 | |||
| 10 | namespace Service::HID { | 14 | namespace Service::HID { |
| 11 | class ControllerBase { | 15 | class ControllerBase { |
| 12 | public: | 16 | public: |
| @@ -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 | ||
| 22 | void Controller_DebugPad::OnRelease() {} | 22 | void Controller_DebugPad::OnRelease() {} |
| 23 | 23 | ||
| 24 | void Controller_DebugPad::OnUpdate(u8* data, std::size_t size) { | 24 | void 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 | ||
| 18 | void Controller_Gesture::OnRelease() {} | 18 | void Controller_Gesture::OnRelease() {} |
| 19 | 19 | ||
| 20 | void Controller_Gesture::OnUpdate(u8* data, std::size_t size) { | 20 | void 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 | ||
| 20 | void Controller_Keyboard::OnRelease() {} | 20 | void Controller_Keyboard::OnRelease() {} |
| 21 | 21 | ||
| 22 | void Controller_Keyboard::OnUpdate(u8* data, std::size_t size) { | 22 | void 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; | |||
| 17 | void Controller_Mouse::OnInit() {} | 17 | void Controller_Mouse::OnInit() {} |
| 18 | void Controller_Mouse::OnRelease() {} | 18 | void Controller_Mouse::OnRelease() {} |
| 19 | 19 | ||
| 20 | void Controller_Mouse::OnUpdate(u8* data, std::size_t size) { | 20 | void 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 | ||
| 291 | void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) { | 291 | void 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 | ||
| 17 | void Controller_Stubbed::OnRelease() {} | 17 | void Controller_Stubbed::OnRelease() {} |
| 18 | 18 | ||
| 19 | void Controller_Stubbed::OnUpdate(u8* data, std::size_t size) { | 19 | void 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 | ||
| 21 | void Controller_Touchscreen::OnRelease() {} | 21 | void Controller_Touchscreen::OnRelease() {} |
| 22 | 22 | ||
| 23 | void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) { | 23 | void 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 | ||
| 18 | void Controller_XPad::OnRelease() {} | 18 | void Controller_XPad::OnRelease() {} |
| 19 | 19 | ||
| 20 | void Controller_XPad::OnUpdate(u8* data, std::size_t size) { | 20 | void 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 | ||
| 95 | IAppletResource ::~IAppletResource() { | 97 | IAppletResource ::~IAppletResource() { |
| 96 | Core::Timing::UnscheduleEvent(pad_update_event, 0); | 98 | Core::System::GetInstance().CoreTiming().UnscheduleEvent(pad_update_event, 0); |
| 97 | } | 99 | } |
| 98 | 100 | ||
| 99 | void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) { | 101 | void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) { |
| @@ -105,15 +107,17 @@ void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) { | |||
| 105 | } | 107 | } |
| 106 | 108 | ||
| 107 | void IAppletResource::UpdateControllers(u64 userdata, int cycles_late) { | 109 | void 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 | ||
| 119 | class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { | 123 | class 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(¶ms, input.data(), input.size()); | 187 | std::memcpy(¶ms, 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(), ¶ms, output.size()); | 189 | std::memcpy(output.data(), ¶ms, 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 { | |||
| 27 | constexpr std::size_t SCREEN_REFRESH_RATE = 60; | 28 | constexpr std::size_t SCREEN_REFRESH_RATE = 60; |
| 28 | constexpr u64 frame_ticks = static_cast<u64>(Core::Timing::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE); | 29 | constexpr u64 frame_ticks = static_cast<u64>(Core::Timing::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE); |
| 29 | 30 | ||
| 30 | NVFlinger::NVFlinger() { | 31 | NVFlinger::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 | ||
| 41 | NVFlinger::~NVFlinger() { | 48 | NVFlinger::~NVFlinger() { |
| 42 | Core::Timing::UnscheduleEvent(composition_event, 0); | 49 | core_timing.UnscheduleEvent(composition_event, 0); |
| 43 | } | 50 | } |
| 44 | 51 | ||
| 45 | void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { | 52 | void 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 | ||
| 64 | std::optional<u64> NVFlinger::CreateLayer(u64 display_id) { | 72 | std::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 | ||
| 91 | Kernel::SharedPtr<Kernel::ReadableEvent> NVFlinger::FindVsyncEvent(u64 display_id) const { | 96 | Kernel::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 | |||
| 106 | BufferQueue& 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 | ||
| 101 | std::shared_ptr<BufferQueue> NVFlinger::FindBufferQueue(u32 id) const { | 114 | const 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 | ||
| 109 | Display* NVFlinger::FindDisplay(u64 display_id) { | 122 | VI::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 | ||
| 120 | const Display* NVFlinger::FindDisplay(u64 display_id) const { | 134 | const 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 | ||
| 131 | Layer* NVFlinger::FindLayer(u64 display_id, u64 layer_id) { | 146 | VI::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 | ||
| 148 | const Layer* NVFlinger::FindLayer(u64 display_id, u64 layer_id) const { | 156 | const 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 | ||
| 165 | void NVFlinger::Compose() { | 166 | void 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 | ||
| 210 | Layer::Layer(u64 id, std::shared_ptr<BufferQueue> queue) : id(id), buffer_queue(std::move(queue)) {} | ||
| 211 | Layer::~Layer() = default; | ||
| 212 | |||
| 213 | Display::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 | |||
| 219 | Display::~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 | ||
| 17 | namespace Core::Timing { | 16 | namespace Core::Timing { |
| 17 | class CoreTiming; | ||
| 18 | struct EventType; | 18 | struct EventType; |
| 19 | } | 19 | } // namespace Core::Timing |
| 20 | 20 | ||
| 21 | namespace Kernel { | 21 | namespace Kernel { |
| 22 | class ReadableEvent; | 22 | class ReadableEvent; |
| @@ -25,34 +25,20 @@ class WritableEvent; | |||
| 25 | 25 | ||
| 26 | namespace Service::Nvidia { | 26 | namespace Service::Nvidia { |
| 27 | class Module; | 27 | class Module; |
| 28 | } | 28 | } // namespace Service::Nvidia |
| 29 | |||
| 30 | namespace Service::VI { | ||
| 31 | class Display; | ||
| 32 | class Layer; | ||
| 33 | } // namespace Service::VI | ||
| 29 | 34 | ||
| 30 | namespace Service::NVFlinger { | 35 | namespace Service::NVFlinger { |
| 31 | 36 | ||
| 32 | class BufferQueue; | 37 | class BufferQueue; |
| 33 | 38 | ||
| 34 | struct 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 | |||
| 42 | struct 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 | |||
| 53 | class NVFlinger final { | 39 | class NVFlinger final { |
| 54 | public: | 40 | public: |
| 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 | ||
| 88 | private: | 77 | private: |
| 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 |
| 197 | void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs) { | 197 | void 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 | ||
| 17 | namespace Core { | ||
| 18 | class System; | ||
| 19 | } | ||
| 20 | |||
| 21 | namespace FileSys { | ||
| 22 | class VfsFilesystem; | ||
| 23 | } | ||
| 24 | |||
| 17 | namespace Kernel { | 25 | namespace Kernel { |
| 18 | class ClientPort; | 26 | class ClientPort; |
| 19 | class ServerPort; | 27 | class ServerPort; |
| @@ -21,10 +29,6 @@ class ServerSession; | |||
| 21 | class HLERequestContext; | 29 | class HLERequestContext; |
| 22 | } // namespace Kernel | 30 | } // namespace Kernel |
| 23 | 31 | ||
| 24 | namespace FileSys { | ||
| 25 | class VfsFilesystem; | ||
| 26 | } | ||
| 27 | |||
| 28 | namespace Service { | 32 | namespace Service { |
| 29 | 33 | ||
| 30 | namespace SM { | 34 | namespace SM { |
| @@ -178,7 +182,8 @@ private: | |||
| 178 | }; | 182 | }; |
| 179 | 183 | ||
| 180 | /// Initialize ServiceManager | 184 | /// Initialize ServiceManager |
| 181 | void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs); | 185 | void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system, |
| 186 | FileSys::VfsFilesystem& vfs); | ||
| 182 | 187 | ||
| 183 | /// Shutdown ServiceManager | 188 | /// Shutdown ServiceManager |
| 184 | void Shutdown(); | 189 | void 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 | |||
| 16 | namespace Service::VI { | ||
| 17 | |||
| 18 | Display::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 | |||
| 24 | Display::~Display() = default; | ||
| 25 | |||
| 26 | Layer& Display::GetLayer(std::size_t index) { | ||
| 27 | return layers.at(index); | ||
| 28 | } | ||
| 29 | |||
| 30 | const Layer& Display::GetLayer(std::size_t index) const { | ||
| 31 | return layers.at(index); | ||
| 32 | } | ||
| 33 | |||
| 34 | Kernel::SharedPtr<Kernel::ReadableEvent> Display::GetVSyncEvent() const { | ||
| 35 | return vsync_event.readable; | ||
| 36 | } | ||
| 37 | |||
| 38 | void Display::SignalVSyncEvent() { | ||
| 39 | vsync_event.writable->Signal(); | ||
| 40 | } | ||
| 41 | |||
| 42 | void 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 | |||
| 49 | Layer* 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 | |||
| 60 | const 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 | |||
| 13 | namespace Service::NVFlinger { | ||
| 14 | class BufferQueue; | ||
| 15 | } | ||
| 16 | |||
| 17 | namespace Service::VI { | ||
| 18 | |||
| 19 | class Layer; | ||
| 20 | |||
| 21 | /// Represents a single display type | ||
| 22 | class Display { | ||
| 23 | public: | ||
| 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 | |||
| 90 | private: | ||
| 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 | |||
| 7 | namespace Service::VI { | ||
| 8 | |||
| 9 | Layer::Layer(u64 id, NVFlinger::BufferQueue& queue) : id{id}, buffer_queue{queue} {} | ||
| 10 | |||
| 11 | Layer::~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 | |||
| 9 | namespace Service::NVFlinger { | ||
| 10 | class BufferQueue; | ||
| 11 | } | ||
| 12 | |||
| 13 | namespace Service::VI { | ||
| 14 | |||
| 15 | /// Represents a single display layer. | ||
| 16 | class Layer { | ||
| 17 | public: | ||
| 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 | |||
| 47 | private: | ||
| 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 | ||
| 31 | class ScopeInit final { | 31 | struct ScopeInit final { |
| 32 | public: | ||
| 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 | ||
| 41 | static void AdvanceAndCheck(u32 idx, int downcount, int expected_lateness = 0, | 42 | static 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 | ||
| 55 | TEST_CASE("CoreTiming[BasicOrder]", "[core]") { | 56 | TEST_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 | ||
| 86 | TEST_CASE("CoreTiming[Threadsave]", "[core]") { | 88 | TEST_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 | ||
| 127 | namespace SharedSlotTest { | 130 | namespace 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 | ||
| 171 | TEST_CASE("Core::Timing[PredictableLateness]", "[core]") { | 175 | TEST_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 | ||
| 187 | namespace ChainSchedulingTest { | 192 | namespace ChainSchedulingTest { |
| 188 | static int reschedules = 0; | 193 | static int reschedules = 0; |
| 189 | 194 | ||
| 190 | static void RescheduleCallback(u64 userdata, s64 cycles_late) { | 195 | static 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 | ||
| 104 | if (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) | ||
| 118 | endif() | ||
| 119 | |||
| 104 | create_target_directory_groups(video_core) | 120 | create_target_directory_groups(video_core) |
| 105 | 121 | ||
| 106 | target_link_libraries(video_core PUBLIC common core) | 122 | target_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 | ||
| 35 | bool DmaPusher::Step() { | 35 | bool 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 | ||
| 12 | namespace Tegra::Engines { | 13 | namespace Tegra::Engines { |
| 13 | 14 | ||
| 14 | KeplerMemory::KeplerMemory(VideoCore::RasterizerInterface& rasterizer, | 15 | KeplerMemory::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 | ||
| 18 | KeplerMemory::~KeplerMemory() = default; | 19 | KeplerMemory::~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 | ||
| 14 | namespace Core { | ||
| 15 | class System; | ||
| 16 | } | ||
| 17 | |||
| 15 | namespace VideoCore { | 18 | namespace VideoCore { |
| 16 | class RasterizerInterface; | 19 | class RasterizerInterface; |
| 17 | } | 20 | } |
| @@ -23,7 +26,8 @@ namespace Tegra::Engines { | |||
| 23 | 26 | ||
| 24 | class KeplerMemory final { | 27 | class KeplerMemory final { |
| 25 | public: | 28 | public: |
| 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 | ||
| 78 | private: | 82 | private: |
| 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. |
| 20 | constexpr u32 MacroRegistersStart = 0xE00; | 20 | constexpr u32 MacroRegistersStart = 0xE00; |
| 21 | 21 | ||
| 22 | Maxwell3D::Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager) | 22 | Maxwell3D::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 | ||
| 105 | void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) { | 107 | void 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 | ||
| 20 | namespace Core { | ||
| 21 | class System; | ||
| 22 | } | ||
| 23 | |||
| 20 | namespace VideoCore { | 24 | namespace VideoCore { |
| 21 | class RasterizerInterface; | 25 | class RasterizerInterface; |
| 22 | } | 26 | } |
| @@ -28,7 +32,8 @@ namespace Tegra::Engines { | |||
| 28 | 32 | ||
| 29 | class Maxwell3D final { | 33 | class Maxwell3D final { |
| 30 | public: | 34 | public: |
| 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: | |||
| 1131 | private: | 1136 | private: |
| 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 | ||
| 12 | namespace Tegra::Engines { | 13 | namespace Tegra::Engines { |
| 13 | 14 | ||
| 14 | MaxwellDMA::MaxwellDMA(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager) | 15 | MaxwellDMA::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 | ||
| 17 | void MaxwellDMA::CallMethod(const GPU::MethodCall& method_call) { | 19 | void 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 | ||
| 14 | namespace Core { | ||
| 15 | class System; | ||
| 16 | } | ||
| 17 | |||
| 15 | namespace VideoCore { | 18 | namespace VideoCore { |
| 16 | class RasterizerInterface; | 19 | class RasterizerInterface; |
| 17 | } | 20 | } |
| @@ -20,7 +23,8 @@ namespace Tegra::Engines { | |||
| 20 | 23 | ||
| 21 | class MaxwellDMA final { | 24 | class MaxwellDMA final { |
| 22 | public: | 25 | public: |
| 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 | ||
| 139 | private: | 143 | private: |
| 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 | ||
| 30 | GPU::GPU(VideoCore::RasterizerInterface& rasterizer) { | 31 | GPU::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 | ||
| 40 | GPU::~GPU() = default; | 41 | GPU::~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 | ||
| 14 | namespace Core { | ||
| 15 | class System; | ||
| 16 | } | ||
| 17 | |||
| 15 | namespace VideoCore { | 18 | namespace VideoCore { |
| 16 | class RasterizerInterface; | 19 | class RasterizerInterface; |
| 17 | } | 20 | } |
| @@ -118,7 +121,7 @@ enum class EngineID { | |||
| 118 | 121 | ||
| 119 | class GPU final { | 122 | class GPU final { |
| 120 | public: | 123 | public: |
| 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; | |||
| 36 | using ComponentType = VideoCore::Surface::ComponentType; | 36 | using ComponentType = VideoCore::Surface::ComponentType; |
| 37 | 37 | ||
| 38 | struct SurfaceParams { | 38 | struct 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 @@ | |||
| 11 | namespace OpenGL { | 11 | namespace OpenGL { |
| 12 | 12 | ||
| 13 | OpenGLState OpenGLState::cur_state; | 13 | OpenGLState OpenGLState::cur_state; |
| 14 | |||
| 14 | bool OpenGLState::s_rgb_used; | 15 | bool OpenGLState::s_rgb_used; |
| 16 | |||
| 15 | OpenGLState::OpenGLState() { | 17 | OpenGLState::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 | ||
| 114 | void OpenGLState::ApplySRgb() const { | 116 | void 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 | ||
| 127 | void OpenGLState::ApplyCulling() const { | 128 | void 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 | ||
| 174 | void OpenGLState::ApplyDepth() const { | 172 | void 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 | ||
| 194 | void OpenGLState::ApplyPrimitiveRestart() const { | 190 | void 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 | ||
| 211 | void OpenGLState::ApplyStencilTest() const { | 204 | void 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 |
| 243 | void OpenGLState::EmulateViewportWithScissor() { | 232 | void 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 { | |||
| 327 | void OpenGLState::ApplyGlobalBlending() const { | 316 | void 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 { | |||
| 354 | void OpenGLState::ApplyTargetBlending(std::size_t target, bool force) const { | 341 | void 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 | ||
| 399 | void OpenGLState::ApplyLogicOp() const { | 382 | void 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 | ||
| 415 | void OpenGLState::ApplyPolygonOffset() const { | 396 | void 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 | |||
| 9 | namespace Vulkan { | ||
| 10 | |||
| 11 | // vulkan.hpp unique handlers use DispatchLoaderStatic | ||
| 12 | template <typename T> | ||
| 13 | using UniqueHandle = vk::UniqueHandle<T, vk::DispatchLoaderDynamic>; | ||
| 14 | |||
| 15 | using UniqueAccelerationStructureNV = UniqueHandle<vk::AccelerationStructureNV>; | ||
| 16 | using UniqueBuffer = UniqueHandle<vk::Buffer>; | ||
| 17 | using UniqueBufferView = UniqueHandle<vk::BufferView>; | ||
| 18 | using UniqueCommandBuffer = UniqueHandle<vk::CommandBuffer>; | ||
| 19 | using UniqueCommandPool = UniqueHandle<vk::CommandPool>; | ||
| 20 | using UniqueDescriptorPool = UniqueHandle<vk::DescriptorPool>; | ||
| 21 | using UniqueDescriptorSet = UniqueHandle<vk::DescriptorSet>; | ||
| 22 | using UniqueDescriptorSetLayout = UniqueHandle<vk::DescriptorSetLayout>; | ||
| 23 | using UniqueDescriptorUpdateTemplate = UniqueHandle<vk::DescriptorUpdateTemplate>; | ||
| 24 | using UniqueDevice = UniqueHandle<vk::Device>; | ||
| 25 | using UniqueDeviceMemory = UniqueHandle<vk::DeviceMemory>; | ||
| 26 | using UniqueEvent = UniqueHandle<vk::Event>; | ||
| 27 | using UniqueFence = UniqueHandle<vk::Fence>; | ||
| 28 | using UniqueFramebuffer = UniqueHandle<vk::Framebuffer>; | ||
| 29 | using UniqueImage = UniqueHandle<vk::Image>; | ||
| 30 | using UniqueImageView = UniqueHandle<vk::ImageView>; | ||
| 31 | using UniqueIndirectCommandsLayoutNVX = UniqueHandle<vk::IndirectCommandsLayoutNVX>; | ||
| 32 | using UniqueObjectTableNVX = UniqueHandle<vk::ObjectTableNVX>; | ||
| 33 | using UniquePipeline = UniqueHandle<vk::Pipeline>; | ||
| 34 | using UniquePipelineCache = UniqueHandle<vk::PipelineCache>; | ||
| 35 | using UniquePipelineLayout = UniqueHandle<vk::PipelineLayout>; | ||
| 36 | using UniqueQueryPool = UniqueHandle<vk::QueryPool>; | ||
| 37 | using UniqueRenderPass = UniqueHandle<vk::RenderPass>; | ||
| 38 | using UniqueSampler = UniqueHandle<vk::Sampler>; | ||
| 39 | using UniqueSamplerYcbcrConversion = UniqueHandle<vk::SamplerYcbcrConversion>; | ||
| 40 | using UniqueSemaphore = UniqueHandle<vk::Semaphore>; | ||
| 41 | using UniqueShaderModule = UniqueHandle<vk::ShaderModule>; | ||
| 42 | using UniqueSwapchainKHR = UniqueHandle<vk::SwapchainKHR>; | ||
| 43 | using 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 | |||
| 13 | namespace Vulkan { | ||
| 14 | |||
| 15 | namespace Alternatives { | ||
| 16 | |||
| 17 | constexpr std::array<vk::Format, 3> Depth24UnormS8Uint = { | ||
| 18 | vk::Format::eD32SfloatS8Uint, vk::Format::eD16UnormS8Uint, {}}; | ||
| 19 | constexpr std::array<vk::Format, 3> Depth16UnormS8Uint = { | ||
| 20 | vk::Format::eD24UnormS8Uint, vk::Format::eD32SfloatS8Uint, {}}; | ||
| 21 | |||
| 22 | } // namespace Alternatives | ||
| 23 | |||
| 24 | constexpr 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 | |||
| 35 | constexpr 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 | |||
| 49 | VKDevice::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 | |||
| 56 | VKDevice::~VKDevice() = default; | ||
| 57 | |||
| 58 | bool 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 | |||
| 81 | vk::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 | |||
| 121 | bool 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 | |||
| 134 | bool 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 | |||
| 173 | void 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 | |||
| 196 | void 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 | |||
| 202 | std::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 | |||
| 214 | std::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 | |||
| 12 | namespace Vulkan { | ||
| 13 | |||
| 14 | /// Format usage descriptor | ||
| 15 | enum class FormatType { Linear, Optimal, Buffer }; | ||
| 16 | |||
| 17 | /// Handles data specific to a physical device. | ||
| 18 | class VKDevice final { | ||
| 19 | public: | ||
| 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 | |||
| 86 | private: | ||
| 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 | |||
| 17 | namespace Vulkan { | ||
| 18 | |||
| 19 | // TODO(Rodrigo): Fine tune this number | ||
| 20 | constexpr u64 ALLOC_CHUNK_SIZE = 64 * 1024 * 1024; | ||
| 21 | |||
| 22 | class VKMemoryAllocation final { | ||
| 23 | public: | ||
| 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 | |||
| 85 | private: | ||
| 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 | |||
| 134 | VKMemoryManager::VKMemoryManager(const VKDevice& device) | ||
| 135 | : device{device}, props{device.GetPhysical().getMemoryProperties(device.GetDispatchLoader())}, | ||
| 136 | is_memory_unified{GetMemoryUnified(props)} {} | ||
| 137 | |||
| 138 | VKMemoryManager::~VKMemoryManager() = default; | ||
| 139 | |||
| 140 | VKMemoryCommit 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 | |||
| 180 | VKMemoryCommit 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 | |||
| 189 | VKMemoryCommit 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 | |||
| 198 | bool 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 | |||
| 239 | VKMemoryCommitImpl::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 | |||
| 243 | VKMemoryCommitImpl::~VKMemoryCommitImpl() { | ||
| 244 | allocation->Free(this); | ||
| 245 | } | ||
| 246 | |||
| 247 | u8* 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 | |||
| 13 | namespace Vulkan { | ||
| 14 | |||
| 15 | class VKDevice; | ||
| 16 | class VKMemoryAllocation; | ||
| 17 | class VKMemoryCommitImpl; | ||
| 18 | |||
| 19 | using VKMemoryCommit = std::unique_ptr<VKMemoryCommitImpl>; | ||
| 20 | |||
| 21 | class VKMemoryManager final { | ||
| 22 | public: | ||
| 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 | |||
| 46 | private: | ||
| 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 | |||
| 59 | class VKMemoryCommitImpl final { | ||
| 60 | friend VKMemoryAllocation; | ||
| 61 | |||
| 62 | public: | ||
| 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 | |||
| 80 | private: | ||
| 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 | |||
| 13 | namespace Vulkan { | ||
| 14 | |||
| 15 | // TODO(Rodrigo): Fine tune these numbers. | ||
| 16 | constexpr std::size_t COMMAND_BUFFER_POOL_SIZE = 0x1000; | ||
| 17 | constexpr std::size_t FENCES_GROW_STEP = 0x40; | ||
| 18 | |||
| 19 | class CommandBufferPool final : public VKFencedPool { | ||
| 20 | public: | ||
| 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 | |||
| 54 | private: | ||
| 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 | |||
| 65 | VKResource::VKResource() = default; | ||
| 66 | |||
| 67 | VKResource::~VKResource() = default; | ||
| 68 | |||
| 69 | VKFence::VKFence(const VKDevice& device, UniqueFence handle) | ||
| 70 | : device{device}, handle{std::move(handle)} {} | ||
| 71 | |||
| 72 | VKFence::~VKFence() = default; | ||
| 73 | |||
| 74 | void 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 | |||
| 80 | void VKFence::Release() { | ||
| 81 | is_owned = false; | ||
| 82 | } | ||
| 83 | |||
| 84 | void VKFence::Commit() { | ||
| 85 | is_owned = true; | ||
| 86 | is_used = true; | ||
| 87 | } | ||
| 88 | |||
| 89 | bool 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 | |||
| 124 | void VKFence::Protect(VKResource* resource) { | ||
| 125 | protected_resources.push_back(resource); | ||
| 126 | } | ||
| 127 | |||
| 128 | void 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 | |||
| 135 | VKFenceWatch::VKFenceWatch() = default; | ||
| 136 | |||
| 137 | VKFenceWatch::~VKFenceWatch() { | ||
| 138 | if (fence) { | ||
| 139 | fence->Unprotect(this); | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | void VKFenceWatch::Wait() { | ||
| 144 | if (!fence) { | ||
| 145 | return; | ||
| 146 | } | ||
| 147 | fence->Wait(); | ||
| 148 | fence->Unprotect(this); | ||
| 149 | fence = nullptr; | ||
| 150 | } | ||
| 151 | |||
| 152 | void VKFenceWatch::Watch(VKFence& new_fence) { | ||
| 153 | Wait(); | ||
| 154 | fence = &new_fence; | ||
| 155 | fence->Protect(this); | ||
| 156 | } | ||
| 157 | |||
| 158 | bool 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 | |||
| 167 | void VKFenceWatch::OnFenceRemoval(VKFence* signaling_fence) { | ||
| 168 | ASSERT_MSG(signaling_fence == fence, "Removing the wrong fence"); | ||
| 169 | fence = nullptr; | ||
| 170 | } | ||
| 171 | |||
| 172 | VKFencedPool::VKFencedPool(std::size_t grow_step) : grow_step{grow_step} {} | ||
| 173 | |||
| 174 | VKFencedPool::~VKFencedPool() = default; | ||
| 175 | |||
| 176 | std::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 | |||
| 205 | std::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 | |||
| 214 | void 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 | |||
| 222 | VKResourceManager::VKResourceManager(const VKDevice& device) : device{device} { | ||
| 223 | GrowFences(FENCES_GROW_STEP); | ||
| 224 | command_buffer_pool = std::make_unique<CommandBufferPool>(device); | ||
| 225 | } | ||
| 226 | |||
| 227 | VKResourceManager::~VKResourceManager() = default; | ||
| 228 | |||
| 229 | VKFence& 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 | |||
| 268 | vk::CommandBuffer VKResourceManager::CommitCommandBuffer(VKFence& fence) { | ||
| 269 | return command_buffer_pool->Commit(fence); | ||
| 270 | } | ||
| 271 | |||
| 272 | void 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 | |||
| 12 | namespace Vulkan { | ||
| 13 | |||
| 14 | class VKDevice; | ||
| 15 | class VKFence; | ||
| 16 | class VKResourceManager; | ||
| 17 | |||
| 18 | class CommandBufferPool; | ||
| 19 | |||
| 20 | /// Interface for a Vulkan resource | ||
| 21 | class VKResource { | ||
| 22 | public: | ||
| 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 | */ | ||
| 41 | class VKFence { | ||
| 42 | friend class VKResourceManager; | ||
| 43 | |||
| 44 | public: | ||
| 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 | |||
| 73 | private: | ||
| 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 | */ | ||
| 97 | class VKFenceWatch final : public VKResource { | ||
| 98 | public: | ||
| 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 | |||
| 119 | private: | ||
| 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 | */ | ||
| 127 | class VKFencedPool { | ||
| 128 | public: | ||
| 129 | explicit VKFencedPool(std::size_t grow_step); | ||
| 130 | virtual ~VKFencedPool(); | ||
| 131 | |||
| 132 | protected: | ||
| 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 | |||
| 143 | private: | ||
| 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 | */ | ||
| 159 | class VKResourceManager final { | ||
| 160 | public: | ||
| 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 | |||
| 170 | private: | ||
| 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 | |||
| 12 | namespace Vulkan { | ||
| 13 | |||
| 14 | VKScheduler::VKScheduler(const VKDevice& device, VKResourceManager& resource_manager) | ||
| 15 | : device{device}, resource_manager{resource_manager} { | ||
| 16 | next_fence = &resource_manager.CommitFence(); | ||
| 17 | AllocateNewContext(); | ||
| 18 | } | ||
| 19 | |||
| 20 | VKScheduler::~VKScheduler() = default; | ||
| 21 | |||
| 22 | VKExecutionContext VKScheduler::GetExecutionContext() const { | ||
| 23 | return VKExecutionContext(current_fence, current_cmdbuf); | ||
| 24 | } | ||
| 25 | |||
| 26 | VKExecutionContext VKScheduler::Flush(vk::Semaphore semaphore) { | ||
| 27 | SubmitExecution(semaphore); | ||
| 28 | current_fence->Release(); | ||
| 29 | AllocateNewContext(); | ||
| 30 | return GetExecutionContext(); | ||
| 31 | } | ||
| 32 | |||
| 33 | VKExecutionContext VKScheduler::Finish(vk::Semaphore semaphore) { | ||
| 34 | SubmitExecution(semaphore); | ||
| 35 | current_fence->Wait(); | ||
| 36 | current_fence->Release(); | ||
| 37 | AllocateNewContext(); | ||
| 38 | return GetExecutionContext(); | ||
| 39 | } | ||
| 40 | |||
| 41 | void 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, ¤t_cmdbuf, semaphore ? 1u : 0u, | ||
| 47 | &semaphore); | ||
| 48 | queue.submit({submit_info}, *current_fence, dld); | ||
| 49 | } | ||
| 50 | |||
| 51 | void 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 | |||
| 10 | namespace Vulkan { | ||
| 11 | |||
| 12 | class VKDevice; | ||
| 13 | class VKExecutionContext; | ||
| 14 | class VKFence; | ||
| 15 | class 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. | ||
| 19 | class VKScheduler { | ||
| 20 | public: | ||
| 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 | |||
| 35 | private: | ||
| 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 | |||
| 47 | class VKExecutionContext { | ||
| 48 | friend class VKScheduler; | ||
| 49 | |||
| 50 | public: | ||
| 51 | VKExecutionContext() = default; | ||
| 52 | |||
| 53 | VKFence& GetFence() const { | ||
| 54 | return *fence; | ||
| 55 | } | ||
| 56 | |||
| 57 | vk::CommandBuffer GetCommandBuffer() const { | ||
| 58 | return cmdbuf; | ||
| 59 | } | ||
| 60 | |||
| 61 | private: | ||
| 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)); |