diff options
Diffstat (limited to 'src')
125 files changed, 2391 insertions, 1117 deletions
diff --git a/src/audio_core/device/audio_buffers.h b/src/audio_core/device/audio_buffers.h index 3dae1a3b7..15082f6c6 100644 --- a/src/audio_core/device/audio_buffers.h +++ b/src/audio_core/device/audio_buffers.h | |||
| @@ -91,9 +91,10 @@ public: | |||
| 91 | * @param core_timing - The CoreTiming instance | 91 | * @param core_timing - The CoreTiming instance |
| 92 | * @param session - The device session | 92 | * @param session - The device session |
| 93 | * | 93 | * |
| 94 | * @return Is the buffer was released. | 94 | * @return If any buffer was released. |
| 95 | */ | 95 | */ |
| 96 | bool ReleaseBuffers(const Core::Timing::CoreTiming& core_timing, const DeviceSession& session) { | 96 | bool ReleaseBuffers(const Core::Timing::CoreTiming& core_timing, const DeviceSession& session, |
| 97 | bool force) { | ||
| 97 | std::scoped_lock l{lock}; | 98 | std::scoped_lock l{lock}; |
| 98 | bool buffer_released{false}; | 99 | bool buffer_released{false}; |
| 99 | while (registered_count > 0) { | 100 | while (registered_count > 0) { |
| @@ -103,7 +104,8 @@ public: | |||
| 103 | } | 104 | } |
| 104 | 105 | ||
| 105 | // Check with the backend if this buffer can be released yet. | 106 | // Check with the backend if this buffer can be released yet. |
| 106 | if (!session.IsBufferConsumed(buffers[index])) { | 107 | // If we're shutting down, we don't care if it's been played or not. |
| 108 | if (!force && !session.IsBufferConsumed(buffers[index])) { | ||
| 107 | break; | 109 | break; |
| 108 | } | 110 | } |
| 109 | 111 | ||
diff --git a/src/audio_core/device/device_session.cpp b/src/audio_core/device/device_session.cpp index 995060414..5a327a606 100644 --- a/src/audio_core/device/device_session.cpp +++ b/src/audio_core/device/device_session.cpp | |||
| @@ -73,6 +73,12 @@ void DeviceSession::Stop() { | |||
| 73 | } | 73 | } |
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | void DeviceSession::ClearBuffers() { | ||
| 77 | if (stream) { | ||
| 78 | stream->ClearQueue(); | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 76 | void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) const { | 82 | void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) const { |
| 77 | for (const auto& buffer : buffers) { | 83 | for (const auto& buffer : buffers) { |
| 78 | Sink::SinkBuffer new_buffer{ | 84 | Sink::SinkBuffer new_buffer{ |
diff --git a/src/audio_core/device/device_session.h b/src/audio_core/device/device_session.h index 74f4dc085..75f766c68 100644 --- a/src/audio_core/device/device_session.h +++ b/src/audio_core/device/device_session.h | |||
| @@ -91,6 +91,11 @@ public: | |||
| 91 | void Stop(); | 91 | void Stop(); |
| 92 | 92 | ||
| 93 | /** | 93 | /** |
| 94 | * Clear out the underlying audio buffers in the backend stream. | ||
| 95 | */ | ||
| 96 | void ClearBuffers(); | ||
| 97 | |||
| 98 | /** | ||
| 94 | * Set this device session's volume. | 99 | * Set this device session's volume. |
| 95 | * | 100 | * |
| 96 | * @param volume - New volume for this session. | 101 | * @param volume - New volume for this session. |
diff --git a/src/audio_core/in/audio_in_system.cpp b/src/audio_core/in/audio_in_system.cpp index 4324cafd8..934ef8c1c 100644 --- a/src/audio_core/in/audio_in_system.cpp +++ b/src/audio_core/in/audio_in_system.cpp | |||
| @@ -23,7 +23,6 @@ System::~System() { | |||
| 23 | void System::Finalize() { | 23 | void System::Finalize() { |
| 24 | Stop(); | 24 | Stop(); |
| 25 | session->Finalize(); | 25 | session->Finalize(); |
| 26 | buffer_event->Signal(); | ||
| 27 | } | 26 | } |
| 28 | 27 | ||
| 29 | void System::StartSession() { | 28 | void System::StartSession() { |
| @@ -102,6 +101,10 @@ Result System::Stop() { | |||
| 102 | if (state == State::Started) { | 101 | if (state == State::Started) { |
| 103 | session->Stop(); | 102 | session->Stop(); |
| 104 | session->SetVolume(0.0f); | 103 | session->SetVolume(0.0f); |
| 104 | session->ClearBuffers(); | ||
| 105 | if (buffers.ReleaseBuffers(system.CoreTiming(), *session, true)) { | ||
| 106 | buffer_event->Signal(); | ||
| 107 | } | ||
| 105 | state = State::Stopped; | 108 | state = State::Stopped; |
| 106 | } | 109 | } |
| 107 | 110 | ||
| @@ -138,7 +141,7 @@ void System::RegisterBuffers() { | |||
| 138 | } | 141 | } |
| 139 | 142 | ||
| 140 | void System::ReleaseBuffers() { | 143 | void System::ReleaseBuffers() { |
| 141 | bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session)}; | 144 | bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session, false)}; |
| 142 | 145 | ||
| 143 | if (signal) { | 146 | if (signal) { |
| 144 | // Signal if any buffer was released, or if none are registered, we need more. | 147 | // Signal if any buffer was released, or if none are registered, we need more. |
diff --git a/src/audio_core/out/audio_out_system.cpp b/src/audio_core/out/audio_out_system.cpp index a66208ed9..e096a1dac 100644 --- a/src/audio_core/out/audio_out_system.cpp +++ b/src/audio_core/out/audio_out_system.cpp | |||
| @@ -24,7 +24,6 @@ System::~System() { | |||
| 24 | void System::Finalize() { | 24 | void System::Finalize() { |
| 25 | Stop(); | 25 | Stop(); |
| 26 | session->Finalize(); | 26 | session->Finalize(); |
| 27 | buffer_event->Signal(); | ||
| 28 | } | 27 | } |
| 29 | 28 | ||
| 30 | std::string_view System::GetDefaultOutputDeviceName() const { | 29 | std::string_view System::GetDefaultOutputDeviceName() const { |
| @@ -102,6 +101,10 @@ Result System::Stop() { | |||
| 102 | if (state == State::Started) { | 101 | if (state == State::Started) { |
| 103 | session->Stop(); | 102 | session->Stop(); |
| 104 | session->SetVolume(0.0f); | 103 | session->SetVolume(0.0f); |
| 104 | session->ClearBuffers(); | ||
| 105 | if (buffers.ReleaseBuffers(system.CoreTiming(), *session, true)) { | ||
| 106 | buffer_event->Signal(); | ||
| 107 | } | ||
| 105 | state = State::Stopped; | 108 | state = State::Stopped; |
| 106 | } | 109 | } |
| 107 | 110 | ||
| @@ -138,7 +141,7 @@ void System::RegisterBuffers() { | |||
| 138 | } | 141 | } |
| 139 | 142 | ||
| 140 | void System::ReleaseBuffers() { | 143 | void System::ReleaseBuffers() { |
| 141 | bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session)}; | 144 | bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session, false)}; |
| 142 | if (signal) { | 145 | if (signal) { |
| 143 | // Signal if any buffer was released, or if none are registered, we need more. | 146 | // Signal if any buffer was released, or if none are registered, we need more. |
| 144 | buffer_event->Signal(); | 147 | buffer_event->Signal(); |
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 25b22a281..eb05e46a8 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -78,6 +78,7 @@ add_library(common STATIC | |||
| 78 | logging/types.h | 78 | logging/types.h |
| 79 | lz4_compression.cpp | 79 | lz4_compression.cpp |
| 80 | lz4_compression.h | 80 | lz4_compression.h |
| 81 | make_unique_for_overwrite.h | ||
| 81 | math_util.h | 82 | math_util.h |
| 82 | memory_detect.cpp | 83 | memory_detect.cpp |
| 83 | memory_detect.h | 84 | memory_detect.h |
| @@ -101,6 +102,7 @@ add_library(common STATIC | |||
| 101 | ${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp | 102 | ${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp |
| 102 | scm_rev.h | 103 | scm_rev.h |
| 103 | scope_exit.h | 104 | scope_exit.h |
| 105 | scratch_buffer.h | ||
| 104 | settings.cpp | 106 | settings.cpp |
| 105 | settings.h | 107 | settings.h |
| 106 | settings_input.cpp | 108 | settings_input.cpp |
diff --git a/src/common/assert.h b/src/common/assert.h index 8c927fcc0..67e7e9375 100644 --- a/src/common/assert.h +++ b/src/common/assert.h | |||
| @@ -69,7 +69,7 @@ void assert_fail_impl(); | |||
| 69 | #define ASSERT_OR_EXECUTE(_a_, _b_) \ | 69 | #define ASSERT_OR_EXECUTE(_a_, _b_) \ |
| 70 | do { \ | 70 | do { \ |
| 71 | ASSERT(_a_); \ | 71 | ASSERT(_a_); \ |
| 72 | if (!(_a_)) { \ | 72 | if (!(_a_)) [[unlikely]] { \ |
| 73 | _b_ \ | 73 | _b_ \ |
| 74 | } \ | 74 | } \ |
| 75 | } while (0) | 75 | } while (0) |
| @@ -78,7 +78,7 @@ void assert_fail_impl(); | |||
| 78 | #define ASSERT_OR_EXECUTE_MSG(_a_, _b_, ...) \ | 78 | #define ASSERT_OR_EXECUTE_MSG(_a_, _b_, ...) \ |
| 79 | do { \ | 79 | do { \ |
| 80 | ASSERT_MSG(_a_, __VA_ARGS__); \ | 80 | ASSERT_MSG(_a_, __VA_ARGS__); \ |
| 81 | if (!(_a_)) { \ | 81 | if (!(_a_)) [[unlikely]] { \ |
| 82 | _b_ \ | 82 | _b_ \ |
| 83 | } \ | 83 | } \ |
| 84 | } while (0) | 84 | } while (0) |
diff --git a/src/common/make_unique_for_overwrite.h b/src/common/make_unique_for_overwrite.h new file mode 100644 index 000000000..c7413cf51 --- /dev/null +++ b/src/common/make_unique_for_overwrite.h | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <memory> | ||
| 7 | #include <type_traits> | ||
| 8 | |||
| 9 | namespace Common { | ||
| 10 | |||
| 11 | template <class T> | ||
| 12 | requires(!std::is_array_v<T>) std::unique_ptr<T> make_unique_for_overwrite() { | ||
| 13 | return std::unique_ptr<T>(new T); | ||
| 14 | } | ||
| 15 | |||
| 16 | template <class T> | ||
| 17 | requires std::is_unbounded_array_v<T> std::unique_ptr<T> make_unique_for_overwrite(std::size_t n) { | ||
| 18 | return std::unique_ptr<T>(new std::remove_extent_t<T>[n]); | ||
| 19 | } | ||
| 20 | |||
| 21 | template <class T, class... Args> | ||
| 22 | requires std::is_bounded_array_v<T> | ||
| 23 | void make_unique_for_overwrite(Args&&...) = delete; | ||
| 24 | |||
| 25 | } // namespace Common | ||
diff --git a/src/common/scratch_buffer.h b/src/common/scratch_buffer.h new file mode 100644 index 000000000..1245a5086 --- /dev/null +++ b/src/common/scratch_buffer.h | |||
| @@ -0,0 +1,95 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/make_unique_for_overwrite.h" | ||
| 7 | |||
| 8 | namespace Common { | ||
| 9 | |||
| 10 | /** | ||
| 11 | * ScratchBuffer class | ||
| 12 | * This class creates a default initialized heap allocated buffer for cases such as intermediate | ||
| 13 | * buffers being copied into entirely, where value initializing members during allocation or resize | ||
| 14 | * is redundant. | ||
| 15 | */ | ||
| 16 | template <typename T> | ||
| 17 | class ScratchBuffer { | ||
| 18 | public: | ||
| 19 | ScratchBuffer() = default; | ||
| 20 | |||
| 21 | explicit ScratchBuffer(size_t initial_capacity) | ||
| 22 | : last_requested_size{initial_capacity}, buffer_capacity{initial_capacity}, | ||
| 23 | buffer{Common::make_unique_for_overwrite<T[]>(initial_capacity)} {} | ||
| 24 | |||
| 25 | ~ScratchBuffer() = default; | ||
| 26 | |||
| 27 | /// This will only grow the buffer's capacity if size is greater than the current capacity. | ||
| 28 | /// The previously held data will remain intact. | ||
| 29 | void resize(size_t size) { | ||
| 30 | if (size > buffer_capacity) { | ||
| 31 | auto new_buffer = Common::make_unique_for_overwrite<T[]>(size); | ||
| 32 | std::move(buffer.get(), buffer.get() + buffer_capacity, new_buffer.get()); | ||
| 33 | buffer = std::move(new_buffer); | ||
| 34 | buffer_capacity = size; | ||
| 35 | } | ||
| 36 | last_requested_size = size; | ||
| 37 | } | ||
| 38 | |||
| 39 | /// This will only grow the buffer's capacity if size is greater than the current capacity. | ||
| 40 | /// The previously held data will be destroyed if a reallocation occurs. | ||
| 41 | void resize_destructive(size_t size) { | ||
| 42 | if (size > buffer_capacity) { | ||
| 43 | buffer_capacity = size; | ||
| 44 | buffer = Common::make_unique_for_overwrite<T[]>(buffer_capacity); | ||
| 45 | } | ||
| 46 | last_requested_size = size; | ||
| 47 | } | ||
| 48 | |||
| 49 | [[nodiscard]] T* data() noexcept { | ||
| 50 | return buffer.get(); | ||
| 51 | } | ||
| 52 | |||
| 53 | [[nodiscard]] const T* data() const noexcept { | ||
| 54 | return buffer.get(); | ||
| 55 | } | ||
| 56 | |||
| 57 | [[nodiscard]] T* begin() noexcept { | ||
| 58 | return data(); | ||
| 59 | } | ||
| 60 | |||
| 61 | [[nodiscard]] const T* begin() const noexcept { | ||
| 62 | return data(); | ||
| 63 | } | ||
| 64 | |||
| 65 | [[nodiscard]] T* end() noexcept { | ||
| 66 | return data() + last_requested_size; | ||
| 67 | } | ||
| 68 | |||
| 69 | [[nodiscard]] const T* end() const noexcept { | ||
| 70 | return data() + last_requested_size; | ||
| 71 | } | ||
| 72 | |||
| 73 | [[nodiscard]] T& operator[](size_t i) { | ||
| 74 | return buffer[i]; | ||
| 75 | } | ||
| 76 | |||
| 77 | [[nodiscard]] const T& operator[](size_t i) const { | ||
| 78 | return buffer[i]; | ||
| 79 | } | ||
| 80 | |||
| 81 | [[nodiscard]] size_t size() const noexcept { | ||
| 82 | return last_requested_size; | ||
| 83 | } | ||
| 84 | |||
| 85 | [[nodiscard]] size_t capacity() const noexcept { | ||
| 86 | return buffer_capacity; | ||
| 87 | } | ||
| 88 | |||
| 89 | private: | ||
| 90 | size_t last_requested_size{}; | ||
| 91 | size_t buffer_capacity{}; | ||
| 92 | std::unique_ptr<T[]> buffer{}; | ||
| 93 | }; | ||
| 94 | |||
| 95 | } // namespace Common | ||
diff --git a/src/common/settings.cpp b/src/common/settings.cpp index d8ffe34c3..149e621f9 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp | |||
| @@ -40,6 +40,7 @@ void LogSettings() { | |||
| 40 | LOG_INFO(Config, "yuzu Configuration:"); | 40 | LOG_INFO(Config, "yuzu Configuration:"); |
| 41 | log_setting("Controls_UseDockedMode", values.use_docked_mode.GetValue()); | 41 | log_setting("Controls_UseDockedMode", values.use_docked_mode.GetValue()); |
| 42 | log_setting("System_RngSeed", values.rng_seed.GetValue().value_or(0)); | 42 | log_setting("System_RngSeed", values.rng_seed.GetValue().value_or(0)); |
| 43 | log_setting("System_DeviceName", values.device_name.GetValue()); | ||
| 43 | log_setting("System_CurrentUser", values.current_user.GetValue()); | 44 | log_setting("System_CurrentUser", values.current_user.GetValue()); |
| 44 | log_setting("System_LanguageIndex", values.language_index.GetValue()); | 45 | log_setting("System_LanguageIndex", values.language_index.GetValue()); |
| 45 | log_setting("System_RegionIndex", values.region_index.GetValue()); | 46 | log_setting("System_RegionIndex", values.region_index.GetValue()); |
diff --git a/src/common/settings.h b/src/common/settings.h index 7ce9ea23c..6b199af93 100644 --- a/src/common/settings.h +++ b/src/common/settings.h | |||
| @@ -458,6 +458,7 @@ struct Values { | |||
| 458 | 458 | ||
| 459 | // System | 459 | // System |
| 460 | SwitchableSetting<std::optional<u32>> rng_seed{std::optional<u32>(), "rng_seed"}; | 460 | SwitchableSetting<std::optional<u32>> rng_seed{std::optional<u32>(), "rng_seed"}; |
| 461 | Setting<std::string> device_name{"Yuzu", "device_name"}; | ||
| 461 | // Measured in seconds since epoch | 462 | // Measured in seconds since epoch |
| 462 | std::optional<s64> custom_rtc; | 463 | std::optional<s64> custom_rtc; |
| 463 | // Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc` | 464 | // Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc` |
diff --git a/src/common/thread.h b/src/common/thread.h index e17a7850f..8ae169b4e 100644 --- a/src/common/thread.h +++ b/src/common/thread.h | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #include <mutex> | 11 | #include <mutex> |
| 12 | #include <thread> | 12 | #include <thread> |
| 13 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 14 | #include "common/polyfill_thread.h" | ||
| 14 | 15 | ||
| 15 | namespace Common { | 16 | namespace Common { |
| 16 | 17 | ||
| @@ -69,7 +70,7 @@ public: | |||
| 69 | explicit Barrier(std::size_t count_) : count(count_) {} | 70 | explicit Barrier(std::size_t count_) : count(count_) {} |
| 70 | 71 | ||
| 71 | /// Blocks until all "count" threads have called Sync() | 72 | /// Blocks until all "count" threads have called Sync() |
| 72 | void Sync() { | 73 | bool Sync(std::stop_token token = {}) { |
| 73 | std::unique_lock lk{mutex}; | 74 | std::unique_lock lk{mutex}; |
| 74 | const std::size_t current_generation = generation; | 75 | const std::size_t current_generation = generation; |
| 75 | 76 | ||
| @@ -77,14 +78,16 @@ public: | |||
| 77 | generation++; | 78 | generation++; |
| 78 | waiting = 0; | 79 | waiting = 0; |
| 79 | condvar.notify_all(); | 80 | condvar.notify_all(); |
| 81 | return true; | ||
| 80 | } else { | 82 | } else { |
| 81 | condvar.wait(lk, | 83 | CondvarWait(condvar, lk, token, |
| 82 | [this, current_generation] { return current_generation != generation; }); | 84 | [this, current_generation] { return current_generation != generation; }); |
| 85 | return !token.stop_requested(); | ||
| 83 | } | 86 | } |
| 84 | } | 87 | } |
| 85 | 88 | ||
| 86 | private: | 89 | private: |
| 87 | std::condition_variable condvar; | 90 | std::condition_variable_any condvar; |
| 88 | std::mutex mutex; | 91 | std::mutex mutex; |
| 89 | std::size_t count; | 92 | std::size_t count; |
| 90 | std::size_t waiting = 0; | 93 | std::size_t waiting = 0; |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c6b5ac196..5afdeb5ff 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -201,6 +201,9 @@ add_library(core STATIC | |||
| 201 | hle/kernel/k_event_info.h | 201 | hle/kernel/k_event_info.h |
| 202 | hle/kernel/k_handle_table.cpp | 202 | hle/kernel/k_handle_table.cpp |
| 203 | hle/kernel/k_handle_table.h | 203 | hle/kernel/k_handle_table.h |
| 204 | hle/kernel/k_hardware_timer_base.h | ||
| 205 | hle/kernel/k_hardware_timer.cpp | ||
| 206 | hle/kernel/k_hardware_timer.h | ||
| 204 | hle/kernel/k_interrupt_manager.cpp | 207 | hle/kernel/k_interrupt_manager.cpp |
| 205 | hle/kernel/k_interrupt_manager.h | 208 | hle/kernel/k_interrupt_manager.h |
| 206 | hle/kernel/k_light_condition_variable.cpp | 209 | hle/kernel/k_light_condition_variable.cpp |
| @@ -223,6 +226,7 @@ add_library(core STATIC | |||
| 223 | hle/kernel/k_page_buffer.h | 226 | hle/kernel/k_page_buffer.h |
| 224 | hle/kernel/k_page_heap.cpp | 227 | hle/kernel/k_page_heap.cpp |
| 225 | hle/kernel/k_page_heap.h | 228 | hle/kernel/k_page_heap.h |
| 229 | hle/kernel/k_page_group.cpp | ||
| 226 | hle/kernel/k_page_group.h | 230 | hle/kernel/k_page_group.h |
| 227 | hle/kernel/k_page_table.cpp | 231 | hle/kernel/k_page_table.cpp |
| 228 | hle/kernel/k_page_table.h | 232 | hle/kernel/k_page_table.h |
| @@ -268,6 +272,7 @@ add_library(core STATIC | |||
| 268 | hle/kernel/k_thread_local_page.h | 272 | hle/kernel/k_thread_local_page.h |
| 269 | hle/kernel/k_thread_queue.cpp | 273 | hle/kernel/k_thread_queue.cpp |
| 270 | hle/kernel/k_thread_queue.h | 274 | hle/kernel/k_thread_queue.h |
| 275 | hle/kernel/k_timer_task.h | ||
| 271 | hle/kernel/k_trace.h | 276 | hle/kernel/k_trace.h |
| 272 | hle/kernel/k_transfer_memory.cpp | 277 | hle/kernel/k_transfer_memory.cpp |
| 273 | hle/kernel/k_transfer_memory.h | 278 | hle/kernel/k_transfer_memory.h |
| @@ -290,8 +295,6 @@ add_library(core STATIC | |||
| 290 | hle/kernel/svc_common.h | 295 | hle/kernel/svc_common.h |
| 291 | hle/kernel/svc_types.h | 296 | hle/kernel/svc_types.h |
| 292 | hle/kernel/svc_wrap.h | 297 | hle/kernel/svc_wrap.h |
| 293 | hle/kernel/time_manager.cpp | ||
| 294 | hle/kernel/time_manager.h | ||
| 295 | hle/result.h | 298 | hle/result.h |
| 296 | hle/service/acc/acc.cpp | 299 | hle/service/acc/acc.cpp |
| 297 | hle/service/acc/acc.h | 300 | hle/service/acc/acc.h |
diff --git a/src/core/core.cpp b/src/core/core.cpp index 94d4e2212..47292cd78 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -183,26 +183,20 @@ struct System::Impl { | |||
| 183 | Initialize(system); | 183 | Initialize(system); |
| 184 | } | 184 | } |
| 185 | 185 | ||
| 186 | SystemResultStatus Run() { | 186 | void Run() { |
| 187 | std::unique_lock<std::mutex> lk(suspend_guard); | 187 | std::unique_lock<std::mutex> lk(suspend_guard); |
| 188 | status = SystemResultStatus::Success; | ||
| 189 | 188 | ||
| 190 | kernel.Suspend(false); | 189 | kernel.Suspend(false); |
| 191 | core_timing.SyncPause(false); | 190 | core_timing.SyncPause(false); |
| 192 | is_paused.store(false, std::memory_order_relaxed); | 191 | is_paused.store(false, std::memory_order_relaxed); |
| 193 | |||
| 194 | return status; | ||
| 195 | } | 192 | } |
| 196 | 193 | ||
| 197 | SystemResultStatus Pause() { | 194 | void Pause() { |
| 198 | std::unique_lock<std::mutex> lk(suspend_guard); | 195 | std::unique_lock<std::mutex> lk(suspend_guard); |
| 199 | status = SystemResultStatus::Success; | ||
| 200 | 196 | ||
| 201 | core_timing.SyncPause(true); | 197 | core_timing.SyncPause(true); |
| 202 | kernel.Suspend(true); | 198 | kernel.Suspend(true); |
| 203 | is_paused.store(true, std::memory_order_relaxed); | 199 | is_paused.store(true, std::memory_order_relaxed); |
| 204 | |||
| 205 | return status; | ||
| 206 | } | 200 | } |
| 207 | 201 | ||
| 208 | bool IsPaused() const { | 202 | bool IsPaused() const { |
| @@ -389,7 +383,9 @@ struct System::Impl { | |||
| 389 | kernel.ShutdownCores(); | 383 | kernel.ShutdownCores(); |
| 390 | cpu_manager.Shutdown(); | 384 | cpu_manager.Shutdown(); |
| 391 | debugger.reset(); | 385 | debugger.reset(); |
| 392 | services->KillNVNFlinger(); | 386 | if (services) { |
| 387 | services->KillNVNFlinger(); | ||
| 388 | } | ||
| 393 | kernel.CloseServices(); | 389 | kernel.CloseServices(); |
| 394 | services.reset(); | 390 | services.reset(); |
| 395 | service_manager.reset(); | 391 | service_manager.reset(); |
| @@ -551,12 +547,12 @@ void System::Initialize() { | |||
| 551 | impl->Initialize(*this); | 547 | impl->Initialize(*this); |
| 552 | } | 548 | } |
| 553 | 549 | ||
| 554 | SystemResultStatus System::Run() { | 550 | void System::Run() { |
| 555 | return impl->Run(); | 551 | impl->Run(); |
| 556 | } | 552 | } |
| 557 | 553 | ||
| 558 | SystemResultStatus System::Pause() { | 554 | void System::Pause() { |
| 559 | return impl->Pause(); | 555 | impl->Pause(); |
| 560 | } | 556 | } |
| 561 | 557 | ||
| 562 | bool System::IsPaused() const { | 558 | bool System::IsPaused() const { |
diff --git a/src/core/core.h b/src/core/core.h index 4ebedffd9..fb5cda2f5 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -152,13 +152,13 @@ public: | |||
| 152 | * Run the OS and Application | 152 | * Run the OS and Application |
| 153 | * This function will start emulation and run the relevant devices | 153 | * This function will start emulation and run the relevant devices |
| 154 | */ | 154 | */ |
| 155 | [[nodiscard]] SystemResultStatus Run(); | 155 | void Run(); |
| 156 | 156 | ||
| 157 | /** | 157 | /** |
| 158 | * Pause the OS and Application | 158 | * Pause the OS and Application |
| 159 | * This function will pause emulation and stop the relevant devices | 159 | * This function will pause emulation and stop the relevant devices |
| 160 | */ | 160 | */ |
| 161 | [[nodiscard]] SystemResultStatus Pause(); | 161 | void Pause(); |
| 162 | 162 | ||
| 163 | /// Check if the core is currently paused. | 163 | /// Check if the core is currently paused. |
| 164 | [[nodiscard]] bool IsPaused() const; | 164 | [[nodiscard]] bool IsPaused() const; |
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 0dd4c2196..04a11f444 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp | |||
| @@ -20,23 +20,20 @@ namespace Core { | |||
| 20 | CpuManager::CpuManager(System& system_) : system{system_} {} | 20 | CpuManager::CpuManager(System& system_) : system{system_} {} |
| 21 | CpuManager::~CpuManager() = default; | 21 | CpuManager::~CpuManager() = default; |
| 22 | 22 | ||
| 23 | void CpuManager::ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, | ||
| 24 | std::size_t core) { | ||
| 25 | cpu_manager.RunThread(core); | ||
| 26 | } | ||
| 27 | |||
| 28 | void CpuManager::Initialize() { | 23 | void CpuManager::Initialize() { |
| 29 | num_cores = is_multicore ? Core::Hardware::NUM_CPU_CORES : 1; | 24 | num_cores = is_multicore ? Core::Hardware::NUM_CPU_CORES : 1; |
| 30 | gpu_barrier = std::make_unique<Common::Barrier>(num_cores + 1); | 25 | gpu_barrier = std::make_unique<Common::Barrier>(num_cores + 1); |
| 31 | 26 | ||
| 32 | for (std::size_t core = 0; core < num_cores; core++) { | 27 | for (std::size_t core = 0; core < num_cores; core++) { |
| 33 | core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core); | 28 | core_data[core].host_thread = |
| 29 | std::jthread([this, core](std::stop_token token) { RunThread(token, core); }); | ||
| 34 | } | 30 | } |
| 35 | } | 31 | } |
| 36 | 32 | ||
| 37 | void CpuManager::Shutdown() { | 33 | void CpuManager::Shutdown() { |
| 38 | for (std::size_t core = 0; core < num_cores; core++) { | 34 | for (std::size_t core = 0; core < num_cores; core++) { |
| 39 | if (core_data[core].host_thread.joinable()) { | 35 | if (core_data[core].host_thread.joinable()) { |
| 36 | core_data[core].host_thread.request_stop(); | ||
| 40 | core_data[core].host_thread.join(); | 37 | core_data[core].host_thread.join(); |
| 41 | } | 38 | } |
| 42 | } | 39 | } |
| @@ -184,7 +181,7 @@ void CpuManager::ShutdownThread() { | |||
| 184 | UNREACHABLE(); | 181 | UNREACHABLE(); |
| 185 | } | 182 | } |
| 186 | 183 | ||
| 187 | void CpuManager::RunThread(std::size_t core) { | 184 | void CpuManager::RunThread(std::stop_token token, std::size_t core) { |
| 188 | /// Initialization | 185 | /// Initialization |
| 189 | system.RegisterCoreThread(core); | 186 | system.RegisterCoreThread(core); |
| 190 | std::string name; | 187 | std::string name; |
| @@ -206,7 +203,9 @@ void CpuManager::RunThread(std::size_t core) { | |||
| 206 | }); | 203 | }); |
| 207 | 204 | ||
| 208 | // Running | 205 | // Running |
| 209 | gpu_barrier->Sync(); | 206 | if (!gpu_barrier->Sync(token)) { |
| 207 | return; | ||
| 208 | } | ||
| 210 | 209 | ||
| 211 | if (!is_async_gpu && !is_multicore) { | 210 | if (!is_async_gpu && !is_multicore) { |
| 212 | system.GPU().ObtainContext(); | 211 | system.GPU().ObtainContext(); |
diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h index 374367468..0deea9c58 100644 --- a/src/core/cpu_manager.h +++ b/src/core/cpu_manager.h | |||
| @@ -81,12 +81,10 @@ private: | |||
| 81 | void SingleCoreRunGuestThread(); | 81 | void SingleCoreRunGuestThread(); |
| 82 | void SingleCoreRunIdleThread(); | 82 | void SingleCoreRunIdleThread(); |
| 83 | 83 | ||
| 84 | static void ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, std::size_t core); | ||
| 85 | |||
| 86 | void GuestActivate(); | 84 | void GuestActivate(); |
| 87 | void HandleInterrupt(); | 85 | void HandleInterrupt(); |
| 88 | void ShutdownThread(); | 86 | void ShutdownThread(); |
| 89 | void RunThread(std::size_t core); | 87 | void RunThread(std::stop_token stop_token, std::size_t core); |
| 90 | 88 | ||
| 91 | struct CoreData { | 89 | struct CoreData { |
| 92 | std::shared_ptr<Common::Fiber> host_context; | 90 | std::shared_ptr<Common::Fiber> host_context; |
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 95363b645..cf85ba29e 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h | |||
| @@ -131,6 +131,10 @@ public: | |||
| 131 | return active_config; | 131 | return active_config; |
| 132 | } | 132 | } |
| 133 | 133 | ||
| 134 | bool StrictContextRequired() const { | ||
| 135 | return strict_context_required; | ||
| 136 | } | ||
| 137 | |||
| 134 | /** | 138 | /** |
| 135 | * Requests the internal configuration to be replaced by the specified argument at some point in | 139 | * Requests the internal configuration to be replaced by the specified argument at some point in |
| 136 | * the future. | 140 | * the future. |
| @@ -207,6 +211,8 @@ protected: | |||
| 207 | 211 | ||
| 208 | WindowSystemInfo window_info; | 212 | WindowSystemInfo window_info; |
| 209 | 213 | ||
| 214 | bool strict_context_required = false; | ||
| 215 | |||
| 210 | private: | 216 | private: |
| 211 | /** | 217 | /** |
| 212 | * Handler called when the minimal client area was requested to be changed via SetConfig. | 218 | * Handler called when the minimal client area was requested to be changed via SetConfig. |
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 67969e938..5587ee097 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp | |||
| @@ -145,6 +145,7 @@ void EmulatedController::LoadDevices() { | |||
| 145 | output_params[3].Set("output", true); | 145 | output_params[3].Set("output", true); |
| 146 | 146 | ||
| 147 | LoadTASParams(); | 147 | LoadTASParams(); |
| 148 | LoadVirtualGamepadParams(); | ||
| 148 | 149 | ||
| 149 | std::ranges::transform(button_params, button_devices.begin(), Common::Input::CreateInputDevice); | 150 | std::ranges::transform(button_params, button_devices.begin(), Common::Input::CreateInputDevice); |
| 150 | std::ranges::transform(stick_params, stick_devices.begin(), Common::Input::CreateInputDevice); | 151 | std::ranges::transform(stick_params, stick_devices.begin(), Common::Input::CreateInputDevice); |
| @@ -163,6 +164,12 @@ void EmulatedController::LoadDevices() { | |||
| 163 | Common::Input::CreateInputDevice); | 164 | Common::Input::CreateInputDevice); |
| 164 | std::ranges::transform(tas_stick_params, tas_stick_devices.begin(), | 165 | std::ranges::transform(tas_stick_params, tas_stick_devices.begin(), |
| 165 | Common::Input::CreateInputDevice); | 166 | Common::Input::CreateInputDevice); |
| 167 | |||
| 168 | // Initialize virtual gamepad devices | ||
| 169 | std::ranges::transform(virtual_button_params, virtual_button_devices.begin(), | ||
| 170 | Common::Input::CreateInputDevice); | ||
| 171 | std::ranges::transform(virtual_stick_params, virtual_stick_devices.begin(), | ||
| 172 | Common::Input::CreateInputDevice); | ||
| 166 | } | 173 | } |
| 167 | 174 | ||
| 168 | void EmulatedController::LoadTASParams() { | 175 | void EmulatedController::LoadTASParams() { |
| @@ -203,6 +210,53 @@ void EmulatedController::LoadTASParams() { | |||
| 203 | tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1); | 210 | tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1); |
| 204 | tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_x", 2); | 211 | tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_x", 2); |
| 205 | tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3); | 212 | tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3); |
| 213 | |||
| 214 | // set to optimal stick to avoid sanitizing the stick and tweaking the coordinates | ||
| 215 | // making sure they play back in the game as originally written down in the script file | ||
| 216 | tas_stick_params[Settings::NativeAnalog::LStick].Set("deadzone", 0.0f); | ||
| 217 | tas_stick_params[Settings::NativeAnalog::LStick].Set("range", 1.0f); | ||
| 218 | tas_stick_params[Settings::NativeAnalog::RStick].Set("deadzone", 0.0f); | ||
| 219 | tas_stick_params[Settings::NativeAnalog::RStick].Set("range", 1.0f); | ||
| 220 | } | ||
| 221 | |||
| 222 | void EmulatedController::LoadVirtualGamepadParams() { | ||
| 223 | const auto player_index = NpadIdTypeToIndex(npad_id_type); | ||
| 224 | Common::ParamPackage common_params{}; | ||
| 225 | common_params.Set("engine", "virtual_gamepad"); | ||
| 226 | common_params.Set("port", static_cast<int>(player_index)); | ||
| 227 | for (auto& param : virtual_button_params) { | ||
| 228 | param = common_params; | ||
| 229 | } | ||
| 230 | for (auto& param : virtual_stick_params) { | ||
| 231 | param = common_params; | ||
| 232 | } | ||
| 233 | |||
| 234 | // TODO(german77): Replace this with an input profile or something better | ||
| 235 | virtual_button_params[Settings::NativeButton::A].Set("button", 0); | ||
| 236 | virtual_button_params[Settings::NativeButton::B].Set("button", 1); | ||
| 237 | virtual_button_params[Settings::NativeButton::X].Set("button", 2); | ||
| 238 | virtual_button_params[Settings::NativeButton::Y].Set("button", 3); | ||
| 239 | virtual_button_params[Settings::NativeButton::LStick].Set("button", 4); | ||
| 240 | virtual_button_params[Settings::NativeButton::RStick].Set("button", 5); | ||
| 241 | virtual_button_params[Settings::NativeButton::L].Set("button", 6); | ||
| 242 | virtual_button_params[Settings::NativeButton::R].Set("button", 7); | ||
| 243 | virtual_button_params[Settings::NativeButton::ZL].Set("button", 8); | ||
| 244 | virtual_button_params[Settings::NativeButton::ZR].Set("button", 9); | ||
| 245 | virtual_button_params[Settings::NativeButton::Plus].Set("button", 10); | ||
| 246 | virtual_button_params[Settings::NativeButton::Minus].Set("button", 11); | ||
| 247 | virtual_button_params[Settings::NativeButton::DLeft].Set("button", 12); | ||
| 248 | virtual_button_params[Settings::NativeButton::DUp].Set("button", 13); | ||
| 249 | virtual_button_params[Settings::NativeButton::DRight].Set("button", 14); | ||
| 250 | virtual_button_params[Settings::NativeButton::DDown].Set("button", 15); | ||
| 251 | virtual_button_params[Settings::NativeButton::SL].Set("button", 16); | ||
| 252 | virtual_button_params[Settings::NativeButton::SR].Set("button", 17); | ||
| 253 | virtual_button_params[Settings::NativeButton::Home].Set("button", 18); | ||
| 254 | virtual_button_params[Settings::NativeButton::Screenshot].Set("button", 19); | ||
| 255 | |||
| 256 | virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0); | ||
| 257 | virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1); | ||
| 258 | virtual_stick_params[Settings::NativeAnalog::RStick].Set("axis_x", 2); | ||
| 259 | virtual_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3); | ||
| 206 | } | 260 | } |
| 207 | 261 | ||
| 208 | void EmulatedController::ReloadInput() { | 262 | void EmulatedController::ReloadInput() { |
| @@ -322,6 +376,35 @@ void EmulatedController::ReloadInput() { | |||
| 322 | }, | 376 | }, |
| 323 | }); | 377 | }); |
| 324 | } | 378 | } |
| 379 | |||
| 380 | // Use a common UUID for Virtual Gamepad | ||
| 381 | static constexpr Common::UUID VIRTUAL_UUID = Common::UUID{ | ||
| 382 | {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; | ||
| 383 | |||
| 384 | // Register virtual devices. No need to force update | ||
| 385 | for (std::size_t index = 0; index < virtual_button_devices.size(); ++index) { | ||
| 386 | if (!virtual_button_devices[index]) { | ||
| 387 | continue; | ||
| 388 | } | ||
| 389 | virtual_button_devices[index]->SetCallback({ | ||
| 390 | .on_change = | ||
| 391 | [this, index](const Common::Input::CallbackStatus& callback) { | ||
| 392 | SetButton(callback, index, VIRTUAL_UUID); | ||
| 393 | }, | ||
| 394 | }); | ||
| 395 | } | ||
| 396 | |||
| 397 | for (std::size_t index = 0; index < virtual_stick_devices.size(); ++index) { | ||
| 398 | if (!virtual_stick_devices[index]) { | ||
| 399 | continue; | ||
| 400 | } | ||
| 401 | virtual_stick_devices[index]->SetCallback({ | ||
| 402 | .on_change = | ||
| 403 | [this, index](const Common::Input::CallbackStatus& callback) { | ||
| 404 | SetStick(callback, index, VIRTUAL_UUID); | ||
| 405 | }, | ||
| 406 | }); | ||
| 407 | } | ||
| 325 | } | 408 | } |
| 326 | 409 | ||
| 327 | void EmulatedController::UnloadInput() { | 410 | void EmulatedController::UnloadInput() { |
| @@ -349,6 +432,12 @@ void EmulatedController::UnloadInput() { | |||
| 349 | for (auto& stick : tas_stick_devices) { | 432 | for (auto& stick : tas_stick_devices) { |
| 350 | stick.reset(); | 433 | stick.reset(); |
| 351 | } | 434 | } |
| 435 | for (auto& button : virtual_button_devices) { | ||
| 436 | button.reset(); | ||
| 437 | } | ||
| 438 | for (auto& stick : virtual_stick_devices) { | ||
| 439 | stick.reset(); | ||
| 440 | } | ||
| 352 | camera_devices.reset(); | 441 | camera_devices.reset(); |
| 353 | nfc_devices.reset(); | 442 | nfc_devices.reset(); |
| 354 | } | 443 | } |
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index fa7a34278..a398543a6 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h | |||
| @@ -385,6 +385,9 @@ private: | |||
| 385 | /// Set the params for TAS devices | 385 | /// Set the params for TAS devices |
| 386 | void LoadTASParams(); | 386 | void LoadTASParams(); |
| 387 | 387 | ||
| 388 | /// Set the params for virtual pad devices | ||
| 389 | void LoadVirtualGamepadParams(); | ||
| 390 | |||
| 388 | /** | 391 | /** |
| 389 | * @param use_temporary_value If true tmp_npad_type will be used | 392 | * @param use_temporary_value If true tmp_npad_type will be used |
| 390 | * @return true if the controller style is fullkey | 393 | * @return true if the controller style is fullkey |
| @@ -500,6 +503,12 @@ private: | |||
| 500 | ButtonDevices tas_button_devices; | 503 | ButtonDevices tas_button_devices; |
| 501 | StickDevices tas_stick_devices; | 504 | StickDevices tas_stick_devices; |
| 502 | 505 | ||
| 506 | // Virtual gamepad related variables | ||
| 507 | ButtonParams virtual_button_params; | ||
| 508 | StickParams virtual_stick_params; | ||
| 509 | ButtonDevices virtual_button_devices; | ||
| 510 | StickDevices virtual_stick_devices; | ||
| 511 | |||
| 503 | mutable std::mutex mutex; | 512 | mutable std::mutex mutex; |
| 504 | mutable std::mutex callback_mutex; | 513 | mutable std::mutex callback_mutex; |
| 505 | std::unordered_map<int, ControllerUpdateCallback> callback_list; | 514 | std::unordered_map<int, ControllerUpdateCallback> callback_list; |
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 06010b8d1..738b6d0f1 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp | |||
| @@ -167,6 +167,9 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32 | |||
| 167 | } | 167 | } |
| 168 | if (incoming) { | 168 | if (incoming) { |
| 169 | // Populate the object lists with the data in the IPC request. | 169 | // Populate the object lists with the data in the IPC request. |
| 170 | incoming_copy_handles.reserve(handle_descriptor_header->num_handles_to_copy); | ||
| 171 | incoming_move_handles.reserve(handle_descriptor_header->num_handles_to_move); | ||
| 172 | |||
| 170 | for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) { | 173 | for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) { |
| 171 | incoming_copy_handles.push_back(rp.Pop<Handle>()); | 174 | incoming_copy_handles.push_back(rp.Pop<Handle>()); |
| 172 | } | 175 | } |
| @@ -181,6 +184,11 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32 | |||
| 181 | } | 184 | } |
| 182 | } | 185 | } |
| 183 | 186 | ||
| 187 | buffer_x_desciptors.reserve(command_header->num_buf_x_descriptors); | ||
| 188 | buffer_a_desciptors.reserve(command_header->num_buf_a_descriptors); | ||
| 189 | buffer_b_desciptors.reserve(command_header->num_buf_b_descriptors); | ||
| 190 | buffer_w_desciptors.reserve(command_header->num_buf_w_descriptors); | ||
| 191 | |||
| 184 | for (u32 i = 0; i < command_header->num_buf_x_descriptors; ++i) { | 192 | for (u32 i = 0; i < command_header->num_buf_x_descriptors; ++i) { |
| 185 | buffer_x_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorX>()); | 193 | buffer_x_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorX>()); |
| 186 | } | 194 | } |
| @@ -318,25 +326,23 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_threa | |||
| 318 | } | 326 | } |
| 319 | 327 | ||
| 320 | std::vector<u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const { | 328 | std::vector<u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const { |
| 321 | std::vector<u8> buffer{}; | ||
| 322 | const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && | 329 | const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && |
| 323 | BufferDescriptorA()[buffer_index].Size()}; | 330 | BufferDescriptorA()[buffer_index].Size()}; |
| 324 | |||
| 325 | if (is_buffer_a) { | 331 | if (is_buffer_a) { |
| 326 | ASSERT_OR_EXECUTE_MSG( | 332 | ASSERT_OR_EXECUTE_MSG( |
| 327 | BufferDescriptorA().size() > buffer_index, { return buffer; }, | 333 | BufferDescriptorA().size() > buffer_index, { return {}; }, |
| 328 | "BufferDescriptorA invalid buffer_index {}", buffer_index); | 334 | "BufferDescriptorA invalid buffer_index {}", buffer_index); |
| 329 | buffer.resize(BufferDescriptorA()[buffer_index].Size()); | 335 | std::vector<u8> buffer(BufferDescriptorA()[buffer_index].Size()); |
| 330 | memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size()); | 336 | memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size()); |
| 337 | return buffer; | ||
| 331 | } else { | 338 | } else { |
| 332 | ASSERT_OR_EXECUTE_MSG( | 339 | ASSERT_OR_EXECUTE_MSG( |
| 333 | BufferDescriptorX().size() > buffer_index, { return buffer; }, | 340 | BufferDescriptorX().size() > buffer_index, { return {}; }, |
| 334 | "BufferDescriptorX invalid buffer_index {}", buffer_index); | 341 | "BufferDescriptorX invalid buffer_index {}", buffer_index); |
| 335 | buffer.resize(BufferDescriptorX()[buffer_index].Size()); | 342 | std::vector<u8> buffer(BufferDescriptorX()[buffer_index].Size()); |
| 336 | memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size()); | 343 | memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size()); |
| 344 | return buffer; | ||
| 337 | } | 345 | } |
| 338 | |||
| 339 | return buffer; | ||
| 340 | } | 346 | } |
| 341 | 347 | ||
| 342 | std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size, | 348 | std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size, |
diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp index f85b11557..a442a3b98 100644 --- a/src/core/hle/kernel/k_address_arbiter.cpp +++ b/src/core/hle/kernel/k_address_arbiter.cpp | |||
| @@ -10,7 +10,6 @@ | |||
| 10 | #include "core/hle/kernel/k_thread_queue.h" | 10 | #include "core/hle/kernel/k_thread_queue.h" |
| 11 | #include "core/hle/kernel/kernel.h" | 11 | #include "core/hle/kernel/kernel.h" |
| 12 | #include "core/hle/kernel/svc_results.h" | 12 | #include "core/hle/kernel/svc_results.h" |
| 13 | #include "core/hle/kernel/time_manager.h" | ||
| 14 | #include "core/memory.h" | 13 | #include "core/memory.h" |
| 15 | 14 | ||
| 16 | namespace Kernel { | 15 | namespace Kernel { |
diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp index 4b1c134d4..d9da1e600 100644 --- a/src/core/hle/kernel/k_code_memory.cpp +++ b/src/core/hle/kernel/k_code_memory.cpp | |||
| @@ -27,13 +27,13 @@ Result KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, si | |||
| 27 | auto& page_table = m_owner->PageTable(); | 27 | auto& page_table = m_owner->PageTable(); |
| 28 | 28 | ||
| 29 | // Construct the page group. | 29 | // Construct the page group. |
| 30 | m_page_group = {}; | 30 | m_page_group.emplace(kernel, page_table.GetBlockInfoManager()); |
| 31 | 31 | ||
| 32 | // Lock the memory. | 32 | // Lock the memory. |
| 33 | R_TRY(page_table.LockForCodeMemory(&m_page_group, addr, size)) | 33 | R_TRY(page_table.LockForCodeMemory(std::addressof(*m_page_group), addr, size)) |
| 34 | 34 | ||
| 35 | // Clear the memory. | 35 | // Clear the memory. |
| 36 | for (const auto& block : m_page_group.Nodes()) { | 36 | for (const auto& block : *m_page_group) { |
| 37 | std::memset(device_memory.GetPointer<void>(block.GetAddress()), 0xFF, block.GetSize()); | 37 | std::memset(device_memory.GetPointer<void>(block.GetAddress()), 0xFF, block.GetSize()); |
| 38 | } | 38 | } |
| 39 | 39 | ||
| @@ -51,12 +51,13 @@ Result KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, si | |||
| 51 | void KCodeMemory::Finalize() { | 51 | void KCodeMemory::Finalize() { |
| 52 | // Unlock. | 52 | // Unlock. |
| 53 | if (!m_is_mapped && !m_is_owner_mapped) { | 53 | if (!m_is_mapped && !m_is_owner_mapped) { |
| 54 | const size_t size = m_page_group.GetNumPages() * PageSize; | 54 | const size_t size = m_page_group->GetNumPages() * PageSize; |
| 55 | m_owner->PageTable().UnlockForCodeMemory(m_address, size, m_page_group); | 55 | m_owner->PageTable().UnlockForCodeMemory(m_address, size, *m_page_group); |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | // Close the page group. | 58 | // Close the page group. |
| 59 | m_page_group = {}; | 59 | m_page_group->Close(); |
| 60 | m_page_group->Finalize(); | ||
| 60 | 61 | ||
| 61 | // Close our reference to our owner. | 62 | // Close our reference to our owner. |
| 62 | m_owner->Close(); | 63 | m_owner->Close(); |
| @@ -64,7 +65,7 @@ void KCodeMemory::Finalize() { | |||
| 64 | 65 | ||
| 65 | Result KCodeMemory::Map(VAddr address, size_t size) { | 66 | Result KCodeMemory::Map(VAddr address, size_t size) { |
| 66 | // Validate the size. | 67 | // Validate the size. |
| 67 | R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); | 68 | R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); |
| 68 | 69 | ||
| 69 | // Lock ourselves. | 70 | // Lock ourselves. |
| 70 | KScopedLightLock lk(m_lock); | 71 | KScopedLightLock lk(m_lock); |
| @@ -74,7 +75,7 @@ Result KCodeMemory::Map(VAddr address, size_t size) { | |||
| 74 | 75 | ||
| 75 | // Map the memory. | 76 | // Map the memory. |
| 76 | R_TRY(kernel.CurrentProcess()->PageTable().MapPages( | 77 | R_TRY(kernel.CurrentProcess()->PageTable().MapPages( |
| 77 | address, m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite)); | 78 | address, *m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite)); |
| 78 | 79 | ||
| 79 | // Mark ourselves as mapped. | 80 | // Mark ourselves as mapped. |
| 80 | m_is_mapped = true; | 81 | m_is_mapped = true; |
| @@ -84,13 +85,13 @@ Result KCodeMemory::Map(VAddr address, size_t size) { | |||
| 84 | 85 | ||
| 85 | Result KCodeMemory::Unmap(VAddr address, size_t size) { | 86 | Result KCodeMemory::Unmap(VAddr address, size_t size) { |
| 86 | // Validate the size. | 87 | // Validate the size. |
| 87 | R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); | 88 | R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); |
| 88 | 89 | ||
| 89 | // Lock ourselves. | 90 | // Lock ourselves. |
| 90 | KScopedLightLock lk(m_lock); | 91 | KScopedLightLock lk(m_lock); |
| 91 | 92 | ||
| 92 | // Unmap the memory. | 93 | // Unmap the memory. |
| 93 | R_TRY(kernel.CurrentProcess()->PageTable().UnmapPages(address, m_page_group, | 94 | R_TRY(kernel.CurrentProcess()->PageTable().UnmapPages(address, *m_page_group, |
| 94 | KMemoryState::CodeOut)); | 95 | KMemoryState::CodeOut)); |
| 95 | 96 | ||
| 96 | // Mark ourselves as unmapped. | 97 | // Mark ourselves as unmapped. |
| @@ -101,7 +102,7 @@ Result KCodeMemory::Unmap(VAddr address, size_t size) { | |||
| 101 | 102 | ||
| 102 | Result KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm) { | 103 | Result KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm) { |
| 103 | // Validate the size. | 104 | // Validate the size. |
| 104 | R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); | 105 | R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); |
| 105 | 106 | ||
| 106 | // Lock ourselves. | 107 | // Lock ourselves. |
| 107 | KScopedLightLock lk(m_lock); | 108 | KScopedLightLock lk(m_lock); |
| @@ -125,7 +126,7 @@ Result KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission | |||
| 125 | 126 | ||
| 126 | // Map the memory. | 127 | // Map the memory. |
| 127 | R_TRY( | 128 | R_TRY( |
| 128 | m_owner->PageTable().MapPages(address, m_page_group, KMemoryState::GeneratedCode, k_perm)); | 129 | m_owner->PageTable().MapPages(address, *m_page_group, KMemoryState::GeneratedCode, k_perm)); |
| 129 | 130 | ||
| 130 | // Mark ourselves as mapped. | 131 | // Mark ourselves as mapped. |
| 131 | m_is_owner_mapped = true; | 132 | m_is_owner_mapped = true; |
| @@ -135,13 +136,13 @@ Result KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission | |||
| 135 | 136 | ||
| 136 | Result KCodeMemory::UnmapFromOwner(VAddr address, size_t size) { | 137 | Result KCodeMemory::UnmapFromOwner(VAddr address, size_t size) { |
| 137 | // Validate the size. | 138 | // Validate the size. |
| 138 | R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); | 139 | R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); |
| 139 | 140 | ||
| 140 | // Lock ourselves. | 141 | // Lock ourselves. |
| 141 | KScopedLightLock lk(m_lock); | 142 | KScopedLightLock lk(m_lock); |
| 142 | 143 | ||
| 143 | // Unmap the memory. | 144 | // Unmap the memory. |
| 144 | R_TRY(m_owner->PageTable().UnmapPages(address, m_page_group, KMemoryState::GeneratedCode)); | 145 | R_TRY(m_owner->PageTable().UnmapPages(address, *m_page_group, KMemoryState::GeneratedCode)); |
| 145 | 146 | ||
| 146 | // Mark ourselves as unmapped. | 147 | // Mark ourselves as unmapped. |
| 147 | m_is_owner_mapped = false; | 148 | m_is_owner_mapped = false; |
diff --git a/src/core/hle/kernel/k_code_memory.h b/src/core/hle/kernel/k_code_memory.h index 2e7e1436a..5b260b385 100644 --- a/src/core/hle/kernel/k_code_memory.h +++ b/src/core/hle/kernel/k_code_memory.h | |||
| @@ -3,6 +3,8 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <optional> | ||
| 7 | |||
| 6 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 7 | #include "core/device_memory.h" | 9 | #include "core/device_memory.h" |
| 8 | #include "core/hle/kernel/k_auto_object.h" | 10 | #include "core/hle/kernel/k_auto_object.h" |
| @@ -49,11 +51,11 @@ public: | |||
| 49 | return m_address; | 51 | return m_address; |
| 50 | } | 52 | } |
| 51 | size_t GetSize() const { | 53 | size_t GetSize() const { |
| 52 | return m_is_initialized ? m_page_group.GetNumPages() * PageSize : 0; | 54 | return m_is_initialized ? m_page_group->GetNumPages() * PageSize : 0; |
| 53 | } | 55 | } |
| 54 | 56 | ||
| 55 | private: | 57 | private: |
| 56 | KPageGroup m_page_group{}; | 58 | std::optional<KPageGroup> m_page_group{}; |
| 57 | KProcess* m_owner{}; | 59 | KProcess* m_owner{}; |
| 58 | VAddr m_address{}; | 60 | VAddr m_address{}; |
| 59 | KLightLock m_lock; | 61 | KLightLock m_lock; |
diff --git a/src/core/hle/kernel/k_hardware_timer.cpp b/src/core/hle/kernel/k_hardware_timer.cpp new file mode 100644 index 000000000..6bba79ea0 --- /dev/null +++ b/src/core/hle/kernel/k_hardware_timer.cpp | |||
| @@ -0,0 +1,74 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/core.h" | ||
| 5 | #include "core/core_timing.h" | ||
| 6 | #include "core/hle/kernel/k_hardware_timer.h" | ||
| 7 | #include "core/hle/kernel/k_scheduler.h" | ||
| 8 | |||
| 9 | namespace Kernel { | ||
| 10 | |||
| 11 | void KHardwareTimer::Initialize() { | ||
| 12 | // Create the timing callback to register with CoreTiming. | ||
| 13 | m_event_type = Core::Timing::CreateEvent( | ||
| 14 | "KHardwareTimer::Callback", [](std::uintptr_t timer_handle, s64, std::chrono::nanoseconds) { | ||
| 15 | reinterpret_cast<KHardwareTimer*>(timer_handle)->DoTask(); | ||
| 16 | return std::nullopt; | ||
| 17 | }); | ||
| 18 | } | ||
| 19 | |||
| 20 | void KHardwareTimer::Finalize() { | ||
| 21 | this->DisableInterrupt(); | ||
| 22 | m_event_type.reset(); | ||
| 23 | } | ||
| 24 | |||
| 25 | void KHardwareTimer::DoTask() { | ||
| 26 | // Handle the interrupt. | ||
| 27 | { | ||
| 28 | KScopedSchedulerLock slk{m_kernel}; | ||
| 29 | KScopedSpinLock lk(this->GetLock()); | ||
| 30 | |||
| 31 | //! Ignore this event if needed. | ||
| 32 | if (!this->GetInterruptEnabled()) { | ||
| 33 | return; | ||
| 34 | } | ||
| 35 | |||
| 36 | // Disable the timer interrupt while we handle this. | ||
| 37 | this->DisableInterrupt(); | ||
| 38 | |||
| 39 | if (const s64 next_time = this->DoInterruptTaskImpl(GetTick()); | ||
| 40 | 0 < next_time && next_time <= m_wakeup_time) { | ||
| 41 | // We have a next time, so we should set the time to interrupt and turn the interrupt | ||
| 42 | // on. | ||
| 43 | this->EnableInterrupt(next_time); | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | // Clear the timer interrupt. | ||
| 48 | // Kernel::GetInterruptManager().ClearInterrupt(KInterruptName_NonSecurePhysicalTimer, | ||
| 49 | // GetCurrentCoreId()); | ||
| 50 | } | ||
| 51 | |||
| 52 | void KHardwareTimer::EnableInterrupt(s64 wakeup_time) { | ||
| 53 | this->DisableInterrupt(); | ||
| 54 | |||
| 55 | m_wakeup_time = wakeup_time; | ||
| 56 | m_kernel.System().CoreTiming().ScheduleEvent(std::chrono::nanoseconds{m_wakeup_time}, | ||
| 57 | m_event_type, reinterpret_cast<uintptr_t>(this), | ||
| 58 | true); | ||
| 59 | } | ||
| 60 | |||
| 61 | void KHardwareTimer::DisableInterrupt() { | ||
| 62 | m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type, reinterpret_cast<uintptr_t>(this)); | ||
| 63 | m_wakeup_time = std::numeric_limits<s64>::max(); | ||
| 64 | } | ||
| 65 | |||
| 66 | s64 KHardwareTimer::GetTick() const { | ||
| 67 | return m_kernel.System().CoreTiming().GetGlobalTimeNs().count(); | ||
| 68 | } | ||
| 69 | |||
| 70 | bool KHardwareTimer::GetInterruptEnabled() { | ||
| 71 | return m_wakeup_time != std::numeric_limits<s64>::max(); | ||
| 72 | } | ||
| 73 | |||
| 74 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/k_hardware_timer.h b/src/core/hle/kernel/k_hardware_timer.h new file mode 100644 index 000000000..00bef6ea1 --- /dev/null +++ b/src/core/hle/kernel/k_hardware_timer.h | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/kernel/k_hardware_timer_base.h" | ||
| 7 | |||
| 8 | namespace Core::Timing { | ||
| 9 | struct EventType; | ||
| 10 | } // namespace Core::Timing | ||
| 11 | |||
| 12 | namespace Kernel { | ||
| 13 | |||
| 14 | class KHardwareTimer : /* public KInterruptTask, */ public KHardwareTimerBase { | ||
| 15 | public: | ||
| 16 | explicit KHardwareTimer(KernelCore& kernel) : KHardwareTimerBase{kernel} {} | ||
| 17 | |||
| 18 | // Public API. | ||
| 19 | void Initialize(); | ||
| 20 | void Finalize(); | ||
| 21 | |||
| 22 | s64 GetCount() const { | ||
| 23 | return GetTick(); | ||
| 24 | } | ||
| 25 | |||
| 26 | void RegisterTask(KTimerTask* task, s64 time_from_now) { | ||
| 27 | this->RegisterAbsoluteTask(task, GetTick() + time_from_now); | ||
| 28 | } | ||
| 29 | |||
| 30 | void RegisterAbsoluteTask(KTimerTask* task, s64 task_time) { | ||
| 31 | KScopedDisableDispatch dd{m_kernel}; | ||
| 32 | KScopedSpinLock lk{this->GetLock()}; | ||
| 33 | |||
| 34 | if (this->RegisterAbsoluteTaskImpl(task, task_time)) { | ||
| 35 | if (task_time <= m_wakeup_time) { | ||
| 36 | this->EnableInterrupt(task_time); | ||
| 37 | } | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | private: | ||
| 42 | void EnableInterrupt(s64 wakeup_time); | ||
| 43 | void DisableInterrupt(); | ||
| 44 | bool GetInterruptEnabled(); | ||
| 45 | s64 GetTick() const; | ||
| 46 | void DoTask(); | ||
| 47 | |||
| 48 | private: | ||
| 49 | // Absolute time in nanoseconds | ||
| 50 | s64 m_wakeup_time{std::numeric_limits<s64>::max()}; | ||
| 51 | std::shared_ptr<Core::Timing::EventType> m_event_type{}; | ||
| 52 | }; | ||
| 53 | |||
| 54 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/k_hardware_timer_base.h b/src/core/hle/kernel/k_hardware_timer_base.h new file mode 100644 index 000000000..6318b35bd --- /dev/null +++ b/src/core/hle/kernel/k_hardware_timer_base.h | |||
| @@ -0,0 +1,92 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/kernel/k_spin_lock.h" | ||
| 7 | #include "core/hle/kernel/k_thread.h" | ||
| 8 | #include "core/hle/kernel/k_timer_task.h" | ||
| 9 | |||
| 10 | namespace Kernel { | ||
| 11 | |||
| 12 | class KHardwareTimerBase { | ||
| 13 | public: | ||
| 14 | explicit KHardwareTimerBase(KernelCore& kernel) : m_kernel{kernel} {} | ||
| 15 | |||
| 16 | void CancelTask(KTimerTask* task) { | ||
| 17 | KScopedDisableDispatch dd{m_kernel}; | ||
| 18 | KScopedSpinLock lk{m_lock}; | ||
| 19 | |||
| 20 | if (const s64 task_time = task->GetTime(); task_time > 0) { | ||
| 21 | this->RemoveTaskFromTree(task); | ||
| 22 | } | ||
| 23 | } | ||
| 24 | |||
| 25 | protected: | ||
| 26 | KSpinLock& GetLock() { | ||
| 27 | return m_lock; | ||
| 28 | } | ||
| 29 | |||
| 30 | s64 DoInterruptTaskImpl(s64 cur_time) { | ||
| 31 | // We want to handle all tasks, returning the next time that a task is scheduled. | ||
| 32 | while (true) { | ||
| 33 | // Get the next task. If there isn't one, return 0. | ||
| 34 | KTimerTask* task = m_next_task; | ||
| 35 | if (task == nullptr) { | ||
| 36 | return 0; | ||
| 37 | } | ||
| 38 | |||
| 39 | // If the task needs to be done in the future, do it in the future and not now. | ||
| 40 | if (const s64 task_time = task->GetTime(); task_time > cur_time) { | ||
| 41 | return task_time; | ||
| 42 | } | ||
| 43 | |||
| 44 | // Remove the task from the tree of tasks, and update our next task. | ||
| 45 | this->RemoveTaskFromTree(task); | ||
| 46 | |||
| 47 | // Handle the task. | ||
| 48 | task->OnTimer(); | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | bool RegisterAbsoluteTaskImpl(KTimerTask* task, s64 task_time) { | ||
| 53 | ASSERT(task_time > 0); | ||
| 54 | |||
| 55 | // Set the task's time, and insert it into our tree. | ||
| 56 | task->SetTime(task_time); | ||
| 57 | m_task_tree.insert(*task); | ||
| 58 | |||
| 59 | // Update our next task if relevant. | ||
| 60 | if (m_next_task != nullptr && m_next_task->GetTime() <= task_time) { | ||
| 61 | return false; | ||
| 62 | } | ||
| 63 | m_next_task = task; | ||
| 64 | return true; | ||
| 65 | } | ||
| 66 | |||
| 67 | private: | ||
| 68 | void RemoveTaskFromTree(KTimerTask* task) { | ||
| 69 | // Erase from the tree. | ||
| 70 | auto it = m_task_tree.erase(m_task_tree.iterator_to(*task)); | ||
| 71 | |||
| 72 | // Clear the task's scheduled time. | ||
| 73 | task->SetTime(0); | ||
| 74 | |||
| 75 | // Update our next task if relevant. | ||
| 76 | if (m_next_task == task) { | ||
| 77 | m_next_task = (it != m_task_tree.end()) ? std::addressof(*it) : nullptr; | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | protected: | ||
| 82 | KernelCore& m_kernel; | ||
| 83 | |||
| 84 | private: | ||
| 85 | using TimerTaskTree = Common::IntrusiveRedBlackTreeBaseTraits<KTimerTask>::TreeType<KTimerTask>; | ||
| 86 | |||
| 87 | KSpinLock m_lock{}; | ||
| 88 | TimerTaskTree m_task_tree{}; | ||
| 89 | KTimerTask* m_next_task{}; | ||
| 90 | }; | ||
| 91 | |||
| 92 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp index bd33571da..cd6ea388e 100644 --- a/src/core/hle/kernel/k_memory_manager.cpp +++ b/src/core/hle/kernel/k_memory_manager.cpp | |||
| @@ -223,7 +223,7 @@ Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, | |||
| 223 | 223 | ||
| 224 | // Ensure that we don't leave anything un-freed. | 224 | // Ensure that we don't leave anything un-freed. |
| 225 | ON_RESULT_FAILURE { | 225 | ON_RESULT_FAILURE { |
| 226 | for (const auto& it : out->Nodes()) { | 226 | for (const auto& it : *out) { |
| 227 | auto& manager = this->GetManager(it.GetAddress()); | 227 | auto& manager = this->GetManager(it.GetAddress()); |
| 228 | const size_t node_num_pages = std::min<u64>( | 228 | const size_t node_num_pages = std::min<u64>( |
| 229 | it.GetNumPages(), (manager.GetEndAddress() - it.GetAddress()) / PageSize); | 229 | it.GetNumPages(), (manager.GetEndAddress() - it.GetAddress()) / PageSize); |
| @@ -285,7 +285,7 @@ Result KMemoryManager::AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 op | |||
| 285 | m_has_optimized_process[static_cast<size_t>(pool)], true)); | 285 | m_has_optimized_process[static_cast<size_t>(pool)], true)); |
| 286 | 286 | ||
| 287 | // Open the first reference to the pages. | 287 | // Open the first reference to the pages. |
| 288 | for (const auto& block : out->Nodes()) { | 288 | for (const auto& block : *out) { |
| 289 | PAddr cur_address = block.GetAddress(); | 289 | PAddr cur_address = block.GetAddress(); |
| 290 | size_t remaining_pages = block.GetNumPages(); | 290 | size_t remaining_pages = block.GetNumPages(); |
| 291 | while (remaining_pages > 0) { | 291 | while (remaining_pages > 0) { |
| @@ -335,7 +335,7 @@ Result KMemoryManager::AllocateForProcess(KPageGroup* out, size_t num_pages, u32 | |||
| 335 | // Perform optimized memory tracking, if we should. | 335 | // Perform optimized memory tracking, if we should. |
| 336 | if (optimized) { | 336 | if (optimized) { |
| 337 | // Iterate over the allocated blocks. | 337 | // Iterate over the allocated blocks. |
| 338 | for (const auto& block : out->Nodes()) { | 338 | for (const auto& block : *out) { |
| 339 | // Get the block extents. | 339 | // Get the block extents. |
| 340 | const PAddr block_address = block.GetAddress(); | 340 | const PAddr block_address = block.GetAddress(); |
| 341 | const size_t block_pages = block.GetNumPages(); | 341 | const size_t block_pages = block.GetNumPages(); |
| @@ -391,7 +391,7 @@ Result KMemoryManager::AllocateForProcess(KPageGroup* out, size_t num_pages, u32 | |||
| 391 | } | 391 | } |
| 392 | } else { | 392 | } else { |
| 393 | // Set all the allocated memory. | 393 | // Set all the allocated memory. |
| 394 | for (const auto& block : out->Nodes()) { | 394 | for (const auto& block : *out) { |
| 395 | std::memset(m_system.DeviceMemory().GetPointer<void>(block.GetAddress()), fill_pattern, | 395 | std::memset(m_system.DeviceMemory().GetPointer<void>(block.GetAddress()), fill_pattern, |
| 396 | block.GetSize()); | 396 | block.GetSize()); |
| 397 | } | 397 | } |
diff --git a/src/core/hle/kernel/k_page_group.cpp b/src/core/hle/kernel/k_page_group.cpp new file mode 100644 index 000000000..d8c644a33 --- /dev/null +++ b/src/core/hle/kernel/k_page_group.cpp | |||
| @@ -0,0 +1,121 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/kernel/k_dynamic_resource_manager.h" | ||
| 5 | #include "core/hle/kernel/k_memory_manager.h" | ||
| 6 | #include "core/hle/kernel/k_page_group.h" | ||
| 7 | #include "core/hle/kernel/kernel.h" | ||
| 8 | #include "core/hle/kernel/svc_results.h" | ||
| 9 | |||
| 10 | namespace Kernel { | ||
| 11 | |||
| 12 | void KPageGroup::Finalize() { | ||
| 13 | KBlockInfo* cur = m_first_block; | ||
| 14 | while (cur != nullptr) { | ||
| 15 | KBlockInfo* next = cur->GetNext(); | ||
| 16 | m_manager->Free(cur); | ||
| 17 | cur = next; | ||
| 18 | } | ||
| 19 | |||
| 20 | m_first_block = nullptr; | ||
| 21 | m_last_block = nullptr; | ||
| 22 | } | ||
| 23 | |||
| 24 | void KPageGroup::CloseAndReset() { | ||
| 25 | auto& mm = m_kernel.MemoryManager(); | ||
| 26 | |||
| 27 | KBlockInfo* cur = m_first_block; | ||
| 28 | while (cur != nullptr) { | ||
| 29 | KBlockInfo* next = cur->GetNext(); | ||
| 30 | mm.Close(cur->GetAddress(), cur->GetNumPages()); | ||
| 31 | m_manager->Free(cur); | ||
| 32 | cur = next; | ||
| 33 | } | ||
| 34 | |||
| 35 | m_first_block = nullptr; | ||
| 36 | m_last_block = nullptr; | ||
| 37 | } | ||
| 38 | |||
| 39 | size_t KPageGroup::GetNumPages() const { | ||
| 40 | size_t num_pages = 0; | ||
| 41 | |||
| 42 | for (const auto& it : *this) { | ||
| 43 | num_pages += it.GetNumPages(); | ||
| 44 | } | ||
| 45 | |||
| 46 | return num_pages; | ||
| 47 | } | ||
| 48 | |||
| 49 | Result KPageGroup::AddBlock(KPhysicalAddress addr, size_t num_pages) { | ||
| 50 | // Succeed immediately if we're adding no pages. | ||
| 51 | R_SUCCEED_IF(num_pages == 0); | ||
| 52 | |||
| 53 | // Check for overflow. | ||
| 54 | ASSERT(addr < addr + num_pages * PageSize); | ||
| 55 | |||
| 56 | // Try to just append to the last block. | ||
| 57 | if (m_last_block != nullptr) { | ||
| 58 | R_SUCCEED_IF(m_last_block->TryConcatenate(addr, num_pages)); | ||
| 59 | } | ||
| 60 | |||
| 61 | // Allocate a new block. | ||
| 62 | KBlockInfo* new_block = m_manager->Allocate(); | ||
| 63 | R_UNLESS(new_block != nullptr, ResultOutOfResource); | ||
| 64 | |||
| 65 | // Initialize the block. | ||
| 66 | new_block->Initialize(addr, num_pages); | ||
| 67 | |||
| 68 | // Add the block to our list. | ||
| 69 | if (m_last_block != nullptr) { | ||
| 70 | m_last_block->SetNext(new_block); | ||
| 71 | } else { | ||
| 72 | m_first_block = new_block; | ||
| 73 | } | ||
| 74 | m_last_block = new_block; | ||
| 75 | |||
| 76 | R_SUCCEED(); | ||
| 77 | } | ||
| 78 | |||
| 79 | void KPageGroup::Open() const { | ||
| 80 | auto& mm = m_kernel.MemoryManager(); | ||
| 81 | |||
| 82 | for (const auto& it : *this) { | ||
| 83 | mm.Open(it.GetAddress(), it.GetNumPages()); | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | void KPageGroup::OpenFirst() const { | ||
| 88 | auto& mm = m_kernel.MemoryManager(); | ||
| 89 | |||
| 90 | for (const auto& it : *this) { | ||
| 91 | mm.OpenFirst(it.GetAddress(), it.GetNumPages()); | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | void KPageGroup::Close() const { | ||
| 96 | auto& mm = m_kernel.MemoryManager(); | ||
| 97 | |||
| 98 | for (const auto& it : *this) { | ||
| 99 | mm.Close(it.GetAddress(), it.GetNumPages()); | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | bool KPageGroup::IsEquivalentTo(const KPageGroup& rhs) const { | ||
| 104 | auto lit = this->begin(); | ||
| 105 | auto rit = rhs.begin(); | ||
| 106 | auto lend = this->end(); | ||
| 107 | auto rend = rhs.end(); | ||
| 108 | |||
| 109 | while (lit != lend && rit != rend) { | ||
| 110 | if (*lit != *rit) { | ||
| 111 | return false; | ||
| 112 | } | ||
| 113 | |||
| 114 | ++lit; | ||
| 115 | ++rit; | ||
| 116 | } | ||
| 117 | |||
| 118 | return lit == lend && rit == rend; | ||
| 119 | } | ||
| 120 | |||
| 121 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/k_page_group.h b/src/core/hle/kernel/k_page_group.h index 316f172f2..c07f17663 100644 --- a/src/core/hle/kernel/k_page_group.h +++ b/src/core/hle/kernel/k_page_group.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| @@ -13,24 +13,23 @@ | |||
| 13 | 13 | ||
| 14 | namespace Kernel { | 14 | namespace Kernel { |
| 15 | 15 | ||
| 16 | class KBlockInfoManager; | ||
| 17 | class KernelCore; | ||
| 16 | class KPageGroup; | 18 | class KPageGroup; |
| 17 | 19 | ||
| 18 | class KBlockInfo { | 20 | class KBlockInfo { |
| 19 | private: | ||
| 20 | friend class KPageGroup; | ||
| 21 | |||
| 22 | public: | 21 | public: |
| 23 | constexpr KBlockInfo() = default; | 22 | constexpr explicit KBlockInfo() : m_next(nullptr) {} |
| 24 | 23 | ||
| 25 | constexpr void Initialize(PAddr addr, size_t np) { | 24 | constexpr void Initialize(KPhysicalAddress addr, size_t np) { |
| 26 | ASSERT(Common::IsAligned(addr, PageSize)); | 25 | ASSERT(Common::IsAligned(addr, PageSize)); |
| 27 | ASSERT(static_cast<u32>(np) == np); | 26 | ASSERT(static_cast<u32>(np) == np); |
| 28 | 27 | ||
| 29 | m_page_index = static_cast<u32>(addr) / PageSize; | 28 | m_page_index = static_cast<u32>(addr / PageSize); |
| 30 | m_num_pages = static_cast<u32>(np); | 29 | m_num_pages = static_cast<u32>(np); |
| 31 | } | 30 | } |
| 32 | 31 | ||
| 33 | constexpr PAddr GetAddress() const { | 32 | constexpr KPhysicalAddress GetAddress() const { |
| 34 | return m_page_index * PageSize; | 33 | return m_page_index * PageSize; |
| 35 | } | 34 | } |
| 36 | constexpr size_t GetNumPages() const { | 35 | constexpr size_t GetNumPages() const { |
| @@ -39,10 +38,10 @@ public: | |||
| 39 | constexpr size_t GetSize() const { | 38 | constexpr size_t GetSize() const { |
| 40 | return this->GetNumPages() * PageSize; | 39 | return this->GetNumPages() * PageSize; |
| 41 | } | 40 | } |
| 42 | constexpr PAddr GetEndAddress() const { | 41 | constexpr KPhysicalAddress GetEndAddress() const { |
| 43 | return (m_page_index + m_num_pages) * PageSize; | 42 | return (m_page_index + m_num_pages) * PageSize; |
| 44 | } | 43 | } |
| 45 | constexpr PAddr GetLastAddress() const { | 44 | constexpr KPhysicalAddress GetLastAddress() const { |
| 46 | return this->GetEndAddress() - 1; | 45 | return this->GetEndAddress() - 1; |
| 47 | } | 46 | } |
| 48 | 47 | ||
| @@ -62,8 +61,8 @@ public: | |||
| 62 | return !(*this == rhs); | 61 | return !(*this == rhs); |
| 63 | } | 62 | } |
| 64 | 63 | ||
| 65 | constexpr bool IsStrictlyBefore(PAddr addr) const { | 64 | constexpr bool IsStrictlyBefore(KPhysicalAddress addr) const { |
| 66 | const PAddr end = this->GetEndAddress(); | 65 | const KPhysicalAddress end = this->GetEndAddress(); |
| 67 | 66 | ||
| 68 | if (m_page_index != 0 && end == 0) { | 67 | if (m_page_index != 0 && end == 0) { |
| 69 | return false; | 68 | return false; |
| @@ -72,11 +71,11 @@ public: | |||
| 72 | return end < addr; | 71 | return end < addr; |
| 73 | } | 72 | } |
| 74 | 73 | ||
| 75 | constexpr bool operator<(PAddr addr) const { | 74 | constexpr bool operator<(KPhysicalAddress addr) const { |
| 76 | return this->IsStrictlyBefore(addr); | 75 | return this->IsStrictlyBefore(addr); |
| 77 | } | 76 | } |
| 78 | 77 | ||
| 79 | constexpr bool TryConcatenate(PAddr addr, size_t np) { | 78 | constexpr bool TryConcatenate(KPhysicalAddress addr, size_t np) { |
| 80 | if (addr != 0 && addr == this->GetEndAddress()) { | 79 | if (addr != 0 && addr == this->GetEndAddress()) { |
| 81 | m_num_pages += static_cast<u32>(np); | 80 | m_num_pages += static_cast<u32>(np); |
| 82 | return true; | 81 | return true; |
| @@ -90,96 +89,118 @@ private: | |||
| 90 | } | 89 | } |
| 91 | 90 | ||
| 92 | private: | 91 | private: |
| 92 | friend class KPageGroup; | ||
| 93 | |||
| 93 | KBlockInfo* m_next{}; | 94 | KBlockInfo* m_next{}; |
| 94 | u32 m_page_index{}; | 95 | u32 m_page_index{}; |
| 95 | u32 m_num_pages{}; | 96 | u32 m_num_pages{}; |
| 96 | }; | 97 | }; |
| 97 | static_assert(sizeof(KBlockInfo) <= 0x10); | 98 | static_assert(sizeof(KBlockInfo) <= 0x10); |
| 98 | 99 | ||
| 99 | class KPageGroup final { | 100 | class KPageGroup { |
| 100 | public: | 101 | public: |
| 101 | class Node final { | 102 | class Iterator { |
| 102 | public: | 103 | public: |
| 103 | constexpr Node(u64 addr_, std::size_t num_pages_) : addr{addr_}, num_pages{num_pages_} {} | 104 | using iterator_category = std::forward_iterator_tag; |
| 105 | using value_type = const KBlockInfo; | ||
| 106 | using difference_type = std::ptrdiff_t; | ||
| 107 | using pointer = value_type*; | ||
| 108 | using reference = value_type&; | ||
| 109 | |||
| 110 | constexpr explicit Iterator(pointer n) : m_node(n) {} | ||
| 111 | |||
| 112 | constexpr bool operator==(const Iterator& rhs) const { | ||
| 113 | return m_node == rhs.m_node; | ||
| 114 | } | ||
| 115 | constexpr bool operator!=(const Iterator& rhs) const { | ||
| 116 | return !(*this == rhs); | ||
| 117 | } | ||
| 104 | 118 | ||
| 105 | constexpr u64 GetAddress() const { | 119 | constexpr pointer operator->() const { |
| 106 | return addr; | 120 | return m_node; |
| 121 | } | ||
| 122 | constexpr reference operator*() const { | ||
| 123 | return *m_node; | ||
| 107 | } | 124 | } |
| 108 | 125 | ||
| 109 | constexpr std::size_t GetNumPages() const { | 126 | constexpr Iterator& operator++() { |
| 110 | return num_pages; | 127 | m_node = m_node->GetNext(); |
| 128 | return *this; | ||
| 111 | } | 129 | } |
| 112 | 130 | ||
| 113 | constexpr std::size_t GetSize() const { | 131 | constexpr Iterator operator++(int) { |
| 114 | return GetNumPages() * PageSize; | 132 | const Iterator it{*this}; |
| 133 | ++(*this); | ||
| 134 | return it; | ||
| 115 | } | 135 | } |
| 116 | 136 | ||
| 117 | private: | 137 | private: |
| 118 | u64 addr{}; | 138 | pointer m_node{}; |
| 119 | std::size_t num_pages{}; | ||
| 120 | }; | 139 | }; |
| 121 | 140 | ||
| 122 | public: | 141 | explicit KPageGroup(KernelCore& kernel, KBlockInfoManager* m) |
| 123 | KPageGroup() = default; | 142 | : m_kernel{kernel}, m_manager{m} {} |
| 124 | KPageGroup(u64 address, u64 num_pages) { | 143 | ~KPageGroup() { |
| 125 | ASSERT(AddBlock(address, num_pages).IsSuccess()); | 144 | this->Finalize(); |
| 126 | } | 145 | } |
| 127 | 146 | ||
| 128 | constexpr std::list<Node>& Nodes() { | 147 | void CloseAndReset(); |
| 129 | return nodes; | 148 | void Finalize(); |
| 130 | } | ||
| 131 | 149 | ||
| 132 | constexpr const std::list<Node>& Nodes() const { | 150 | Iterator begin() const { |
| 133 | return nodes; | 151 | return Iterator{m_first_block}; |
| 152 | } | ||
| 153 | Iterator end() const { | ||
| 154 | return Iterator{nullptr}; | ||
| 155 | } | ||
| 156 | bool empty() const { | ||
| 157 | return m_first_block == nullptr; | ||
| 134 | } | 158 | } |
| 135 | 159 | ||
| 136 | std::size_t GetNumPages() const { | 160 | Result AddBlock(KPhysicalAddress addr, size_t num_pages); |
| 137 | std::size_t num_pages = 0; | 161 | void Open() const; |
| 138 | for (const Node& node : nodes) { | 162 | void OpenFirst() const; |
| 139 | num_pages += node.GetNumPages(); | 163 | void Close() const; |
| 140 | } | 164 | |
| 141 | return num_pages; | 165 | size_t GetNumPages() const; |
| 142 | } | 166 | |
| 143 | 167 | bool IsEquivalentTo(const KPageGroup& rhs) const; | |
| 144 | bool IsEqual(KPageGroup& other) const { | 168 | |
| 145 | auto this_node = nodes.begin(); | 169 | bool operator==(const KPageGroup& rhs) const { |
| 146 | auto other_node = other.nodes.begin(); | 170 | return this->IsEquivalentTo(rhs); |
| 147 | while (this_node != nodes.end() && other_node != other.nodes.end()) { | 171 | } |
| 148 | if (this_node->GetAddress() != other_node->GetAddress() || | ||
| 149 | this_node->GetNumPages() != other_node->GetNumPages()) { | ||
| 150 | return false; | ||
| 151 | } | ||
| 152 | this_node = std::next(this_node); | ||
| 153 | other_node = std::next(other_node); | ||
| 154 | } | ||
| 155 | 172 | ||
| 156 | return this_node == nodes.end() && other_node == other.nodes.end(); | 173 | bool operator!=(const KPageGroup& rhs) const { |
| 174 | return !(*this == rhs); | ||
| 157 | } | 175 | } |
| 158 | 176 | ||
| 159 | Result AddBlock(u64 address, u64 num_pages) { | 177 | private: |
| 160 | if (!num_pages) { | 178 | KernelCore& m_kernel; |
| 161 | return ResultSuccess; | 179 | KBlockInfo* m_first_block{}; |
| 180 | KBlockInfo* m_last_block{}; | ||
| 181 | KBlockInfoManager* m_manager{}; | ||
| 182 | }; | ||
| 183 | |||
| 184 | class KScopedPageGroup { | ||
| 185 | public: | ||
| 186 | explicit KScopedPageGroup(const KPageGroup* gp) : m_pg(gp) { | ||
| 187 | if (m_pg) { | ||
| 188 | m_pg->Open(); | ||
| 162 | } | 189 | } |
| 163 | if (!nodes.empty()) { | 190 | } |
| 164 | const auto node = nodes.back(); | 191 | explicit KScopedPageGroup(const KPageGroup& gp) : KScopedPageGroup(std::addressof(gp)) {} |
| 165 | if (node.GetAddress() + node.GetNumPages() * PageSize == address) { | 192 | ~KScopedPageGroup() { |
| 166 | address = node.GetAddress(); | 193 | if (m_pg) { |
| 167 | num_pages += node.GetNumPages(); | 194 | m_pg->Close(); |
| 168 | nodes.pop_back(); | ||
| 169 | } | ||
| 170 | } | 195 | } |
| 171 | nodes.push_back({address, num_pages}); | ||
| 172 | return ResultSuccess; | ||
| 173 | } | 196 | } |
| 174 | 197 | ||
| 175 | bool Empty() const { | 198 | void CancelClose() { |
| 176 | return nodes.empty(); | 199 | m_pg = nullptr; |
| 177 | } | 200 | } |
| 178 | 201 | ||
| 179 | void Finalize() {} | ||
| 180 | |||
| 181 | private: | 202 | private: |
| 182 | std::list<Node> nodes; | 203 | const KPageGroup* m_pg{}; |
| 183 | }; | 204 | }; |
| 184 | 205 | ||
| 185 | } // namespace Kernel | 206 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index 612fc76fa..9c7ac22dc 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp | |||
| @@ -100,7 +100,7 @@ constexpr size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceType a | |||
| 100 | 100 | ||
| 101 | KPageTable::KPageTable(Core::System& system_) | 101 | KPageTable::KPageTable(Core::System& system_) |
| 102 | : m_general_lock{system_.Kernel()}, | 102 | : m_general_lock{system_.Kernel()}, |
| 103 | m_map_physical_memory_lock{system_.Kernel()}, m_system{system_} {} | 103 | m_map_physical_memory_lock{system_.Kernel()}, m_system{system_}, m_kernel{system_.Kernel()} {} |
| 104 | 104 | ||
| 105 | KPageTable::~KPageTable() = default; | 105 | KPageTable::~KPageTable() = default; |
| 106 | 106 | ||
| @@ -373,7 +373,7 @@ Result KPageTable::MapProcessCode(VAddr addr, size_t num_pages, KMemoryState sta | |||
| 373 | m_memory_block_slab_manager); | 373 | m_memory_block_slab_manager); |
| 374 | 374 | ||
| 375 | // Allocate and open. | 375 | // Allocate and open. |
| 376 | KPageGroup pg; | 376 | KPageGroup pg{m_kernel, m_block_info_manager}; |
| 377 | R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen( | 377 | R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen( |
| 378 | &pg, num_pages, | 378 | &pg, num_pages, |
| 379 | KMemoryManager::EncodeOption(KMemoryManager::Pool::Application, m_allocation_option))); | 379 | KMemoryManager::EncodeOption(KMemoryManager::Pool::Application, m_allocation_option))); |
| @@ -432,7 +432,7 @@ Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, size_t si | |||
| 432 | const size_t num_pages = size / PageSize; | 432 | const size_t num_pages = size / PageSize; |
| 433 | 433 | ||
| 434 | // Create page groups for the memory being mapped. | 434 | // Create page groups for the memory being mapped. |
| 435 | KPageGroup pg; | 435 | KPageGroup pg{m_kernel, m_block_info_manager}; |
| 436 | AddRegionToPages(src_address, num_pages, pg); | 436 | AddRegionToPages(src_address, num_pages, pg); |
| 437 | 437 | ||
| 438 | // Reprotect the source as kernel-read/not mapped. | 438 | // Reprotect the source as kernel-read/not mapped. |
| @@ -593,7 +593,7 @@ Result KPageTable::MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages) { | |||
| 593 | const size_t size = num_pages * PageSize; | 593 | const size_t size = num_pages * PageSize; |
| 594 | 594 | ||
| 595 | // We're making a new group, not adding to an existing one. | 595 | // We're making a new group, not adding to an existing one. |
| 596 | R_UNLESS(pg.Empty(), ResultInvalidCurrentMemory); | 596 | R_UNLESS(pg.empty(), ResultInvalidCurrentMemory); |
| 597 | 597 | ||
| 598 | // Begin traversal. | 598 | // Begin traversal. |
| 599 | Common::PageTable::TraversalContext context; | 599 | Common::PageTable::TraversalContext context; |
| @@ -640,11 +640,10 @@ Result KPageTable::MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages) { | |||
| 640 | R_SUCCEED(); | 640 | R_SUCCEED(); |
| 641 | } | 641 | } |
| 642 | 642 | ||
| 643 | bool KPageTable::IsValidPageGroup(const KPageGroup& pg_ll, VAddr addr, size_t num_pages) { | 643 | bool KPageTable::IsValidPageGroup(const KPageGroup& pg, VAddr addr, size_t num_pages) { |
| 644 | ASSERT(this->IsLockedByCurrentThread()); | 644 | ASSERT(this->IsLockedByCurrentThread()); |
| 645 | 645 | ||
| 646 | const size_t size = num_pages * PageSize; | 646 | const size_t size = num_pages * PageSize; |
| 647 | const auto& pg = pg_ll.Nodes(); | ||
| 648 | const auto& memory_layout = m_system.Kernel().MemoryLayout(); | 647 | const auto& memory_layout = m_system.Kernel().MemoryLayout(); |
| 649 | 648 | ||
| 650 | // Empty groups are necessarily invalid. | 649 | // Empty groups are necessarily invalid. |
| @@ -942,9 +941,6 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add | |||
| 942 | 941 | ||
| 943 | ON_RESULT_FAILURE { | 942 | ON_RESULT_FAILURE { |
| 944 | if (cur_mapped_addr != dst_addr) { | 943 | if (cur_mapped_addr != dst_addr) { |
| 945 | // HACK: Manually close the pages. | ||
| 946 | HACK_ClosePages(dst_addr, (cur_mapped_addr - dst_addr) / PageSize); | ||
| 947 | |||
| 948 | ASSERT(Operate(dst_addr, (cur_mapped_addr - dst_addr) / PageSize, | 944 | ASSERT(Operate(dst_addr, (cur_mapped_addr - dst_addr) / PageSize, |
| 949 | KMemoryPermission::None, OperationType::Unmap) | 945 | KMemoryPermission::None, OperationType::Unmap) |
| 950 | .IsSuccess()); | 946 | .IsSuccess()); |
| @@ -1020,9 +1016,6 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add | |||
| 1020 | // Map the page. | 1016 | // Map the page. |
| 1021 | R_TRY(Operate(cur_mapped_addr, 1, test_perm, OperationType::Map, start_partial_page)); | 1017 | R_TRY(Operate(cur_mapped_addr, 1, test_perm, OperationType::Map, start_partial_page)); |
| 1022 | 1018 | ||
| 1023 | // HACK: Manually open the pages. | ||
| 1024 | HACK_OpenPages(start_partial_page, 1); | ||
| 1025 | |||
| 1026 | // Update tracking extents. | 1019 | // Update tracking extents. |
| 1027 | cur_mapped_addr += PageSize; | 1020 | cur_mapped_addr += PageSize; |
| 1028 | cur_block_addr += PageSize; | 1021 | cur_block_addr += PageSize; |
| @@ -1051,9 +1044,6 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add | |||
| 1051 | R_TRY(Operate(cur_mapped_addr, cur_block_size / PageSize, test_perm, OperationType::Map, | 1044 | R_TRY(Operate(cur_mapped_addr, cur_block_size / PageSize, test_perm, OperationType::Map, |
| 1052 | cur_block_addr)); | 1045 | cur_block_addr)); |
| 1053 | 1046 | ||
| 1054 | // HACK: Manually open the pages. | ||
| 1055 | HACK_OpenPages(cur_block_addr, cur_block_size / PageSize); | ||
| 1056 | |||
| 1057 | // Update tracking extents. | 1047 | // Update tracking extents. |
| 1058 | cur_mapped_addr += cur_block_size; | 1048 | cur_mapped_addr += cur_block_size; |
| 1059 | cur_block_addr = next_entry.phys_addr; | 1049 | cur_block_addr = next_entry.phys_addr; |
| @@ -1073,9 +1063,6 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add | |||
| 1073 | R_TRY(Operate(cur_mapped_addr, last_block_size / PageSize, test_perm, OperationType::Map, | 1063 | R_TRY(Operate(cur_mapped_addr, last_block_size / PageSize, test_perm, OperationType::Map, |
| 1074 | cur_block_addr)); | 1064 | cur_block_addr)); |
| 1075 | 1065 | ||
| 1076 | // HACK: Manually open the pages. | ||
| 1077 | HACK_OpenPages(cur_block_addr, last_block_size / PageSize); | ||
| 1078 | |||
| 1079 | // Update tracking extents. | 1066 | // Update tracking extents. |
| 1080 | cur_mapped_addr += last_block_size; | 1067 | cur_mapped_addr += last_block_size; |
| 1081 | cur_block_addr += last_block_size; | 1068 | cur_block_addr += last_block_size; |
| @@ -1107,9 +1094,6 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add | |||
| 1107 | 1094 | ||
| 1108 | // Map the page. | 1095 | // Map the page. |
| 1109 | R_TRY(Operate(cur_mapped_addr, 1, test_perm, OperationType::Map, end_partial_page)); | 1096 | R_TRY(Operate(cur_mapped_addr, 1, test_perm, OperationType::Map, end_partial_page)); |
| 1110 | |||
| 1111 | // HACK: Manually open the pages. | ||
| 1112 | HACK_OpenPages(end_partial_page, 1); | ||
| 1113 | } | 1097 | } |
| 1114 | 1098 | ||
| 1115 | // Update memory blocks to reflect our changes | 1099 | // Update memory blocks to reflect our changes |
| @@ -1211,9 +1195,6 @@ Result KPageTable::CleanupForIpcServer(VAddr address, size_t size, KMemoryState | |||
| 1211 | const size_t aligned_size = aligned_end - aligned_start; | 1195 | const size_t aligned_size = aligned_end - aligned_start; |
| 1212 | const size_t aligned_num_pages = aligned_size / PageSize; | 1196 | const size_t aligned_num_pages = aligned_size / PageSize; |
| 1213 | 1197 | ||
| 1214 | // HACK: Manually close the pages. | ||
| 1215 | HACK_ClosePages(aligned_start, aligned_num_pages); | ||
| 1216 | |||
| 1217 | // Unmap the pages. | 1198 | // Unmap the pages. |
| 1218 | R_TRY(Operate(aligned_start, aligned_num_pages, KMemoryPermission::None, OperationType::Unmap)); | 1199 | R_TRY(Operate(aligned_start, aligned_num_pages, KMemoryPermission::None, OperationType::Unmap)); |
| 1219 | 1200 | ||
| @@ -1501,17 +1482,6 @@ void KPageTable::CleanupForIpcClientOnServerSetupFailure([[maybe_unused]] PageLi | |||
| 1501 | } | 1482 | } |
| 1502 | } | 1483 | } |
| 1503 | 1484 | ||
| 1504 | void KPageTable::HACK_OpenPages(PAddr phys_addr, size_t num_pages) { | ||
| 1505 | m_system.Kernel().MemoryManager().OpenFirst(phys_addr, num_pages); | ||
| 1506 | } | ||
| 1507 | |||
| 1508 | void KPageTable::HACK_ClosePages(VAddr virt_addr, size_t num_pages) { | ||
| 1509 | for (size_t index = 0; index < num_pages; ++index) { | ||
| 1510 | const auto paddr = GetPhysicalAddr(virt_addr + (index * PageSize)); | ||
| 1511 | m_system.Kernel().MemoryManager().Close(paddr, 1); | ||
| 1512 | } | ||
| 1513 | } | ||
| 1514 | |||
| 1515 | Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) { | 1485 | Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) { |
| 1516 | // Lock the physical memory lock. | 1486 | // Lock the physical memory lock. |
| 1517 | KScopedLightLock phys_lk(m_map_physical_memory_lock); | 1487 | KScopedLightLock phys_lk(m_map_physical_memory_lock); |
| @@ -1572,7 +1542,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) { | |||
| 1572 | R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); | 1542 | R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); |
| 1573 | 1543 | ||
| 1574 | // Allocate pages for the new memory. | 1544 | // Allocate pages for the new memory. |
| 1575 | KPageGroup pg; | 1545 | KPageGroup pg{m_kernel, m_block_info_manager}; |
| 1576 | R_TRY(m_system.Kernel().MemoryManager().AllocateForProcess( | 1546 | R_TRY(m_system.Kernel().MemoryManager().AllocateForProcess( |
| 1577 | &pg, (size - mapped_size) / PageSize, m_allocate_option, 0, 0)); | 1547 | &pg, (size - mapped_size) / PageSize, m_allocate_option, 0, 0)); |
| 1578 | 1548 | ||
| @@ -1650,7 +1620,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) { | |||
| 1650 | KScopedPageTableUpdater updater(this); | 1620 | KScopedPageTableUpdater updater(this); |
| 1651 | 1621 | ||
| 1652 | // Prepare to iterate over the memory. | 1622 | // Prepare to iterate over the memory. |
| 1653 | auto pg_it = pg.Nodes().begin(); | 1623 | auto pg_it = pg.begin(); |
| 1654 | PAddr pg_phys_addr = pg_it->GetAddress(); | 1624 | PAddr pg_phys_addr = pg_it->GetAddress(); |
| 1655 | size_t pg_pages = pg_it->GetNumPages(); | 1625 | size_t pg_pages = pg_it->GetNumPages(); |
| 1656 | 1626 | ||
| @@ -1680,9 +1650,6 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) { | |||
| 1680 | last_unmap_address + 1 - cur_address) / | 1650 | last_unmap_address + 1 - cur_address) / |
| 1681 | PageSize; | 1651 | PageSize; |
| 1682 | 1652 | ||
| 1683 | // HACK: Manually close the pages. | ||
| 1684 | HACK_ClosePages(cur_address, cur_pages); | ||
| 1685 | |||
| 1686 | // Unmap. | 1653 | // Unmap. |
| 1687 | ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None, | 1654 | ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None, |
| 1688 | OperationType::Unmap) | 1655 | OperationType::Unmap) |
| @@ -1703,7 +1670,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) { | |||
| 1703 | // Release any remaining unmapped memory. | 1670 | // Release any remaining unmapped memory. |
| 1704 | m_system.Kernel().MemoryManager().OpenFirst(pg_phys_addr, pg_pages); | 1671 | m_system.Kernel().MemoryManager().OpenFirst(pg_phys_addr, pg_pages); |
| 1705 | m_system.Kernel().MemoryManager().Close(pg_phys_addr, pg_pages); | 1672 | m_system.Kernel().MemoryManager().Close(pg_phys_addr, pg_pages); |
| 1706 | for (++pg_it; pg_it != pg.Nodes().end(); ++pg_it) { | 1673 | for (++pg_it; pg_it != pg.end(); ++pg_it) { |
| 1707 | m_system.Kernel().MemoryManager().OpenFirst(pg_it->GetAddress(), | 1674 | m_system.Kernel().MemoryManager().OpenFirst(pg_it->GetAddress(), |
| 1708 | pg_it->GetNumPages()); | 1675 | pg_it->GetNumPages()); |
| 1709 | m_system.Kernel().MemoryManager().Close(pg_it->GetAddress(), | 1676 | m_system.Kernel().MemoryManager().Close(pg_it->GetAddress(), |
| @@ -1731,7 +1698,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) { | |||
| 1731 | // Check if we're at the end of the physical block. | 1698 | // Check if we're at the end of the physical block. |
| 1732 | if (pg_pages == 0) { | 1699 | if (pg_pages == 0) { |
| 1733 | // Ensure there are more pages to map. | 1700 | // Ensure there are more pages to map. |
| 1734 | ASSERT(pg_it != pg.Nodes().end()); | 1701 | ASSERT(pg_it != pg.end()); |
| 1735 | 1702 | ||
| 1736 | // Advance our physical block. | 1703 | // Advance our physical block. |
| 1737 | ++pg_it; | 1704 | ++pg_it; |
| @@ -1742,10 +1709,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) { | |||
| 1742 | // Map whatever we can. | 1709 | // Map whatever we can. |
| 1743 | const size_t cur_pages = std::min(pg_pages, map_pages); | 1710 | const size_t cur_pages = std::min(pg_pages, map_pages); |
| 1744 | R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::UserReadWrite, | 1711 | R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::UserReadWrite, |
| 1745 | OperationType::Map, pg_phys_addr)); | 1712 | OperationType::MapFirst, pg_phys_addr)); |
| 1746 | |||
| 1747 | // HACK: Manually open the pages. | ||
| 1748 | HACK_OpenPages(pg_phys_addr, cur_pages); | ||
| 1749 | 1713 | ||
| 1750 | // Advance. | 1714 | // Advance. |
| 1751 | cur_address += cur_pages * PageSize; | 1715 | cur_address += cur_pages * PageSize; |
| @@ -1888,9 +1852,6 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) { | |||
| 1888 | last_address + 1 - cur_address) / | 1852 | last_address + 1 - cur_address) / |
| 1889 | PageSize; | 1853 | PageSize; |
| 1890 | 1854 | ||
| 1891 | // HACK: Manually close the pages. | ||
| 1892 | HACK_ClosePages(cur_address, cur_pages); | ||
| 1893 | |||
| 1894 | // Unmap. | 1855 | // Unmap. |
| 1895 | ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None, OperationType::Unmap) | 1856 | ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None, OperationType::Unmap) |
| 1896 | .IsSuccess()); | 1857 | .IsSuccess()); |
| @@ -1955,7 +1916,7 @@ Result KPageTable::MapMemory(VAddr dst_address, VAddr src_address, size_t size) | |||
| 1955 | R_TRY(dst_allocator_result); | 1916 | R_TRY(dst_allocator_result); |
| 1956 | 1917 | ||
| 1957 | // Map the memory. | 1918 | // Map the memory. |
| 1958 | KPageGroup page_linked_list; | 1919 | KPageGroup page_linked_list{m_kernel, m_block_info_manager}; |
| 1959 | const size_t num_pages{size / PageSize}; | 1920 | const size_t num_pages{size / PageSize}; |
| 1960 | const KMemoryPermission new_src_perm = static_cast<KMemoryPermission>( | 1921 | const KMemoryPermission new_src_perm = static_cast<KMemoryPermission>( |
| 1961 | KMemoryPermission::KernelRead | KMemoryPermission::NotMapped); | 1922 | KMemoryPermission::KernelRead | KMemoryPermission::NotMapped); |
| @@ -2022,14 +1983,14 @@ Result KPageTable::UnmapMemory(VAddr dst_address, VAddr src_address, size_t size | |||
| 2022 | num_dst_allocator_blocks); | 1983 | num_dst_allocator_blocks); |
| 2023 | R_TRY(dst_allocator_result); | 1984 | R_TRY(dst_allocator_result); |
| 2024 | 1985 | ||
| 2025 | KPageGroup src_pages; | 1986 | KPageGroup src_pages{m_kernel, m_block_info_manager}; |
| 2026 | KPageGroup dst_pages; | 1987 | KPageGroup dst_pages{m_kernel, m_block_info_manager}; |
| 2027 | const size_t num_pages{size / PageSize}; | 1988 | const size_t num_pages{size / PageSize}; |
| 2028 | 1989 | ||
| 2029 | AddRegionToPages(src_address, num_pages, src_pages); | 1990 | AddRegionToPages(src_address, num_pages, src_pages); |
| 2030 | AddRegionToPages(dst_address, num_pages, dst_pages); | 1991 | AddRegionToPages(dst_address, num_pages, dst_pages); |
| 2031 | 1992 | ||
| 2032 | R_UNLESS(dst_pages.IsEqual(src_pages), ResultInvalidMemoryRegion); | 1993 | R_UNLESS(dst_pages.IsEquivalentTo(src_pages), ResultInvalidMemoryRegion); |
| 2033 | 1994 | ||
| 2034 | { | 1995 | { |
| 2035 | auto block_guard = detail::ScopeExit([&] { MapPages(dst_address, dst_pages, dst_perm); }); | 1996 | auto block_guard = detail::ScopeExit([&] { MapPages(dst_address, dst_pages, dst_perm); }); |
| @@ -2060,7 +2021,7 @@ Result KPageTable::MapPages(VAddr addr, const KPageGroup& page_linked_list, | |||
| 2060 | 2021 | ||
| 2061 | VAddr cur_addr{addr}; | 2022 | VAddr cur_addr{addr}; |
| 2062 | 2023 | ||
| 2063 | for (const auto& node : page_linked_list.Nodes()) { | 2024 | for (const auto& node : page_linked_list) { |
| 2064 | if (const auto result{ | 2025 | if (const auto result{ |
| 2065 | Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())}; | 2026 | Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())}; |
| 2066 | result.IsError()) { | 2027 | result.IsError()) { |
| @@ -2160,7 +2121,7 @@ Result KPageTable::UnmapPages(VAddr addr, const KPageGroup& page_linked_list) { | |||
| 2160 | 2121 | ||
| 2161 | VAddr cur_addr{addr}; | 2122 | VAddr cur_addr{addr}; |
| 2162 | 2123 | ||
| 2163 | for (const auto& node : page_linked_list.Nodes()) { | 2124 | for (const auto& node : page_linked_list) { |
| 2164 | if (const auto result{Operate(cur_addr, node.GetNumPages(), KMemoryPermission::None, | 2125 | if (const auto result{Operate(cur_addr, node.GetNumPages(), KMemoryPermission::None, |
| 2165 | OperationType::Unmap)}; | 2126 | OperationType::Unmap)}; |
| 2166 | result.IsError()) { | 2127 | result.IsError()) { |
| @@ -2527,13 +2488,13 @@ Result KPageTable::SetHeapSize(VAddr* out, size_t size) { | |||
| 2527 | R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); | 2488 | R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); |
| 2528 | 2489 | ||
| 2529 | // Allocate pages for the heap extension. | 2490 | // Allocate pages for the heap extension. |
| 2530 | KPageGroup pg; | 2491 | KPageGroup pg{m_kernel, m_block_info_manager}; |
| 2531 | R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen( | 2492 | R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen( |
| 2532 | &pg, allocation_size / PageSize, | 2493 | &pg, allocation_size / PageSize, |
| 2533 | KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option))); | 2494 | KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option))); |
| 2534 | 2495 | ||
| 2535 | // Clear all the newly allocated pages. | 2496 | // Clear all the newly allocated pages. |
| 2536 | for (const auto& it : pg.Nodes()) { | 2497 | for (const auto& it : pg) { |
| 2537 | std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()), m_heap_fill_value, | 2498 | std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()), m_heap_fill_value, |
| 2538 | it.GetSize()); | 2499 | it.GetSize()); |
| 2539 | } | 2500 | } |
| @@ -2610,11 +2571,23 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(size_t needed_num_pages, size_ | |||
| 2610 | if (is_map_only) { | 2571 | if (is_map_only) { |
| 2611 | R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr)); | 2572 | R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr)); |
| 2612 | } else { | 2573 | } else { |
| 2613 | KPageGroup page_group; | 2574 | // Create a page group tohold the pages we allocate. |
| 2614 | R_TRY(m_system.Kernel().MemoryManager().AllocateForProcess( | 2575 | KPageGroup pg{m_kernel, m_block_info_manager}; |
| 2615 | &page_group, needed_num_pages, | 2576 | |
| 2616 | KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option), 0, 0)); | 2577 | R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen( |
| 2617 | R_TRY(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup)); | 2578 | &pg, needed_num_pages, |
| 2579 | KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option))); | ||
| 2580 | |||
| 2581 | // Ensure that the page group is closed when we're done working with it. | ||
| 2582 | SCOPE_EXIT({ pg.Close(); }); | ||
| 2583 | |||
| 2584 | // Clear all pages. | ||
| 2585 | for (const auto& it : pg) { | ||
| 2586 | std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()), | ||
| 2587 | m_heap_fill_value, it.GetSize()); | ||
| 2588 | } | ||
| 2589 | |||
| 2590 | R_TRY(Operate(addr, needed_num_pages, pg, OperationType::MapGroup)); | ||
| 2618 | } | 2591 | } |
| 2619 | 2592 | ||
| 2620 | // Update the blocks. | 2593 | // Update the blocks. |
| @@ -2795,19 +2768,28 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, const KPageGroup& page_ | |||
| 2795 | ASSERT(num_pages > 0); | 2768 | ASSERT(num_pages > 0); |
| 2796 | ASSERT(num_pages == page_group.GetNumPages()); | 2769 | ASSERT(num_pages == page_group.GetNumPages()); |
| 2797 | 2770 | ||
| 2798 | for (const auto& node : page_group.Nodes()) { | 2771 | switch (operation) { |
| 2799 | const size_t size{node.GetNumPages() * PageSize}; | 2772 | case OperationType::MapGroup: { |
| 2773 | // We want to maintain a new reference to every page in the group. | ||
| 2774 | KScopedPageGroup spg(page_group); | ||
| 2775 | |||
| 2776 | for (const auto& node : page_group) { | ||
| 2777 | const size_t size{node.GetNumPages() * PageSize}; | ||
| 2800 | 2778 | ||
| 2801 | switch (operation) { | 2779 | // Map the pages. |
| 2802 | case OperationType::MapGroup: | ||
| 2803 | m_system.Memory().MapMemoryRegion(*m_page_table_impl, addr, size, node.GetAddress()); | 2780 | m_system.Memory().MapMemoryRegion(*m_page_table_impl, addr, size, node.GetAddress()); |
| 2804 | break; | 2781 | |
| 2805 | default: | 2782 | addr += size; |
| 2806 | ASSERT(false); | ||
| 2807 | break; | ||
| 2808 | } | 2783 | } |
| 2809 | 2784 | ||
| 2810 | addr += size; | 2785 | // We succeeded! We want to persist the reference to the pages. |
| 2786 | spg.CancelClose(); | ||
| 2787 | |||
| 2788 | break; | ||
| 2789 | } | ||
| 2790 | default: | ||
| 2791 | ASSERT(false); | ||
| 2792 | break; | ||
| 2811 | } | 2793 | } |
| 2812 | 2794 | ||
| 2813 | R_SUCCEED(); | 2795 | R_SUCCEED(); |
| @@ -2822,13 +2804,29 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, KMemoryPermission perm, | |||
| 2822 | ASSERT(ContainsPages(addr, num_pages)); | 2804 | ASSERT(ContainsPages(addr, num_pages)); |
| 2823 | 2805 | ||
| 2824 | switch (operation) { | 2806 | switch (operation) { |
| 2825 | case OperationType::Unmap: | 2807 | case OperationType::Unmap: { |
| 2808 | // Ensure that any pages we track close on exit. | ||
| 2809 | KPageGroup pages_to_close{m_kernel, this->GetBlockInfoManager()}; | ||
| 2810 | SCOPE_EXIT({ pages_to_close.CloseAndReset(); }); | ||
| 2811 | |||
| 2812 | this->AddRegionToPages(addr, num_pages, pages_to_close); | ||
| 2826 | m_system.Memory().UnmapRegion(*m_page_table_impl, addr, num_pages * PageSize); | 2813 | m_system.Memory().UnmapRegion(*m_page_table_impl, addr, num_pages * PageSize); |
| 2827 | break; | 2814 | break; |
| 2815 | } | ||
| 2816 | case OperationType::MapFirst: | ||
| 2828 | case OperationType::Map: { | 2817 | case OperationType::Map: { |
| 2829 | ASSERT(map_addr); | 2818 | ASSERT(map_addr); |
| 2830 | ASSERT(Common::IsAligned(map_addr, PageSize)); | 2819 | ASSERT(Common::IsAligned(map_addr, PageSize)); |
| 2831 | m_system.Memory().MapMemoryRegion(*m_page_table_impl, addr, num_pages * PageSize, map_addr); | 2820 | m_system.Memory().MapMemoryRegion(*m_page_table_impl, addr, num_pages * PageSize, map_addr); |
| 2821 | |||
| 2822 | // Open references to pages, if we should. | ||
| 2823 | if (IsHeapPhysicalAddress(m_kernel.MemoryLayout(), map_addr)) { | ||
| 2824 | if (operation == OperationType::MapFirst) { | ||
| 2825 | m_kernel.MemoryManager().OpenFirst(map_addr, num_pages); | ||
| 2826 | } else { | ||
| 2827 | m_kernel.MemoryManager().Open(map_addr, num_pages); | ||
| 2828 | } | ||
| 2829 | } | ||
| 2832 | break; | 2830 | break; |
| 2833 | } | 2831 | } |
| 2834 | case OperationType::Separate: { | 2832 | case OperationType::Separate: { |
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index f1ca785d7..0a454b05b 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h | |||
| @@ -107,6 +107,10 @@ public: | |||
| 107 | return *m_page_table_impl; | 107 | return *m_page_table_impl; |
| 108 | } | 108 | } |
| 109 | 109 | ||
| 110 | KBlockInfoManager* GetBlockInfoManager() { | ||
| 111 | return m_block_info_manager; | ||
| 112 | } | ||
| 113 | |||
| 110 | bool CanContain(VAddr addr, size_t size, KMemoryState state) const; | 114 | bool CanContain(VAddr addr, size_t size, KMemoryState state) const; |
| 111 | 115 | ||
| 112 | protected: | 116 | protected: |
| @@ -261,10 +265,6 @@ private: | |||
| 261 | void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, VAddr address, | 265 | void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, VAddr address, |
| 262 | size_t size, KMemoryPermission prot_perm); | 266 | size_t size, KMemoryPermission prot_perm); |
| 263 | 267 | ||
| 264 | // HACK: These will be removed once we automatically manage page reference counts. | ||
| 265 | void HACK_OpenPages(PAddr phys_addr, size_t num_pages); | ||
| 266 | void HACK_ClosePages(VAddr virt_addr, size_t num_pages); | ||
| 267 | |||
| 268 | mutable KLightLock m_general_lock; | 268 | mutable KLightLock m_general_lock; |
| 269 | mutable KLightLock m_map_physical_memory_lock; | 269 | mutable KLightLock m_map_physical_memory_lock; |
| 270 | 270 | ||
| @@ -488,6 +488,7 @@ private: | |||
| 488 | std::unique_ptr<Common::PageTable> m_page_table_impl; | 488 | std::unique_ptr<Common::PageTable> m_page_table_impl; |
| 489 | 489 | ||
| 490 | Core::System& m_system; | 490 | Core::System& m_system; |
| 491 | KernelCore& m_kernel; | ||
| 491 | }; | 492 | }; |
| 492 | 493 | ||
| 493 | } // namespace Kernel | 494 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index d1dc62401..a1abf5d68 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp | |||
| @@ -285,6 +285,17 @@ void KProcess::UnregisterThread(KThread* thread) { | |||
| 285 | thread_list.remove(thread); | 285 | thread_list.remove(thread); |
| 286 | } | 286 | } |
| 287 | 287 | ||
| 288 | u64 KProcess::GetFreeThreadCount() const { | ||
| 289 | if (resource_limit != nullptr) { | ||
| 290 | const auto current_value = | ||
| 291 | resource_limit->GetCurrentValue(LimitableResource::ThreadCountMax); | ||
| 292 | const auto limit_value = resource_limit->GetLimitValue(LimitableResource::ThreadCountMax); | ||
| 293 | return limit_value - current_value; | ||
| 294 | } else { | ||
| 295 | return 0; | ||
| 296 | } | ||
| 297 | } | ||
| 298 | |||
| 288 | Result KProcess::Reset() { | 299 | Result KProcess::Reset() { |
| 289 | // Lock the process and the scheduler. | 300 | // Lock the process and the scheduler. |
| 290 | KScopedLightLock lk(state_lock); | 301 | KScopedLightLock lk(state_lock); |
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index 2e0cc3d0b..09bf2f1d0 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h | |||
| @@ -304,6 +304,9 @@ public: | |||
| 304 | /// from this process' thread list. | 304 | /// from this process' thread list. |
| 305 | void UnregisterThread(KThread* thread); | 305 | void UnregisterThread(KThread* thread); |
| 306 | 306 | ||
| 307 | /// Retrieves the number of available threads for this process. | ||
| 308 | u64 GetFreeThreadCount() const; | ||
| 309 | |||
| 307 | /// Clears the signaled state of the process if and only if it's signaled. | 310 | /// Clears the signaled state of the process if and only if it's signaled. |
| 308 | /// | 311 | /// |
| 309 | /// @pre The process must not be already terminated. If this is called on a | 312 | /// @pre The process must not be already terminated. If this is called on a |
diff --git a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h index 76c095e69..76db65a4d 100644 --- a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h +++ b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h | |||
| @@ -5,9 +5,9 @@ | |||
| 5 | 5 | ||
| 6 | #include "common/common_types.h" | 6 | #include "common/common_types.h" |
| 7 | #include "core/hle/kernel/global_scheduler_context.h" | 7 | #include "core/hle/kernel/global_scheduler_context.h" |
| 8 | #include "core/hle/kernel/k_hardware_timer.h" | ||
| 8 | #include "core/hle/kernel/k_thread.h" | 9 | #include "core/hle/kernel/k_thread.h" |
| 9 | #include "core/hle/kernel/kernel.h" | 10 | #include "core/hle/kernel/kernel.h" |
| 10 | #include "core/hle/kernel/time_manager.h" | ||
| 11 | 11 | ||
| 12 | namespace Kernel { | 12 | namespace Kernel { |
| 13 | 13 | ||
| @@ -22,7 +22,7 @@ public: | |||
| 22 | ~KScopedSchedulerLockAndSleep() { | 22 | ~KScopedSchedulerLockAndSleep() { |
| 23 | // Register the sleep. | 23 | // Register the sleep. |
| 24 | if (timeout_tick > 0) { | 24 | if (timeout_tick > 0) { |
| 25 | kernel.TimeManager().ScheduleTimeEvent(thread, timeout_tick); | 25 | kernel.HardwareTimer().RegisterTask(thread, timeout_tick); |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | // Unlock the scheduler. | 28 | // Unlock the scheduler. |
diff --git a/src/core/hle/kernel/k_shared_memory.cpp b/src/core/hle/kernel/k_shared_memory.cpp index 10cd4c43d..3cf2b5d91 100644 --- a/src/core/hle/kernel/k_shared_memory.cpp +++ b/src/core/hle/kernel/k_shared_memory.cpp | |||
| @@ -6,31 +6,29 @@ | |||
| 6 | #include "core/hle/kernel/k_page_table.h" | 6 | #include "core/hle/kernel/k_page_table.h" |
| 7 | #include "core/hle/kernel/k_scoped_resource_reservation.h" | 7 | #include "core/hle/kernel/k_scoped_resource_reservation.h" |
| 8 | #include "core/hle/kernel/k_shared_memory.h" | 8 | #include "core/hle/kernel/k_shared_memory.h" |
| 9 | #include "core/hle/kernel/k_system_resource.h" | ||
| 9 | #include "core/hle/kernel/kernel.h" | 10 | #include "core/hle/kernel/kernel.h" |
| 10 | #include "core/hle/kernel/svc_results.h" | 11 | #include "core/hle/kernel/svc_results.h" |
| 11 | 12 | ||
| 12 | namespace Kernel { | 13 | namespace Kernel { |
| 13 | 14 | ||
| 14 | KSharedMemory::KSharedMemory(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_} {} | 15 | KSharedMemory::KSharedMemory(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_} {} |
| 15 | 16 | KSharedMemory::~KSharedMemory() = default; | |
| 16 | KSharedMemory::~KSharedMemory() { | ||
| 17 | kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemoryMax, size); | ||
| 18 | } | ||
| 19 | 17 | ||
| 20 | Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_, | 18 | Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_, |
| 21 | KPageGroup&& page_list_, Svc::MemoryPermission owner_permission_, | 19 | Svc::MemoryPermission owner_permission_, |
| 22 | Svc::MemoryPermission user_permission_, PAddr physical_address_, | 20 | Svc::MemoryPermission user_permission_, std::size_t size_, |
| 23 | std::size_t size_, std::string name_) { | 21 | std::string name_) { |
| 24 | // Set members. | 22 | // Set members. |
| 25 | owner_process = owner_process_; | 23 | owner_process = owner_process_; |
| 26 | device_memory = &device_memory_; | 24 | device_memory = &device_memory_; |
| 27 | page_list = std::move(page_list_); | ||
| 28 | owner_permission = owner_permission_; | 25 | owner_permission = owner_permission_; |
| 29 | user_permission = user_permission_; | 26 | user_permission = user_permission_; |
| 30 | physical_address = physical_address_; | 27 | size = Common::AlignUp(size_, PageSize); |
| 31 | size = size_; | ||
| 32 | name = std::move(name_); | 28 | name = std::move(name_); |
| 33 | 29 | ||
| 30 | const size_t num_pages = Common::DivideUp(size, PageSize); | ||
| 31 | |||
| 34 | // Get the resource limit. | 32 | // Get the resource limit. |
| 35 | KResourceLimit* reslimit = kernel.GetSystemResourceLimit(); | 33 | KResourceLimit* reslimit = kernel.GetSystemResourceLimit(); |
| 36 | 34 | ||
| @@ -39,6 +37,18 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o | |||
| 39 | size_); | 37 | size_); |
| 40 | R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); | 38 | R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); |
| 41 | 39 | ||
| 40 | // Allocate the memory. | ||
| 41 | |||
| 42 | //! HACK: Open continuous mapping from sysmodule pool. | ||
| 43 | auto option = KMemoryManager::EncodeOption(KMemoryManager::Pool::Secure, | ||
| 44 | KMemoryManager::Direction::FromBack); | ||
| 45 | physical_address = kernel.MemoryManager().AllocateAndOpenContinuous(num_pages, 1, option); | ||
| 46 | R_UNLESS(physical_address != 0, ResultOutOfMemory); | ||
| 47 | |||
| 48 | //! Insert the result into our page group. | ||
| 49 | page_group.emplace(kernel, &kernel.GetSystemSystemResource().GetBlockInfoManager()); | ||
| 50 | page_group->AddBlock(physical_address, num_pages); | ||
| 51 | |||
| 42 | // Commit our reservation. | 52 | // Commit our reservation. |
| 43 | memory_reservation.Commit(); | 53 | memory_reservation.Commit(); |
| 44 | 54 | ||
| @@ -50,12 +60,18 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o | |||
| 50 | is_initialized = true; | 60 | is_initialized = true; |
| 51 | 61 | ||
| 52 | // Clear all pages in the memory. | 62 | // Clear all pages in the memory. |
| 53 | std::memset(device_memory_.GetPointer<void>(physical_address_), 0, size_); | 63 | for (const auto& block : *page_group) { |
| 64 | std::memset(device_memory_.GetPointer<void>(block.GetAddress()), 0, block.GetSize()); | ||
| 65 | } | ||
| 54 | 66 | ||
| 55 | return ResultSuccess; | 67 | return ResultSuccess; |
| 56 | } | 68 | } |
| 57 | 69 | ||
| 58 | void KSharedMemory::Finalize() { | 70 | void KSharedMemory::Finalize() { |
| 71 | // Close and finalize the page group. | ||
| 72 | page_group->Close(); | ||
| 73 | page_group->Finalize(); | ||
| 74 | |||
| 59 | // Release the memory reservation. | 75 | // Release the memory reservation. |
| 60 | resource_limit->Release(LimitableResource::PhysicalMemoryMax, size); | 76 | resource_limit->Release(LimitableResource::PhysicalMemoryMax, size); |
| 61 | resource_limit->Close(); | 77 | resource_limit->Close(); |
| @@ -65,32 +81,28 @@ void KSharedMemory::Finalize() { | |||
| 65 | } | 81 | } |
| 66 | 82 | ||
| 67 | Result KSharedMemory::Map(KProcess& target_process, VAddr address, std::size_t map_size, | 83 | Result KSharedMemory::Map(KProcess& target_process, VAddr address, std::size_t map_size, |
| 68 | Svc::MemoryPermission permissions) { | 84 | Svc::MemoryPermission map_perm) { |
| 69 | const u64 page_count{(map_size + PageSize - 1) / PageSize}; | 85 | // Validate the size. |
| 70 | 86 | R_UNLESS(size == map_size, ResultInvalidSize); | |
| 71 | if (page_list.GetNumPages() != page_count) { | ||
| 72 | UNIMPLEMENTED_MSG("Page count does not match"); | ||
| 73 | } | ||
| 74 | 87 | ||
| 75 | const Svc::MemoryPermission expected = | 88 | // Validate the permission. |
| 89 | const Svc::MemoryPermission test_perm = | ||
| 76 | &target_process == owner_process ? owner_permission : user_permission; | 90 | &target_process == owner_process ? owner_permission : user_permission; |
| 77 | 91 | if (test_perm == Svc::MemoryPermission::DontCare) { | |
| 78 | if (permissions != expected) { | 92 | ASSERT(map_perm == Svc::MemoryPermission::Read || map_perm == Svc::MemoryPermission::Write); |
| 79 | UNIMPLEMENTED_MSG("Permission does not match"); | 93 | } else { |
| 94 | R_UNLESS(map_perm == test_perm, ResultInvalidNewMemoryPermission); | ||
| 80 | } | 95 | } |
| 81 | 96 | ||
| 82 | return target_process.PageTable().MapPages(address, page_list, KMemoryState::Shared, | 97 | return target_process.PageTable().MapPages(address, *page_group, KMemoryState::Shared, |
| 83 | ConvertToKMemoryPermission(permissions)); | 98 | ConvertToKMemoryPermission(map_perm)); |
| 84 | } | 99 | } |
| 85 | 100 | ||
| 86 | Result KSharedMemory::Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size) { | 101 | Result KSharedMemory::Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size) { |
| 87 | const u64 page_count{(unmap_size + PageSize - 1) / PageSize}; | 102 | // Validate the size. |
| 88 | 103 | R_UNLESS(size == unmap_size, ResultInvalidSize); | |
| 89 | if (page_list.GetNumPages() != page_count) { | ||
| 90 | UNIMPLEMENTED_MSG("Page count does not match"); | ||
| 91 | } | ||
| 92 | 104 | ||
| 93 | return target_process.PageTable().UnmapPages(address, page_list, KMemoryState::Shared); | 105 | return target_process.PageTable().UnmapPages(address, *page_group, KMemoryState::Shared); |
| 94 | } | 106 | } |
| 95 | 107 | ||
| 96 | } // namespace Kernel | 108 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/k_shared_memory.h b/src/core/hle/kernel/k_shared_memory.h index a96c55a3e..8b29f0b4a 100644 --- a/src/core/hle/kernel/k_shared_memory.h +++ b/src/core/hle/kernel/k_shared_memory.h | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <optional> | ||
| 6 | #include <string> | 7 | #include <string> |
| 7 | 8 | ||
| 8 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| @@ -26,9 +27,8 @@ public: | |||
| 26 | ~KSharedMemory() override; | 27 | ~KSharedMemory() override; |
| 27 | 28 | ||
| 28 | Result Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_, | 29 | Result Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_, |
| 29 | KPageGroup&& page_list_, Svc::MemoryPermission owner_permission_, | 30 | Svc::MemoryPermission owner_permission_, |
| 30 | Svc::MemoryPermission user_permission_, PAddr physical_address_, | 31 | Svc::MemoryPermission user_permission_, std::size_t size_, std::string name_); |
| 31 | std::size_t size_, std::string name_); | ||
| 32 | 32 | ||
| 33 | /** | 33 | /** |
| 34 | * Maps a shared memory block to an address in the target process' address space | 34 | * Maps a shared memory block to an address in the target process' address space |
| @@ -76,7 +76,7 @@ public: | |||
| 76 | private: | 76 | private: |
| 77 | Core::DeviceMemory* device_memory{}; | 77 | Core::DeviceMemory* device_memory{}; |
| 78 | KProcess* owner_process{}; | 78 | KProcess* owner_process{}; |
| 79 | KPageGroup page_list; | 79 | std::optional<KPageGroup> page_group{}; |
| 80 | Svc::MemoryPermission owner_permission{}; | 80 | Svc::MemoryPermission owner_permission{}; |
| 81 | Svc::MemoryPermission user_permission{}; | 81 | Svc::MemoryPermission user_permission{}; |
| 82 | PAddr physical_address{}; | 82 | PAddr physical_address{}; |
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index dc52b4ed3..7cd94a340 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | #include "core/hle/kernel/k_light_lock.h" | 22 | #include "core/hle/kernel/k_light_lock.h" |
| 23 | #include "core/hle/kernel/k_spin_lock.h" | 23 | #include "core/hle/kernel/k_spin_lock.h" |
| 24 | #include "core/hle/kernel/k_synchronization_object.h" | 24 | #include "core/hle/kernel/k_synchronization_object.h" |
| 25 | #include "core/hle/kernel/k_timer_task.h" | ||
| 25 | #include "core/hle/kernel/k_worker_task.h" | 26 | #include "core/hle/kernel/k_worker_task.h" |
| 26 | #include "core/hle/kernel/slab_helpers.h" | 27 | #include "core/hle/kernel/slab_helpers.h" |
| 27 | #include "core/hle/kernel/svc_common.h" | 28 | #include "core/hle/kernel/svc_common.h" |
| @@ -112,7 +113,8 @@ void SetCurrentThread(KernelCore& kernel, KThread* thread); | |||
| 112 | [[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel); | 113 | [[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel); |
| 113 | 114 | ||
| 114 | class KThread final : public KAutoObjectWithSlabHeapAndContainer<KThread, KWorkerTask>, | 115 | class KThread final : public KAutoObjectWithSlabHeapAndContainer<KThread, KWorkerTask>, |
| 115 | public boost::intrusive::list_base_hook<> { | 116 | public boost::intrusive::list_base_hook<>, |
| 117 | public KTimerTask { | ||
| 116 | KERNEL_AUTOOBJECT_TRAITS(KThread, KSynchronizationObject); | 118 | KERNEL_AUTOOBJECT_TRAITS(KThread, KSynchronizationObject); |
| 117 | 119 | ||
| 118 | private: | 120 | private: |
| @@ -840,4 +842,8 @@ private: | |||
| 840 | KernelCore& kernel; | 842 | KernelCore& kernel; |
| 841 | }; | 843 | }; |
| 842 | 844 | ||
| 845 | inline void KTimerTask::OnTimer() { | ||
| 846 | static_cast<KThread*>(this)->OnTimer(); | ||
| 847 | } | ||
| 848 | |||
| 843 | } // namespace Kernel | 849 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/k_thread_queue.cpp b/src/core/hle/kernel/k_thread_queue.cpp index 9f4e081ba..5f1dc97eb 100644 --- a/src/core/hle/kernel/k_thread_queue.cpp +++ b/src/core/hle/kernel/k_thread_queue.cpp | |||
| @@ -1,9 +1,9 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "core/hle/kernel/k_hardware_timer.h" | ||
| 4 | #include "core/hle/kernel/k_thread_queue.h" | 5 | #include "core/hle/kernel/k_thread_queue.h" |
| 5 | #include "core/hle/kernel/kernel.h" | 6 | #include "core/hle/kernel/kernel.h" |
| 6 | #include "core/hle/kernel/time_manager.h" | ||
| 7 | 7 | ||
| 8 | namespace Kernel { | 8 | namespace Kernel { |
| 9 | 9 | ||
| @@ -22,7 +22,7 @@ void KThreadQueue::EndWait(KThread* waiting_thread, Result wait_result) { | |||
| 22 | waiting_thread->ClearWaitQueue(); | 22 | waiting_thread->ClearWaitQueue(); |
| 23 | 23 | ||
| 24 | // Cancel the thread task. | 24 | // Cancel the thread task. |
| 25 | kernel.TimeManager().UnscheduleTimeEvent(waiting_thread); | 25 | kernel.HardwareTimer().CancelTask(waiting_thread); |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | void KThreadQueue::CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) { | 28 | void KThreadQueue::CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) { |
| @@ -37,7 +37,7 @@ void KThreadQueue::CancelWait(KThread* waiting_thread, Result wait_result, bool | |||
| 37 | 37 | ||
| 38 | // Cancel the thread task. | 38 | // Cancel the thread task. |
| 39 | if (cancel_timer_task) { | 39 | if (cancel_timer_task) { |
| 40 | kernel.TimeManager().UnscheduleTimeEvent(waiting_thread); | 40 | kernel.HardwareTimer().CancelTask(waiting_thread); |
| 41 | } | 41 | } |
| 42 | } | 42 | } |
| 43 | 43 | ||
diff --git a/src/core/hle/kernel/k_timer_task.h b/src/core/hle/kernel/k_timer_task.h new file mode 100644 index 000000000..66f0a5a90 --- /dev/null +++ b/src/core/hle/kernel/k_timer_task.h | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/intrusive_red_black_tree.h" | ||
| 7 | |||
| 8 | namespace Kernel { | ||
| 9 | |||
| 10 | class KTimerTask : public Common::IntrusiveRedBlackTreeBaseNode<KTimerTask> { | ||
| 11 | public: | ||
| 12 | static constexpr int Compare(const KTimerTask& lhs, const KTimerTask& rhs) { | ||
| 13 | if (lhs.GetTime() < rhs.GetTime()) { | ||
| 14 | return -1; | ||
| 15 | } else { | ||
| 16 | return 1; | ||
| 17 | } | ||
| 18 | } | ||
| 19 | |||
| 20 | constexpr explicit KTimerTask() = default; | ||
| 21 | |||
| 22 | constexpr void SetTime(s64 t) { | ||
| 23 | m_time = t; | ||
| 24 | } | ||
| 25 | |||
| 26 | constexpr s64 GetTime() const { | ||
| 27 | return m_time; | ||
| 28 | } | ||
| 29 | |||
| 30 | // NOTE: This is virtual in Nintendo's kernel. Prior to 13.0.0, KWaitObject was also a | ||
| 31 | // TimerTask; this is no longer the case. Since this is now KThread exclusive, we have | ||
| 32 | // devirtualized (see inline declaration for this inside k_thread.h). | ||
| 33 | void OnTimer(); | ||
| 34 | |||
| 35 | private: | ||
| 36 | // Absolute time in nanoseconds | ||
| 37 | s64 m_time{}; | ||
| 38 | }; | ||
| 39 | |||
| 40 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 288f97df5..1fb25f221 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -26,6 +26,7 @@ | |||
| 26 | #include "core/hle/kernel/k_client_port.h" | 26 | #include "core/hle/kernel/k_client_port.h" |
| 27 | #include "core/hle/kernel/k_dynamic_resource_manager.h" | 27 | #include "core/hle/kernel/k_dynamic_resource_manager.h" |
| 28 | #include "core/hle/kernel/k_handle_table.h" | 28 | #include "core/hle/kernel/k_handle_table.h" |
| 29 | #include "core/hle/kernel/k_hardware_timer.h" | ||
| 29 | #include "core/hle/kernel/k_memory_layout.h" | 30 | #include "core/hle/kernel/k_memory_layout.h" |
| 30 | #include "core/hle/kernel/k_memory_manager.h" | 31 | #include "core/hle/kernel/k_memory_manager.h" |
| 31 | #include "core/hle/kernel/k_page_buffer.h" | 32 | #include "core/hle/kernel/k_page_buffer.h" |
| @@ -39,7 +40,6 @@ | |||
| 39 | #include "core/hle/kernel/kernel.h" | 40 | #include "core/hle/kernel/kernel.h" |
| 40 | #include "core/hle/kernel/physical_core.h" | 41 | #include "core/hle/kernel/physical_core.h" |
| 41 | #include "core/hle/kernel/service_thread.h" | 42 | #include "core/hle/kernel/service_thread.h" |
| 42 | #include "core/hle/kernel/time_manager.h" | ||
| 43 | #include "core/hle/result.h" | 43 | #include "core/hle/result.h" |
| 44 | #include "core/hle/service/sm/sm.h" | 44 | #include "core/hle/service/sm/sm.h" |
| 45 | #include "core/memory.h" | 45 | #include "core/memory.h" |
| @@ -55,7 +55,7 @@ struct KernelCore::Impl { | |||
| 55 | static constexpr size_t ReservedDynamicPageCount = 64; | 55 | static constexpr size_t ReservedDynamicPageCount = 64; |
| 56 | 56 | ||
| 57 | explicit Impl(Core::System& system_, KernelCore& kernel_) | 57 | explicit Impl(Core::System& system_, KernelCore& kernel_) |
| 58 | : time_manager{system_}, service_threads_manager{1, "ServiceThreadsManager"}, | 58 | : service_threads_manager{1, "ServiceThreadsManager"}, |
| 59 | service_thread_barrier{2}, system{system_} {} | 59 | service_thread_barrier{2}, system{system_} {} |
| 60 | 60 | ||
| 61 | void SetMulticore(bool is_multi) { | 61 | void SetMulticore(bool is_multi) { |
| @@ -63,6 +63,9 @@ struct KernelCore::Impl { | |||
| 63 | } | 63 | } |
| 64 | 64 | ||
| 65 | void Initialize(KernelCore& kernel) { | 65 | void Initialize(KernelCore& kernel) { |
| 66 | hardware_timer = std::make_unique<Kernel::KHardwareTimer>(kernel); | ||
| 67 | hardware_timer->Initialize(); | ||
| 68 | |||
| 66 | global_object_list_container = std::make_unique<KAutoObjectWithListContainer>(kernel); | 69 | global_object_list_container = std::make_unique<KAutoObjectWithListContainer>(kernel); |
| 67 | global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel); | 70 | global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel); |
| 68 | global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel); | 71 | global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel); |
| @@ -91,6 +94,7 @@ struct KernelCore::Impl { | |||
| 91 | pt_heap_region.GetSize()); | 94 | pt_heap_region.GetSize()); |
| 92 | } | 95 | } |
| 93 | 96 | ||
| 97 | InitializeHackSharedMemory(); | ||
| 94 | RegisterHostThread(nullptr); | 98 | RegisterHostThread(nullptr); |
| 95 | 99 | ||
| 96 | default_service_thread = &CreateServiceThread(kernel, "DefaultServiceThread"); | 100 | default_service_thread = &CreateServiceThread(kernel, "DefaultServiceThread"); |
| @@ -104,12 +108,16 @@ struct KernelCore::Impl { | |||
| 104 | } | 108 | } |
| 105 | 109 | ||
| 106 | void CloseCurrentProcess() { | 110 | void CloseCurrentProcess() { |
| 107 | (*current_process).Finalize(); | 111 | KProcess* old_process = current_process.exchange(nullptr); |
| 108 | // current_process->Close(); | 112 | if (old_process == nullptr) { |
| 109 | // TODO: The current process should be destroyed based on accurate ref counting after | 113 | return; |
| 114 | } | ||
| 115 | |||
| 116 | // old_process->Close(); | ||
| 117 | // TODO: The process should be destroyed based on accurate ref counting after | ||
| 110 | // calling Close(). Adding a manual Destroy() call instead to avoid a memory leak. | 118 | // calling Close(). Adding a manual Destroy() call instead to avoid a memory leak. |
| 111 | (*current_process).Destroy(); | 119 | old_process->Finalize(); |
| 112 | current_process = nullptr; | 120 | old_process->Destroy(); |
| 113 | } | 121 | } |
| 114 | 122 | ||
| 115 | void Shutdown() { | 123 | void Shutdown() { |
| @@ -189,6 +197,9 @@ struct KernelCore::Impl { | |||
| 189 | // Ensure that the object list container is finalized and properly shutdown. | 197 | // Ensure that the object list container is finalized and properly shutdown. |
| 190 | global_object_list_container->Finalize(); | 198 | global_object_list_container->Finalize(); |
| 191 | global_object_list_container.reset(); | 199 | global_object_list_container.reset(); |
| 200 | |||
| 201 | hardware_timer->Finalize(); | ||
| 202 | hardware_timer.reset(); | ||
| 192 | } | 203 | } |
| 193 | 204 | ||
| 194 | void CloseServices() { | 205 | void CloseServices() { |
| @@ -716,14 +727,14 @@ struct KernelCore::Impl { | |||
| 716 | } | 727 | } |
| 717 | 728 | ||
| 718 | void InitializeMemoryLayout() { | 729 | void InitializeMemoryLayout() { |
| 719 | const auto system_pool = memory_layout->GetKernelSystemPoolRegionPhysicalExtents(); | ||
| 720 | |||
| 721 | // Initialize the memory manager. | 730 | // Initialize the memory manager. |
| 722 | memory_manager = std::make_unique<KMemoryManager>(system); | 731 | memory_manager = std::make_unique<KMemoryManager>(system); |
| 723 | const auto& management_region = memory_layout->GetPoolManagementRegion(); | 732 | const auto& management_region = memory_layout->GetPoolManagementRegion(); |
| 724 | ASSERT(management_region.GetEndAddress() != 0); | 733 | ASSERT(management_region.GetEndAddress() != 0); |
| 725 | memory_manager->Initialize(management_region.GetAddress(), management_region.GetSize()); | 734 | memory_manager->Initialize(management_region.GetAddress(), management_region.GetSize()); |
| 735 | } | ||
| 726 | 736 | ||
| 737 | void InitializeHackSharedMemory() { | ||
| 727 | // Setup memory regions for emulated processes | 738 | // Setup memory regions for emulated processes |
| 728 | // TODO(bunnei): These should not be hardcoded regions initialized within the kernel | 739 | // TODO(bunnei): These should not be hardcoded regions initialized within the kernel |
| 729 | constexpr std::size_t hid_size{0x40000}; | 740 | constexpr std::size_t hid_size{0x40000}; |
| @@ -732,39 +743,23 @@ struct KernelCore::Impl { | |||
| 732 | constexpr std::size_t time_size{0x1000}; | 743 | constexpr std::size_t time_size{0x1000}; |
| 733 | constexpr std::size_t hidbus_size{0x1000}; | 744 | constexpr std::size_t hidbus_size{0x1000}; |
| 734 | 745 | ||
| 735 | const PAddr hid_phys_addr{system_pool.GetAddress()}; | ||
| 736 | const PAddr font_phys_addr{system_pool.GetAddress() + hid_size}; | ||
| 737 | const PAddr irs_phys_addr{system_pool.GetAddress() + hid_size + font_size}; | ||
| 738 | const PAddr time_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size}; | ||
| 739 | const PAddr hidbus_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size + | ||
| 740 | time_size}; | ||
| 741 | |||
| 742 | hid_shared_mem = KSharedMemory::Create(system.Kernel()); | 746 | hid_shared_mem = KSharedMemory::Create(system.Kernel()); |
| 743 | font_shared_mem = KSharedMemory::Create(system.Kernel()); | 747 | font_shared_mem = KSharedMemory::Create(system.Kernel()); |
| 744 | irs_shared_mem = KSharedMemory::Create(system.Kernel()); | 748 | irs_shared_mem = KSharedMemory::Create(system.Kernel()); |
| 745 | time_shared_mem = KSharedMemory::Create(system.Kernel()); | 749 | time_shared_mem = KSharedMemory::Create(system.Kernel()); |
| 746 | hidbus_shared_mem = KSharedMemory::Create(system.Kernel()); | 750 | hidbus_shared_mem = KSharedMemory::Create(system.Kernel()); |
| 747 | 751 | ||
| 748 | hid_shared_mem->Initialize(system.DeviceMemory(), nullptr, | 752 | hid_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None, |
| 749 | {hid_phys_addr, hid_size / PageSize}, | 753 | Svc::MemoryPermission::Read, hid_size, "HID:SharedMemory"); |
| 750 | Svc::MemoryPermission::None, Svc::MemoryPermission::Read, | 754 | font_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None, |
| 751 | hid_phys_addr, hid_size, "HID:SharedMemory"); | 755 | Svc::MemoryPermission::Read, font_size, "Font:SharedMemory"); |
| 752 | font_shared_mem->Initialize(system.DeviceMemory(), nullptr, | 756 | irs_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None, |
| 753 | {font_phys_addr, font_size / PageSize}, | 757 | Svc::MemoryPermission::Read, irs_size, "IRS:SharedMemory"); |
| 754 | Svc::MemoryPermission::None, Svc::MemoryPermission::Read, | 758 | time_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None, |
| 755 | font_phys_addr, font_size, "Font:SharedMemory"); | 759 | Svc::MemoryPermission::Read, time_size, "Time:SharedMemory"); |
| 756 | irs_shared_mem->Initialize(system.DeviceMemory(), nullptr, | 760 | hidbus_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None, |
| 757 | {irs_phys_addr, irs_size / PageSize}, | 761 | Svc::MemoryPermission::Read, hidbus_size, |
| 758 | Svc::MemoryPermission::None, Svc::MemoryPermission::Read, | 762 | "HidBus:SharedMemory"); |
| 759 | irs_phys_addr, irs_size, "IRS:SharedMemory"); | ||
| 760 | time_shared_mem->Initialize(system.DeviceMemory(), nullptr, | ||
| 761 | {time_phys_addr, time_size / PageSize}, | ||
| 762 | Svc::MemoryPermission::None, Svc::MemoryPermission::Read, | ||
| 763 | time_phys_addr, time_size, "Time:SharedMemory"); | ||
| 764 | hidbus_shared_mem->Initialize(system.DeviceMemory(), nullptr, | ||
| 765 | {hidbus_phys_addr, hidbus_size / PageSize}, | ||
| 766 | Svc::MemoryPermission::None, Svc::MemoryPermission::Read, | ||
| 767 | hidbus_phys_addr, hidbus_size, "HidBus:SharedMemory"); | ||
| 768 | } | 763 | } |
| 769 | 764 | ||
| 770 | KClientPort* CreateNamedServicePort(std::string name) { | 765 | KClientPort* CreateNamedServicePort(std::string name) { |
| @@ -828,7 +823,7 @@ struct KernelCore::Impl { | |||
| 828 | std::vector<KProcess*> process_list; | 823 | std::vector<KProcess*> process_list; |
| 829 | std::atomic<KProcess*> current_process{}; | 824 | std::atomic<KProcess*> current_process{}; |
| 830 | std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context; | 825 | std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context; |
| 831 | Kernel::TimeManager time_manager; | 826 | std::unique_ptr<Kernel::KHardwareTimer> hardware_timer; |
| 832 | 827 | ||
| 833 | Init::KSlabResourceCounts slab_resource_counts{}; | 828 | Init::KSlabResourceCounts slab_resource_counts{}; |
| 834 | KResourceLimit* system_resource_limit{}; | 829 | KResourceLimit* system_resource_limit{}; |
| @@ -1015,12 +1010,8 @@ Kernel::KScheduler* KernelCore::CurrentScheduler() { | |||
| 1015 | return impl->schedulers[core_id].get(); | 1010 | return impl->schedulers[core_id].get(); |
| 1016 | } | 1011 | } |
| 1017 | 1012 | ||
| 1018 | Kernel::TimeManager& KernelCore::TimeManager() { | 1013 | Kernel::KHardwareTimer& KernelCore::HardwareTimer() { |
| 1019 | return impl->time_manager; | 1014 | return *impl->hardware_timer; |
| 1020 | } | ||
| 1021 | |||
| 1022 | const Kernel::TimeManager& KernelCore::TimeManager() const { | ||
| 1023 | return impl->time_manager; | ||
| 1024 | } | 1015 | } |
| 1025 | 1016 | ||
| 1026 | Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() { | 1017 | Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() { |
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 2e22fe0f6..8d22f8d2c 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -39,6 +39,7 @@ class KDynamicPageManager; | |||
| 39 | class KEvent; | 39 | class KEvent; |
| 40 | class KEventInfo; | 40 | class KEventInfo; |
| 41 | class KHandleTable; | 41 | class KHandleTable; |
| 42 | class KHardwareTimer; | ||
| 42 | class KLinkedListNode; | 43 | class KLinkedListNode; |
| 43 | class KMemoryLayout; | 44 | class KMemoryLayout; |
| 44 | class KMemoryManager; | 45 | class KMemoryManager; |
| @@ -63,7 +64,6 @@ class KCodeMemory; | |||
| 63 | class PhysicalCore; | 64 | class PhysicalCore; |
| 64 | class ServiceThread; | 65 | class ServiceThread; |
| 65 | class Synchronization; | 66 | class Synchronization; |
| 66 | class TimeManager; | ||
| 67 | 67 | ||
| 68 | using ServiceInterfaceFactory = | 68 | using ServiceInterfaceFactory = |
| 69 | std::function<KClientPort&(Service::SM::ServiceManager&, Core::System&)>; | 69 | std::function<KClientPort&(Service::SM::ServiceManager&, Core::System&)>; |
| @@ -175,11 +175,8 @@ public: | |||
| 175 | /// Gets the an instance of the current physical CPU core. | 175 | /// Gets the an instance of the current physical CPU core. |
| 176 | const Kernel::PhysicalCore& CurrentPhysicalCore() const; | 176 | const Kernel::PhysicalCore& CurrentPhysicalCore() const; |
| 177 | 177 | ||
| 178 | /// Gets the an instance of the TimeManager Interface. | 178 | /// Gets the an instance of the hardware timer. |
| 179 | Kernel::TimeManager& TimeManager(); | 179 | Kernel::KHardwareTimer& HardwareTimer(); |
| 180 | |||
| 181 | /// Gets the an instance of the TimeManager Interface. | ||
| 182 | const Kernel::TimeManager& TimeManager() const; | ||
| 183 | 180 | ||
| 184 | /// Stops execution of 'id' core, in order to reschedule a new thread. | 181 | /// Stops execution of 'id' core, in order to reschedule a new thread. |
| 185 | void PrepareReschedule(std::size_t id); | 182 | void PrepareReschedule(std::size_t id); |
diff --git a/src/core/hle/kernel/memory_types.h b/src/core/hle/kernel/memory_types.h index 3975507bd..92b8b37ac 100644 --- a/src/core/hle/kernel/memory_types.h +++ b/src/core/hle/kernel/memory_types.h | |||
| @@ -14,4 +14,7 @@ constexpr std::size_t PageSize{1 << PageBits}; | |||
| 14 | 14 | ||
| 15 | using Page = std::array<u8, PageSize>; | 15 | using Page = std::array<u8, PageSize>; |
| 16 | 16 | ||
| 17 | using KPhysicalAddress = PAddr; | ||
| 18 | using KProcessAddress = VAddr; | ||
| 19 | |||
| 17 | } // namespace Kernel | 20 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index e520cab47..aca442196 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -784,63 +784,29 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han | |||
| 784 | LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, | 784 | LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, |
| 785 | info_sub_id, handle); | 785 | info_sub_id, handle); |
| 786 | 786 | ||
| 787 | enum class GetInfoType : u64 { | 787 | const auto info_id_type = static_cast<InfoType>(info_id); |
| 788 | // 1.0.0+ | ||
| 789 | AllowedCPUCoreMask = 0, | ||
| 790 | AllowedThreadPriorityMask = 1, | ||
| 791 | MapRegionBaseAddr = 2, | ||
| 792 | MapRegionSize = 3, | ||
| 793 | HeapRegionBaseAddr = 4, | ||
| 794 | HeapRegionSize = 5, | ||
| 795 | TotalPhysicalMemoryAvailable = 6, | ||
| 796 | TotalPhysicalMemoryUsed = 7, | ||
| 797 | IsCurrentProcessBeingDebugged = 8, | ||
| 798 | RegisterResourceLimit = 9, | ||
| 799 | IdleTickCount = 10, | ||
| 800 | RandomEntropy = 11, | ||
| 801 | ThreadTickCount = 0xF0000002, | ||
| 802 | // 2.0.0+ | ||
| 803 | ASLRRegionBaseAddr = 12, | ||
| 804 | ASLRRegionSize = 13, | ||
| 805 | StackRegionBaseAddr = 14, | ||
| 806 | StackRegionSize = 15, | ||
| 807 | // 3.0.0+ | ||
| 808 | SystemResourceSize = 16, | ||
| 809 | SystemResourceUsage = 17, | ||
| 810 | TitleId = 18, | ||
| 811 | // 4.0.0+ | ||
| 812 | PrivilegedProcessId = 19, | ||
| 813 | // 5.0.0+ | ||
| 814 | UserExceptionContextAddr = 20, | ||
| 815 | // 6.0.0+ | ||
| 816 | TotalPhysicalMemoryAvailableWithoutSystemResource = 21, | ||
| 817 | TotalPhysicalMemoryUsedWithoutSystemResource = 22, | ||
| 818 | |||
| 819 | // Homebrew only | ||
| 820 | MesosphereCurrentProcess = 65001, | ||
| 821 | }; | ||
| 822 | |||
| 823 | const auto info_id_type = static_cast<GetInfoType>(info_id); | ||
| 824 | 788 | ||
| 825 | switch (info_id_type) { | 789 | switch (info_id_type) { |
| 826 | case GetInfoType::AllowedCPUCoreMask: | 790 | case InfoType::CoreMask: |
| 827 | case GetInfoType::AllowedThreadPriorityMask: | 791 | case InfoType::PriorityMask: |
| 828 | case GetInfoType::MapRegionBaseAddr: | 792 | case InfoType::AliasRegionAddress: |
| 829 | case GetInfoType::MapRegionSize: | 793 | case InfoType::AliasRegionSize: |
| 830 | case GetInfoType::HeapRegionBaseAddr: | 794 | case InfoType::HeapRegionAddress: |
| 831 | case GetInfoType::HeapRegionSize: | 795 | case InfoType::HeapRegionSize: |
| 832 | case GetInfoType::ASLRRegionBaseAddr: | 796 | case InfoType::AslrRegionAddress: |
| 833 | case GetInfoType::ASLRRegionSize: | 797 | case InfoType::AslrRegionSize: |
| 834 | case GetInfoType::StackRegionBaseAddr: | 798 | case InfoType::StackRegionAddress: |
| 835 | case GetInfoType::StackRegionSize: | 799 | case InfoType::StackRegionSize: |
| 836 | case GetInfoType::TotalPhysicalMemoryAvailable: | 800 | case InfoType::TotalMemorySize: |
| 837 | case GetInfoType::TotalPhysicalMemoryUsed: | 801 | case InfoType::UsedMemorySize: |
| 838 | case GetInfoType::SystemResourceSize: | 802 | case InfoType::SystemResourceSizeTotal: |
| 839 | case GetInfoType::SystemResourceUsage: | 803 | case InfoType::SystemResourceSizeUsed: |
| 840 | case GetInfoType::TitleId: | 804 | case InfoType::ProgramId: |
| 841 | case GetInfoType::UserExceptionContextAddr: | 805 | case InfoType::UserExceptionContextAddress: |
| 842 | case GetInfoType::TotalPhysicalMemoryAvailableWithoutSystemResource: | 806 | case InfoType::TotalNonSystemMemorySize: |
| 843 | case GetInfoType::TotalPhysicalMemoryUsedWithoutSystemResource: { | 807 | case InfoType::UsedNonSystemMemorySize: |
| 808 | case InfoType::IsApplication: | ||
| 809 | case InfoType::FreeThreadCount: { | ||
| 844 | if (info_sub_id != 0) { | 810 | if (info_sub_id != 0) { |
| 845 | LOG_ERROR(Kernel_SVC, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id, | 811 | LOG_ERROR(Kernel_SVC, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id, |
| 846 | info_sub_id); | 812 | info_sub_id); |
| @@ -856,79 +822,83 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han | |||
| 856 | } | 822 | } |
| 857 | 823 | ||
| 858 | switch (info_id_type) { | 824 | switch (info_id_type) { |
| 859 | case GetInfoType::AllowedCPUCoreMask: | 825 | case InfoType::CoreMask: |
| 860 | *result = process->GetCoreMask(); | 826 | *result = process->GetCoreMask(); |
| 861 | return ResultSuccess; | 827 | return ResultSuccess; |
| 862 | 828 | ||
| 863 | case GetInfoType::AllowedThreadPriorityMask: | 829 | case InfoType::PriorityMask: |
| 864 | *result = process->GetPriorityMask(); | 830 | *result = process->GetPriorityMask(); |
| 865 | return ResultSuccess; | 831 | return ResultSuccess; |
| 866 | 832 | ||
| 867 | case GetInfoType::MapRegionBaseAddr: | 833 | case InfoType::AliasRegionAddress: |
| 868 | *result = process->PageTable().GetAliasRegionStart(); | 834 | *result = process->PageTable().GetAliasRegionStart(); |
| 869 | return ResultSuccess; | 835 | return ResultSuccess; |
| 870 | 836 | ||
| 871 | case GetInfoType::MapRegionSize: | 837 | case InfoType::AliasRegionSize: |
| 872 | *result = process->PageTable().GetAliasRegionSize(); | 838 | *result = process->PageTable().GetAliasRegionSize(); |
| 873 | return ResultSuccess; | 839 | return ResultSuccess; |
| 874 | 840 | ||
| 875 | case GetInfoType::HeapRegionBaseAddr: | 841 | case InfoType::HeapRegionAddress: |
| 876 | *result = process->PageTable().GetHeapRegionStart(); | 842 | *result = process->PageTable().GetHeapRegionStart(); |
| 877 | return ResultSuccess; | 843 | return ResultSuccess; |
| 878 | 844 | ||
| 879 | case GetInfoType::HeapRegionSize: | 845 | case InfoType::HeapRegionSize: |
| 880 | *result = process->PageTable().GetHeapRegionSize(); | 846 | *result = process->PageTable().GetHeapRegionSize(); |
| 881 | return ResultSuccess; | 847 | return ResultSuccess; |
| 882 | 848 | ||
| 883 | case GetInfoType::ASLRRegionBaseAddr: | 849 | case InfoType::AslrRegionAddress: |
| 884 | *result = process->PageTable().GetAliasCodeRegionStart(); | 850 | *result = process->PageTable().GetAliasCodeRegionStart(); |
| 885 | return ResultSuccess; | 851 | return ResultSuccess; |
| 886 | 852 | ||
| 887 | case GetInfoType::ASLRRegionSize: | 853 | case InfoType::AslrRegionSize: |
| 888 | *result = process->PageTable().GetAliasCodeRegionSize(); | 854 | *result = process->PageTable().GetAliasCodeRegionSize(); |
| 889 | return ResultSuccess; | 855 | return ResultSuccess; |
| 890 | 856 | ||
| 891 | case GetInfoType::StackRegionBaseAddr: | 857 | case InfoType::StackRegionAddress: |
| 892 | *result = process->PageTable().GetStackRegionStart(); | 858 | *result = process->PageTable().GetStackRegionStart(); |
| 893 | return ResultSuccess; | 859 | return ResultSuccess; |
| 894 | 860 | ||
| 895 | case GetInfoType::StackRegionSize: | 861 | case InfoType::StackRegionSize: |
| 896 | *result = process->PageTable().GetStackRegionSize(); | 862 | *result = process->PageTable().GetStackRegionSize(); |
| 897 | return ResultSuccess; | 863 | return ResultSuccess; |
| 898 | 864 | ||
| 899 | case GetInfoType::TotalPhysicalMemoryAvailable: | 865 | case InfoType::TotalMemorySize: |
| 900 | *result = process->GetTotalPhysicalMemoryAvailable(); | 866 | *result = process->GetTotalPhysicalMemoryAvailable(); |
| 901 | return ResultSuccess; | 867 | return ResultSuccess; |
| 902 | 868 | ||
| 903 | case GetInfoType::TotalPhysicalMemoryUsed: | 869 | case InfoType::UsedMemorySize: |
| 904 | *result = process->GetTotalPhysicalMemoryUsed(); | 870 | *result = process->GetTotalPhysicalMemoryUsed(); |
| 905 | return ResultSuccess; | 871 | return ResultSuccess; |
| 906 | 872 | ||
| 907 | case GetInfoType::SystemResourceSize: | 873 | case InfoType::SystemResourceSizeTotal: |
| 908 | *result = process->GetSystemResourceSize(); | 874 | *result = process->GetSystemResourceSize(); |
| 909 | return ResultSuccess; | 875 | return ResultSuccess; |
| 910 | 876 | ||
| 911 | case GetInfoType::SystemResourceUsage: | 877 | case InfoType::SystemResourceSizeUsed: |
| 912 | LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query system resource usage"); | 878 | LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query system resource usage"); |
| 913 | *result = process->GetSystemResourceUsage(); | 879 | *result = process->GetSystemResourceUsage(); |
| 914 | return ResultSuccess; | 880 | return ResultSuccess; |
| 915 | 881 | ||
| 916 | case GetInfoType::TitleId: | 882 | case InfoType::ProgramId: |
| 917 | *result = process->GetProgramID(); | 883 | *result = process->GetProgramID(); |
| 918 | return ResultSuccess; | 884 | return ResultSuccess; |
| 919 | 885 | ||
| 920 | case GetInfoType::UserExceptionContextAddr: | 886 | case InfoType::UserExceptionContextAddress: |
| 921 | *result = process->GetProcessLocalRegionAddress(); | 887 | *result = process->GetProcessLocalRegionAddress(); |
| 922 | return ResultSuccess; | 888 | return ResultSuccess; |
| 923 | 889 | ||
| 924 | case GetInfoType::TotalPhysicalMemoryAvailableWithoutSystemResource: | 890 | case InfoType::TotalNonSystemMemorySize: |
| 925 | *result = process->GetTotalPhysicalMemoryAvailableWithoutSystemResource(); | 891 | *result = process->GetTotalPhysicalMemoryAvailableWithoutSystemResource(); |
| 926 | return ResultSuccess; | 892 | return ResultSuccess; |
| 927 | 893 | ||
| 928 | case GetInfoType::TotalPhysicalMemoryUsedWithoutSystemResource: | 894 | case InfoType::UsedNonSystemMemorySize: |
| 929 | *result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource(); | 895 | *result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource(); |
| 930 | return ResultSuccess; | 896 | return ResultSuccess; |
| 931 | 897 | ||
| 898 | case InfoType::FreeThreadCount: | ||
| 899 | *result = process->GetFreeThreadCount(); | ||
| 900 | return ResultSuccess; | ||
| 901 | |||
| 932 | default: | 902 | default: |
| 933 | break; | 903 | break; |
| 934 | } | 904 | } |
| @@ -937,11 +907,11 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han | |||
| 937 | return ResultInvalidEnumValue; | 907 | return ResultInvalidEnumValue; |
| 938 | } | 908 | } |
| 939 | 909 | ||
| 940 | case GetInfoType::IsCurrentProcessBeingDebugged: | 910 | case InfoType::DebuggerAttached: |
| 941 | *result = 0; | 911 | *result = 0; |
| 942 | return ResultSuccess; | 912 | return ResultSuccess; |
| 943 | 913 | ||
| 944 | case GetInfoType::RegisterResourceLimit: { | 914 | case InfoType::ResourceLimit: { |
| 945 | if (handle != 0) { | 915 | if (handle != 0) { |
| 946 | LOG_ERROR(Kernel, "Handle is non zero! handle={:08X}", handle); | 916 | LOG_ERROR(Kernel, "Handle is non zero! handle={:08X}", handle); |
| 947 | return ResultInvalidHandle; | 917 | return ResultInvalidHandle; |
| @@ -969,7 +939,7 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han | |||
| 969 | return ResultSuccess; | 939 | return ResultSuccess; |
| 970 | } | 940 | } |
| 971 | 941 | ||
| 972 | case GetInfoType::RandomEntropy: | 942 | case InfoType::RandomEntropy: |
| 973 | if (handle != 0) { | 943 | if (handle != 0) { |
| 974 | LOG_ERROR(Kernel_SVC, "Process Handle is non zero, expected 0 result but got {:016X}", | 944 | LOG_ERROR(Kernel_SVC, "Process Handle is non zero, expected 0 result but got {:016X}", |
| 975 | handle); | 945 | handle); |
| @@ -985,13 +955,13 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han | |||
| 985 | *result = system.Kernel().CurrentProcess()->GetRandomEntropy(info_sub_id); | 955 | *result = system.Kernel().CurrentProcess()->GetRandomEntropy(info_sub_id); |
| 986 | return ResultSuccess; | 956 | return ResultSuccess; |
| 987 | 957 | ||
| 988 | case GetInfoType::PrivilegedProcessId: | 958 | case InfoType::InitialProcessIdRange: |
| 989 | LOG_WARNING(Kernel_SVC, | 959 | LOG_WARNING(Kernel_SVC, |
| 990 | "(STUBBED) Attempted to query privileged process id bounds, returned 0"); | 960 | "(STUBBED) Attempted to query privileged process id bounds, returned 0"); |
| 991 | *result = 0; | 961 | *result = 0; |
| 992 | return ResultSuccess; | 962 | return ResultSuccess; |
| 993 | 963 | ||
| 994 | case GetInfoType::ThreadTickCount: { | 964 | case InfoType::ThreadTickCount: { |
| 995 | constexpr u64 num_cpus = 4; | 965 | constexpr u64 num_cpus = 4; |
| 996 | if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) { | 966 | if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) { |
| 997 | LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus, | 967 | LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus, |
| @@ -1026,7 +996,7 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han | |||
| 1026 | *result = out_ticks; | 996 | *result = out_ticks; |
| 1027 | return ResultSuccess; | 997 | return ResultSuccess; |
| 1028 | } | 998 | } |
| 1029 | case GetInfoType::IdleTickCount: { | 999 | case InfoType::IdleTickCount: { |
| 1030 | // Verify the input handle is invalid. | 1000 | // Verify the input handle is invalid. |
| 1031 | R_UNLESS(handle == InvalidHandle, ResultInvalidHandle); | 1001 | R_UNLESS(handle == InvalidHandle, ResultInvalidHandle); |
| 1032 | 1002 | ||
| @@ -1040,7 +1010,7 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han | |||
| 1040 | *result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime(); | 1010 | *result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime(); |
| 1041 | return ResultSuccess; | 1011 | return ResultSuccess; |
| 1042 | } | 1012 | } |
| 1043 | case GetInfoType::MesosphereCurrentProcess: { | 1013 | case InfoType::MesosphereCurrentProcess: { |
| 1044 | // Verify the input handle is invalid. | 1014 | // Verify the input handle is invalid. |
| 1045 | R_UNLESS(handle == InvalidHandle, ResultInvalidHandle); | 1015 | R_UNLESS(handle == InvalidHandle, ResultInvalidHandle); |
| 1046 | 1016 | ||
| @@ -1515,7 +1485,7 @@ static Result MapProcessMemory(Core::System& system, VAddr dst_address, Handle p | |||
| 1515 | ResultInvalidMemoryRegion); | 1485 | ResultInvalidMemoryRegion); |
| 1516 | 1486 | ||
| 1517 | // Create a new page group. | 1487 | // Create a new page group. |
| 1518 | KPageGroup pg; | 1488 | KPageGroup pg{system.Kernel(), dst_pt.GetBlockInfoManager()}; |
| 1519 | R_TRY(src_pt.MakeAndOpenPageGroup( | 1489 | R_TRY(src_pt.MakeAndOpenPageGroup( |
| 1520 | std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess, | 1490 | std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess, |
| 1521 | KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None, | 1491 | KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None, |
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp deleted file mode 100644 index 5ee72c432..000000000 --- a/src/core/hle/kernel/time_manager.cpp +++ /dev/null | |||
| @@ -1,44 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/assert.h" | ||
| 5 | #include "core/core.h" | ||
| 6 | #include "core/core_timing.h" | ||
| 7 | #include "core/hle/kernel/k_scheduler.h" | ||
| 8 | #include "core/hle/kernel/k_thread.h" | ||
| 9 | #include "core/hle/kernel/time_manager.h" | ||
| 10 | |||
| 11 | namespace Kernel { | ||
| 12 | |||
| 13 | TimeManager::TimeManager(Core::System& system_) : system{system_} { | ||
| 14 | time_manager_event_type = Core::Timing::CreateEvent( | ||
| 15 | "Kernel::TimeManagerCallback", | ||
| 16 | [this](std::uintptr_t thread_handle, s64 time, | ||
| 17 | std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> { | ||
| 18 | KThread* thread = reinterpret_cast<KThread*>(thread_handle); | ||
| 19 | { | ||
| 20 | KScopedSchedulerLock sl(system.Kernel()); | ||
| 21 | thread->OnTimer(); | ||
| 22 | } | ||
| 23 | return std::nullopt; | ||
| 24 | }); | ||
| 25 | } | ||
| 26 | |||
| 27 | void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) { | ||
| 28 | std::scoped_lock lock{mutex}; | ||
| 29 | if (nanoseconds > 0) { | ||
| 30 | ASSERT(thread); | ||
| 31 | ASSERT(thread->GetState() != ThreadState::Runnable); | ||
| 32 | system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{nanoseconds}, | ||
| 33 | time_manager_event_type, | ||
| 34 | reinterpret_cast<uintptr_t>(thread)); | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | void TimeManager::UnscheduleTimeEvent(KThread* thread) { | ||
| 39 | std::scoped_lock lock{mutex}; | ||
| 40 | system.CoreTiming().UnscheduleEvent(time_manager_event_type, | ||
| 41 | reinterpret_cast<uintptr_t>(thread)); | ||
| 42 | } | ||
| 43 | |||
| 44 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/time_manager.h b/src/core/hle/kernel/time_manager.h deleted file mode 100644 index 94d16b3b4..000000000 --- a/src/core/hle/kernel/time_manager.h +++ /dev/null | |||
| @@ -1,41 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <memory> | ||
| 7 | #include <mutex> | ||
| 8 | |||
| 9 | namespace Core { | ||
| 10 | class System; | ||
| 11 | } // namespace Core | ||
| 12 | |||
| 13 | namespace Core::Timing { | ||
| 14 | struct EventType; | ||
| 15 | } // namespace Core::Timing | ||
| 16 | |||
| 17 | namespace Kernel { | ||
| 18 | |||
| 19 | class KThread; | ||
| 20 | |||
| 21 | /** | ||
| 22 | * The `TimeManager` takes care of scheduling time events on threads and executes their TimeUp | ||
| 23 | * method when the event is triggered. | ||
| 24 | */ | ||
| 25 | class TimeManager { | ||
| 26 | public: | ||
| 27 | explicit TimeManager(Core::System& system); | ||
| 28 | |||
| 29 | /// Schedule a time event on `timetask` thread that will expire in 'nanoseconds' | ||
| 30 | void ScheduleTimeEvent(KThread* time_task, s64 nanoseconds); | ||
| 31 | |||
| 32 | /// Unschedule an existing time event | ||
| 33 | void UnscheduleTimeEvent(KThread* thread); | ||
| 34 | |||
| 35 | private: | ||
| 36 | Core::System& system; | ||
| 37 | std::shared_ptr<Core::Timing::EventType> time_manager_event_type; | ||
| 38 | std::mutex mutex; | ||
| 39 | }; | ||
| 40 | |||
| 41 | } // namespace Kernel | ||
diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp index 26dec7147..053e8f9dd 100644 --- a/src/core/hle/service/audio/audin_u.cpp +++ b/src/core/hle/service/audio/audin_u.cpp | |||
| @@ -203,8 +203,9 @@ private: | |||
| 203 | }; | 203 | }; |
| 204 | 204 | ||
| 205 | AudInU::AudInU(Core::System& system_) | 205 | AudInU::AudInU(Core::System& system_) |
| 206 | : ServiceFramework{system_, "audin:u"}, service_context{system_, "AudInU"}, | 206 | : ServiceFramework{system_, "audin:u", ServiceThreadType::CreateNew}, |
| 207 | impl{std::make_unique<AudioCore::AudioIn::Manager>(system_)} { | 207 | service_context{system_, "AudInU"}, impl{std::make_unique<AudioCore::AudioIn::Manager>( |
| 208 | system_)} { | ||
| 208 | // clang-format off | 209 | // clang-format off |
| 209 | static const FunctionInfo functions[] = { | 210 | static const FunctionInfo functions[] = { |
| 210 | {0, &AudInU::ListAudioIns, "ListAudioIns"}, | 211 | {0, &AudInU::ListAudioIns, "ListAudioIns"}, |
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 991e30ba1..29751f075 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp | |||
| @@ -26,8 +26,9 @@ public: | |||
| 26 | explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager, | 26 | explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager, |
| 27 | size_t session_id, const std::string& device_name, | 27 | size_t session_id, const std::string& device_name, |
| 28 | const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id) | 28 | const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id) |
| 29 | : ServiceFramework{system_, "IAudioOut"}, service_context{system_, "IAudioOut"}, | 29 | : ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew}, |
| 30 | event{service_context.CreateEvent("AudioOutEvent")}, | 30 | service_context{system_, "IAudioOut"}, event{service_context.CreateEvent( |
| 31 | "AudioOutEvent")}, | ||
| 31 | impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} { | 32 | impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} { |
| 32 | 33 | ||
| 33 | // clang-format off | 34 | // clang-format off |
| @@ -220,8 +221,9 @@ private: | |||
| 220 | }; | 221 | }; |
| 221 | 222 | ||
| 222 | AudOutU::AudOutU(Core::System& system_) | 223 | AudOutU::AudOutU(Core::System& system_) |
| 223 | : ServiceFramework{system_, "audout:u"}, service_context{system_, "AudOutU"}, | 224 | : ServiceFramework{system_, "audout:u", ServiceThreadType::CreateNew}, |
| 224 | impl{std::make_unique<AudioCore::AudioOut::Manager>(system_)} { | 225 | service_context{system_, "AudOutU"}, impl{std::make_unique<AudioCore::AudioOut::Manager>( |
| 226 | system_)} { | ||
| 225 | // clang-format off | 227 | // clang-format off |
| 226 | static const FunctionInfo functions[] = { | 228 | static const FunctionInfo functions[] = { |
| 227 | {0, &AudOutU::ListAudioOuts, "ListAudioOuts"}, | 229 | {0, &AudOutU::ListAudioOuts, "ListAudioOuts"}, |
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index ead16c321..3a1c231b6 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp | |||
| @@ -35,9 +35,10 @@ public: | |||
| 35 | AudioCore::AudioRendererParameterInternal& params, | 35 | AudioCore::AudioRendererParameterInternal& params, |
| 36 | Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size, | 36 | Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size, |
| 37 | u32 process_handle, u64 applet_resource_user_id, s32 session_id) | 37 | u32 process_handle, u64 applet_resource_user_id, s32 session_id) |
| 38 | : ServiceFramework{system_, "IAudioRenderer"}, service_context{system_, "IAudioRenderer"}, | 38 | : ServiceFramework{system_, "IAudioRenderer", ServiceThreadType::CreateNew}, |
| 39 | rendered_event{service_context.CreateEvent("IAudioRendererEvent")}, manager{manager_}, | 39 | service_context{system_, "IAudioRenderer"}, rendered_event{service_context.CreateEvent( |
| 40 | impl{std::make_unique<Renderer>(system_, manager, rendered_event)} { | 40 | "IAudioRendererEvent")}, |
| 41 | manager{manager_}, impl{std::make_unique<Renderer>(system_, manager, rendered_event)} { | ||
| 41 | // clang-format off | 42 | // clang-format off |
| 42 | static const FunctionInfo functions[] = { | 43 | static const FunctionInfo functions[] = { |
| 43 | {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"}, | 44 | {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"}, |
| @@ -242,8 +243,10 @@ class IAudioDevice final : public ServiceFramework<IAudioDevice> { | |||
| 242 | public: | 243 | public: |
| 243 | explicit IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u32 revision, | 244 | explicit IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u32 revision, |
| 244 | u32 device_num) | 245 | u32 device_num) |
| 245 | : ServiceFramework{system_, "IAudioDevice"}, service_context{system_, "IAudioDevice"}, | 246 | : ServiceFramework{system_, "IAudioDevice", ServiceThreadType::CreateNew}, |
| 246 | impl{std::make_unique<AudioDevice>(system_, applet_resource_user_id, revision)}, | 247 | service_context{system_, "IAudioDevice"}, impl{std::make_unique<AudioDevice>( |
| 248 | system_, applet_resource_user_id, | ||
| 249 | revision)}, | ||
| 247 | event{service_context.CreateEvent(fmt::format("IAudioDeviceEvent-{}", device_num))} { | 250 | event{service_context.CreateEvent(fmt::format("IAudioDeviceEvent-{}", device_num))} { |
| 248 | static const FunctionInfo functions[] = { | 251 | static const FunctionInfo functions[] = { |
| 249 | {0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"}, | 252 | {0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"}, |
| @@ -418,7 +421,7 @@ private: | |||
| 418 | }; | 421 | }; |
| 419 | 422 | ||
| 420 | AudRenU::AudRenU(Core::System& system_) | 423 | AudRenU::AudRenU(Core::System& system_) |
| 421 | : ServiceFramework{system_, "audren:u"}, | 424 | : ServiceFramework{system_, "audren:u", ServiceThreadType::CreateNew}, |
| 422 | service_context{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} { | 425 | service_context{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} { |
| 423 | // clang-format off | 426 | // clang-format off |
| 424 | static const FunctionInfo functions[] = { | 427 | static const FunctionInfo functions[] = { |
diff --git a/src/core/hle/service/nfc/nfc_user.cpp b/src/core/hle/service/nfc/nfc_user.cpp index 4615697e2..89aa6b3f5 100644 --- a/src/core/hle/service/nfc/nfc_user.cpp +++ b/src/core/hle/service/nfc/nfc_user.cpp | |||
| @@ -97,7 +97,7 @@ void IUser::IsNfcEnabled(Kernel::HLERequestContext& ctx) { | |||
| 97 | } | 97 | } |
| 98 | 98 | ||
| 99 | void IUser::ListDevices(Kernel::HLERequestContext& ctx) { | 99 | void IUser::ListDevices(Kernel::HLERequestContext& ctx) { |
| 100 | LOG_INFO(Service_NFC, "called"); | 100 | LOG_DEBUG(Service_NFC, "called"); |
| 101 | 101 | ||
| 102 | if (state == State::NonInitialized) { | 102 | if (state == State::NonInitialized) { |
| 103 | IPC::ResponseBuilder rb{ctx, 2}; | 103 | IPC::ResponseBuilder rb{ctx, 2}; |
diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_user.cpp index 49816b4c7..a4d3d1bc7 100644 --- a/src/core/hle/service/nfp/nfp_user.cpp +++ b/src/core/hle/service/nfp/nfp_user.cpp | |||
| @@ -83,7 +83,7 @@ void IUser::Finalize(Kernel::HLERequestContext& ctx) { | |||
| 83 | } | 83 | } |
| 84 | 84 | ||
| 85 | void IUser::ListDevices(Kernel::HLERequestContext& ctx) { | 85 | void IUser::ListDevices(Kernel::HLERequestContext& ctx) { |
| 86 | LOG_INFO(Service_NFP, "called"); | 86 | LOG_DEBUG(Service_NFP, "called"); |
| 87 | 87 | ||
| 88 | if (state == State::NonInitialized) { | 88 | if (state == State::NonInitialized) { |
| 89 | IPC::ResponseBuilder rb{ctx, 2}; | 89 | IPC::ResponseBuilder rb{ctx, 2}; |
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp index 4f1a8d6b7..16c5eaf75 100644 --- a/src/core/hle/service/set/set.cpp +++ b/src/core/hle/service/set/set.cpp | |||
| @@ -191,6 +191,13 @@ void SET::GetKeyCodeMap2(Kernel::HLERequestContext& ctx) { | |||
| 191 | GetKeyCodeMapImpl(ctx); | 191 | GetKeyCodeMapImpl(ctx); |
| 192 | } | 192 | } |
| 193 | 193 | ||
| 194 | void SET::GetDeviceNickName(Kernel::HLERequestContext& ctx) { | ||
| 195 | LOG_DEBUG(Service_SET, "called"); | ||
| 196 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 197 | rb.Push(ResultSuccess); | ||
| 198 | ctx.WriteBuffer(Settings::values.device_name.GetValue()); | ||
| 199 | } | ||
| 200 | |||
| 194 | SET::SET(Core::System& system_) : ServiceFramework{system_, "set"} { | 201 | SET::SET(Core::System& system_) : ServiceFramework{system_, "set"} { |
| 195 | // clang-format off | 202 | // clang-format off |
| 196 | static const FunctionInfo functions[] = { | 203 | static const FunctionInfo functions[] = { |
| @@ -205,7 +212,7 @@ SET::SET(Core::System& system_) : ServiceFramework{system_, "set"} { | |||
| 205 | {8, &SET::GetQuestFlag, "GetQuestFlag"}, | 212 | {8, &SET::GetQuestFlag, "GetQuestFlag"}, |
| 206 | {9, &SET::GetKeyCodeMap2, "GetKeyCodeMap2"}, | 213 | {9, &SET::GetKeyCodeMap2, "GetKeyCodeMap2"}, |
| 207 | {10, nullptr, "GetFirmwareVersionForDebug"}, | 214 | {10, nullptr, "GetFirmwareVersionForDebug"}, |
| 208 | {11, nullptr, "GetDeviceNickName"}, | 215 | {11, &SET::GetDeviceNickName, "GetDeviceNickName"}, |
| 209 | }; | 216 | }; |
| 210 | // clang-format on | 217 | // clang-format on |
| 211 | 218 | ||
diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h index 60cad3e6f..375975711 100644 --- a/src/core/hle/service/set/set.h +++ b/src/core/hle/service/set/set.h | |||
| @@ -50,6 +50,7 @@ private: | |||
| 50 | void GetRegionCode(Kernel::HLERequestContext& ctx); | 50 | void GetRegionCode(Kernel::HLERequestContext& ctx); |
| 51 | void GetKeyCodeMap(Kernel::HLERequestContext& ctx); | 51 | void GetKeyCodeMap(Kernel::HLERequestContext& ctx); |
| 52 | void GetKeyCodeMap2(Kernel::HLERequestContext& ctx); | 52 | void GetKeyCodeMap2(Kernel::HLERequestContext& ctx); |
| 53 | void GetDeviceNickName(Kernel::HLERequestContext& ctx); | ||
| 53 | }; | 54 | }; |
| 54 | 55 | ||
| 55 | } // namespace Service::Set | 56 | } // namespace Service::Set |
diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp index d7cea6aac..94c20edda 100644 --- a/src/core/hle/service/set/set_sys.cpp +++ b/src/core/hle/service/set/set_sys.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include "common/assert.h" | 4 | #include "common/assert.h" |
| 5 | #include "common/logging/log.h" | 5 | #include "common/logging/log.h" |
| 6 | #include "common/settings.h" | ||
| 6 | #include "core/file_sys/errors.h" | 7 | #include "core/file_sys/errors.h" |
| 7 | #include "core/file_sys/system_archive/system_version.h" | 8 | #include "core/file_sys/system_archive/system_version.h" |
| 8 | #include "core/hle/ipc_helpers.h" | 9 | #include "core/hle/ipc_helpers.h" |
| @@ -176,6 +177,13 @@ void SET_SYS::GetSettingsItemValue(Kernel::HLERequestContext& ctx) { | |||
| 176 | rb.Push(response); | 177 | rb.Push(response); |
| 177 | } | 178 | } |
| 178 | 179 | ||
| 180 | void SET_SYS::GetDeviceNickName(Kernel::HLERequestContext& ctx) { | ||
| 181 | LOG_DEBUG(Service_SET, "called"); | ||
| 182 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 183 | rb.Push(ResultSuccess); | ||
| 184 | ctx.WriteBuffer(::Settings::values.device_name.GetValue()); | ||
| 185 | } | ||
| 186 | |||
| 179 | SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { | 187 | SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { |
| 180 | // clang-format off | 188 | // clang-format off |
| 181 | static const FunctionInfo functions[] = { | 189 | static const FunctionInfo functions[] = { |
| @@ -253,7 +261,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { | |||
| 253 | {74, nullptr, "SetWirelessLanEnableFlag"}, | 261 | {74, nullptr, "SetWirelessLanEnableFlag"}, |
| 254 | {75, nullptr, "GetInitialLaunchSettings"}, | 262 | {75, nullptr, "GetInitialLaunchSettings"}, |
| 255 | {76, nullptr, "SetInitialLaunchSettings"}, | 263 | {76, nullptr, "SetInitialLaunchSettings"}, |
| 256 | {77, nullptr, "GetDeviceNickName"}, | 264 | {77, &SET_SYS::GetDeviceNickName, "GetDeviceNickName"}, |
| 257 | {78, nullptr, "SetDeviceNickName"}, | 265 | {78, nullptr, "SetDeviceNickName"}, |
| 258 | {79, nullptr, "GetProductModel"}, | 266 | {79, nullptr, "GetProductModel"}, |
| 259 | {80, nullptr, "GetLdnChannel"}, | 267 | {80, nullptr, "GetLdnChannel"}, |
diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h index 258ef8c57..464ac3da1 100644 --- a/src/core/hle/service/set/set_sys.h +++ b/src/core/hle/service/set/set_sys.h | |||
| @@ -29,6 +29,7 @@ private: | |||
| 29 | void GetFirmwareVersion2(Kernel::HLERequestContext& ctx); | 29 | void GetFirmwareVersion2(Kernel::HLERequestContext& ctx); |
| 30 | void GetColorSetId(Kernel::HLERequestContext& ctx); | 30 | void GetColorSetId(Kernel::HLERequestContext& ctx); |
| 31 | void SetColorSetId(Kernel::HLERequestContext& ctx); | 31 | void SetColorSetId(Kernel::HLERequestContext& ctx); |
| 32 | void GetDeviceNickName(Kernel::HLERequestContext& ctx); | ||
| 32 | 33 | ||
| 33 | ColorSet color_set = ColorSet::BasicWhite; | 34 | ColorSet color_set = ColorSet::BasicWhite; |
| 34 | }; | 35 | }; |
diff --git a/src/core/hle/service/time/clock_types.h b/src/core/hle/service/time/clock_types.h index ef070f32f..ed1eb5b2d 100644 --- a/src/core/hle/service/time/clock_types.h +++ b/src/core/hle/service/time/clock_types.h | |||
| @@ -49,6 +49,7 @@ struct SteadyClockContext { | |||
| 49 | static_assert(sizeof(SteadyClockContext) == 0x18, "SteadyClockContext is incorrect size"); | 49 | static_assert(sizeof(SteadyClockContext) == 0x18, "SteadyClockContext is incorrect size"); |
| 50 | static_assert(std::is_trivially_copyable_v<SteadyClockContext>, | 50 | static_assert(std::is_trivially_copyable_v<SteadyClockContext>, |
| 51 | "SteadyClockContext must be trivially copyable"); | 51 | "SteadyClockContext must be trivially copyable"); |
| 52 | using StandardSteadyClockTimePointType = SteadyClockContext; | ||
| 52 | 53 | ||
| 53 | struct SystemClockContext { | 54 | struct SystemClockContext { |
| 54 | s64 offset; | 55 | s64 offset; |
diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp index a3aa0e77f..ff53a7d6f 100644 --- a/src/core/hle/service/time/time_sharedmemory.cpp +++ b/src/core/hle/service/time/time_sharedmemory.cpp | |||
| @@ -26,23 +26,24 @@ void SharedMemory::SetupStandardSteadyClock(const Common::UUID& clock_source_id, | |||
| 26 | const Clock::SteadyClockContext context{ | 26 | const Clock::SteadyClockContext context{ |
| 27 | static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds), | 27 | static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds), |
| 28 | clock_source_id}; | 28 | clock_source_id}; |
| 29 | shared_memory_format.standard_steady_clock_timepoint.StoreData( | 29 | StoreToLockFreeAtomicType(&GetFormat()->standard_steady_clock_timepoint, context); |
| 30 | system.Kernel().GetTimeSharedMem().GetPointer(), context); | ||
| 31 | } | 30 | } |
| 32 | 31 | ||
| 33 | void SharedMemory::UpdateLocalSystemClockContext(const Clock::SystemClockContext& context) { | 32 | void SharedMemory::UpdateLocalSystemClockContext(const Clock::SystemClockContext& context) { |
| 34 | shared_memory_format.standard_local_system_clock_context.StoreData( | 33 | StoreToLockFreeAtomicType(&GetFormat()->standard_local_system_clock_context, context); |
| 35 | system.Kernel().GetTimeSharedMem().GetPointer(), context); | ||
| 36 | } | 34 | } |
| 37 | 35 | ||
| 38 | void SharedMemory::UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context) { | 36 | void SharedMemory::UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context) { |
| 39 | shared_memory_format.standard_network_system_clock_context.StoreData( | 37 | StoreToLockFreeAtomicType(&GetFormat()->standard_network_system_clock_context, context); |
| 40 | system.Kernel().GetTimeSharedMem().GetPointer(), context); | ||
| 41 | } | 38 | } |
| 42 | 39 | ||
| 43 | void SharedMemory::SetAutomaticCorrectionEnabled(bool is_enabled) { | 40 | void SharedMemory::SetAutomaticCorrectionEnabled(bool is_enabled) { |
| 44 | shared_memory_format.standard_user_system_clock_automatic_correction.StoreData( | 41 | StoreToLockFreeAtomicType( |
| 45 | system.Kernel().GetTimeSharedMem().GetPointer(), is_enabled); | 42 | &GetFormat()->is_standard_user_system_clock_automatic_correction_enabled, is_enabled); |
| 43 | } | ||
| 44 | |||
| 45 | SharedMemory::Format* SharedMemory::GetFormat() { | ||
| 46 | return reinterpret_cast<SharedMemory::Format*>(system.Kernel().GetTimeSharedMem().GetPointer()); | ||
| 46 | } | 47 | } |
| 47 | 48 | ||
| 48 | } // namespace Service::Time | 49 | } // namespace Service::Time |
diff --git a/src/core/hle/service/time/time_sharedmemory.h b/src/core/hle/service/time/time_sharedmemory.h index 561685acd..044a4d24e 100644 --- a/src/core/hle/service/time/time_sharedmemory.h +++ b/src/core/hle/service/time/time_sharedmemory.h | |||
| @@ -10,45 +10,68 @@ | |||
| 10 | 10 | ||
| 11 | namespace Service::Time { | 11 | namespace Service::Time { |
| 12 | 12 | ||
| 13 | // Note: this type is not safe for concurrent writes. | ||
| 14 | template <typename T> | ||
| 15 | struct LockFreeAtomicType { | ||
| 16 | u32 counter_; | ||
| 17 | std::array<T, 2> value_; | ||
| 18 | }; | ||
| 19 | |||
| 20 | template <typename T> | ||
| 21 | static inline void StoreToLockFreeAtomicType(LockFreeAtomicType<T>* p, const T& value) { | ||
| 22 | // Get the current counter. | ||
| 23 | auto counter = p->counter_; | ||
| 24 | |||
| 25 | // Increment the counter. | ||
| 26 | ++counter; | ||
| 27 | |||
| 28 | // Store the updated value. | ||
| 29 | p->value_[counter % 2] = value; | ||
| 30 | |||
| 31 | // Fence memory. | ||
| 32 | std::atomic_thread_fence(std::memory_order_release); | ||
| 33 | |||
| 34 | // Set the updated counter. | ||
| 35 | p->counter_ = counter; | ||
| 36 | } | ||
| 37 | |||
| 38 | template <typename T> | ||
| 39 | static inline T LoadFromLockFreeAtomicType(const LockFreeAtomicType<T>* p) { | ||
| 40 | while (true) { | ||
| 41 | // Get the counter. | ||
| 42 | auto counter = p->counter_; | ||
| 43 | |||
| 44 | // Get the value. | ||
| 45 | auto value = p->value_[counter % 2]; | ||
| 46 | |||
| 47 | // Fence memory. | ||
| 48 | std::atomic_thread_fence(std::memory_order_acquire); | ||
| 49 | |||
| 50 | // Check that the counter matches. | ||
| 51 | if (counter == p->counter_) { | ||
| 52 | return value; | ||
| 53 | } | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 13 | class SharedMemory final { | 57 | class SharedMemory final { |
| 14 | public: | 58 | public: |
| 15 | explicit SharedMemory(Core::System& system_); | 59 | explicit SharedMemory(Core::System& system_); |
| 16 | ~SharedMemory(); | 60 | ~SharedMemory(); |
| 17 | 61 | ||
| 18 | // TODO(ogniK): We have to properly simulate memory barriers, how are we going to do this? | ||
| 19 | template <typename T, std::size_t Offset> | ||
| 20 | struct MemoryBarrier { | ||
| 21 | static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable"); | ||
| 22 | u32_le read_attempt{}; | ||
| 23 | std::array<T, 2> data{}; | ||
| 24 | |||
| 25 | // These are not actually memory barriers at the moment as we don't have multicore and all | ||
| 26 | // HLE is mutexed. This will need to properly be implemented when we start updating the time | ||
| 27 | // points on threads. As of right now, we'll be updated both values synchronously and just | ||
| 28 | // incrementing the read_attempt to indicate that we waited. | ||
| 29 | void StoreData(u8* shared_memory, T data_to_store) { | ||
| 30 | std::memcpy(this, shared_memory + Offset, sizeof(*this)); | ||
| 31 | read_attempt++; | ||
| 32 | data[read_attempt & 1] = data_to_store; | ||
| 33 | std::memcpy(shared_memory + Offset, this, sizeof(*this)); | ||
| 34 | } | ||
| 35 | |||
| 36 | // For reading we're just going to read the last stored value. If there was no value stored | ||
| 37 | // it will just end up reading an empty value as intended. | ||
| 38 | T ReadData(u8* shared_memory) { | ||
| 39 | std::memcpy(this, shared_memory + Offset, sizeof(*this)); | ||
| 40 | return data[(read_attempt - 1) & 1]; | ||
| 41 | } | ||
| 42 | }; | ||
| 43 | |||
| 44 | // Shared memory format | 62 | // Shared memory format |
| 45 | struct Format { | 63 | struct Format { |
| 46 | MemoryBarrier<Clock::SteadyClockContext, 0x0> standard_steady_clock_timepoint; | 64 | LockFreeAtomicType<Clock::StandardSteadyClockTimePointType> standard_steady_clock_timepoint; |
| 47 | MemoryBarrier<Clock::SystemClockContext, 0x38> standard_local_system_clock_context; | 65 | LockFreeAtomicType<Clock::SystemClockContext> standard_local_system_clock_context; |
| 48 | MemoryBarrier<Clock::SystemClockContext, 0x80> standard_network_system_clock_context; | 66 | LockFreeAtomicType<Clock::SystemClockContext> standard_network_system_clock_context; |
| 49 | MemoryBarrier<bool, 0xc8> standard_user_system_clock_automatic_correction; | 67 | LockFreeAtomicType<bool> is_standard_user_system_clock_automatic_correction_enabled; |
| 50 | u32_le format_version; | 68 | u32 format_version; |
| 51 | }; | 69 | }; |
| 70 | static_assert(offsetof(Format, standard_steady_clock_timepoint) == 0x0); | ||
| 71 | static_assert(offsetof(Format, standard_local_system_clock_context) == 0x38); | ||
| 72 | static_assert(offsetof(Format, standard_network_system_clock_context) == 0x80); | ||
| 73 | static_assert(offsetof(Format, is_standard_user_system_clock_automatic_correction_enabled) == | ||
| 74 | 0xc8); | ||
| 52 | static_assert(sizeof(Format) == 0xd8, "Format is an invalid size"); | 75 | static_assert(sizeof(Format) == 0xd8, "Format is an invalid size"); |
| 53 | 76 | ||
| 54 | void SetupStandardSteadyClock(const Common::UUID& clock_source_id, | 77 | void SetupStandardSteadyClock(const Common::UUID& clock_source_id, |
| @@ -56,10 +79,10 @@ public: | |||
| 56 | void UpdateLocalSystemClockContext(const Clock::SystemClockContext& context); | 79 | void UpdateLocalSystemClockContext(const Clock::SystemClockContext& context); |
| 57 | void UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context); | 80 | void UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context); |
| 58 | void SetAutomaticCorrectionEnabled(bool is_enabled); | 81 | void SetAutomaticCorrectionEnabled(bool is_enabled); |
| 82 | Format* GetFormat(); | ||
| 59 | 83 | ||
| 60 | private: | 84 | private: |
| 61 | Core::System& system; | 85 | Core::System& system; |
| 62 | Format shared_memory_format{}; | ||
| 63 | }; | 86 | }; |
| 64 | 87 | ||
| 65 | } // namespace Service::Time | 88 | } // namespace Service::Time |
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index 7932aaab0..f24c89b04 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt | |||
| @@ -20,6 +20,8 @@ add_library(input_common STATIC | |||
| 20 | drivers/udp_client.h | 20 | drivers/udp_client.h |
| 21 | drivers/virtual_amiibo.cpp | 21 | drivers/virtual_amiibo.cpp |
| 22 | drivers/virtual_amiibo.h | 22 | drivers/virtual_amiibo.h |
| 23 | drivers/virtual_gamepad.cpp | ||
| 24 | drivers/virtual_gamepad.h | ||
| 23 | helpers/stick_from_buttons.cpp | 25 | helpers/stick_from_buttons.cpp |
| 24 | helpers/stick_from_buttons.h | 26 | helpers/stick_from_buttons.h |
| 25 | helpers/touch_from_buttons.cpp | 27 | helpers/touch_from_buttons.cpp |
diff --git a/src/input_common/drivers/camera.cpp b/src/input_common/drivers/camera.cpp index dceea67e0..fad9177dc 100644 --- a/src/input_common/drivers/camera.cpp +++ b/src/input_common/drivers/camera.cpp | |||
| @@ -17,7 +17,7 @@ Camera::Camera(std::string input_engine_) : InputEngine(std::move(input_engine_) | |||
| 17 | PreSetController(identifier); | 17 | PreSetController(identifier); |
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | void Camera::SetCameraData(std::size_t width, std::size_t height, std::vector<u32> data) { | 20 | void Camera::SetCameraData(std::size_t width, std::size_t height, std::span<const u32> data) { |
| 21 | const std::size_t desired_width = getImageWidth(); | 21 | const std::size_t desired_width = getImageWidth(); |
| 22 | const std::size_t desired_height = getImageHeight(); | 22 | const std::size_t desired_height = getImageHeight(); |
| 23 | status.data.resize(desired_width * desired_height); | 23 | status.data.resize(desired_width * desired_height); |
diff --git a/src/input_common/drivers/camera.h b/src/input_common/drivers/camera.h index b8a7c75e5..ead3e0fde 100644 --- a/src/input_common/drivers/camera.h +++ b/src/input_common/drivers/camera.h | |||
| @@ -3,6 +3,8 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <span> | ||
| 7 | |||
| 6 | #include "input_common/input_engine.h" | 8 | #include "input_common/input_engine.h" |
| 7 | 9 | ||
| 8 | namespace InputCommon { | 10 | namespace InputCommon { |
| @@ -15,7 +17,7 @@ class Camera final : public InputEngine { | |||
| 15 | public: | 17 | public: |
| 16 | explicit Camera(std::string input_engine_); | 18 | explicit Camera(std::string input_engine_); |
| 17 | 19 | ||
| 18 | void SetCameraData(std::size_t width, std::size_t height, std::vector<u32> data); | 20 | void SetCameraData(std::size_t width, std::size_t height, std::span<const u32> data); |
| 19 | 21 | ||
| 20 | std::size_t getImageWidth() const; | 22 | std::size_t getImageWidth() const; |
| 21 | std::size_t getImageHeight() const; | 23 | std::size_t getImageHeight() const; |
| @@ -23,6 +25,7 @@ public: | |||
| 23 | Common::Input::CameraError SetCameraFormat(const PadIdentifier& identifier_, | 25 | Common::Input::CameraError SetCameraFormat(const PadIdentifier& identifier_, |
| 24 | Common::Input::CameraFormat camera_format) override; | 26 | Common::Input::CameraFormat camera_format) override; |
| 25 | 27 | ||
| 28 | private: | ||
| 26 | Common::Input::CameraStatus status{}; | 29 | Common::Input::CameraStatus status{}; |
| 27 | }; | 30 | }; |
| 28 | 31 | ||
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index 8de86b61e..4818bb744 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp | |||
| @@ -16,6 +16,8 @@ Common::UUID GetGUID(SDL_Joystick* joystick) { | |||
| 16 | const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick); | 16 | const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick); |
| 17 | std::array<u8, 16> data{}; | 17 | std::array<u8, 16> data{}; |
| 18 | std::memcpy(data.data(), guid.data, sizeof(data)); | 18 | std::memcpy(data.data(), guid.data, sizeof(data)); |
| 19 | // Clear controller name crc | ||
| 20 | std::memset(data.data() + 2, 0, sizeof(u16)); | ||
| 19 | return Common::UUID{data}; | 21 | return Common::UUID{data}; |
| 20 | } | 22 | } |
| 21 | } // Anonymous namespace | 23 | } // Anonymous namespace |
diff --git a/src/input_common/drivers/virtual_gamepad.cpp b/src/input_common/drivers/virtual_gamepad.cpp new file mode 100644 index 000000000..7db945aa6 --- /dev/null +++ b/src/input_common/drivers/virtual_gamepad.cpp | |||
| @@ -0,0 +1,78 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "input_common/drivers/virtual_gamepad.h" | ||
| 5 | |||
| 6 | namespace InputCommon { | ||
| 7 | constexpr std::size_t PlayerIndexCount = 10; | ||
| 8 | |||
| 9 | VirtualGamepad::VirtualGamepad(std::string input_engine_) : InputEngine(std::move(input_engine_)) { | ||
| 10 | for (std::size_t i = 0; i < PlayerIndexCount; i++) { | ||
| 11 | PreSetController(GetIdentifier(i)); | ||
| 12 | } | ||
| 13 | } | ||
| 14 | |||
| 15 | void VirtualGamepad::SetButtonState(std::size_t player_index, int button_id, bool value) { | ||
| 16 | if (player_index > PlayerIndexCount) { | ||
| 17 | return; | ||
| 18 | } | ||
| 19 | const auto identifier = GetIdentifier(player_index); | ||
| 20 | SetButton(identifier, button_id, value); | ||
| 21 | } | ||
| 22 | |||
| 23 | void VirtualGamepad::SetButtonState(std::size_t player_index, VirtualButton button_id, bool value) { | ||
| 24 | SetButtonState(player_index, static_cast<int>(button_id), value); | ||
| 25 | } | ||
| 26 | |||
| 27 | void VirtualGamepad::SetStickPosition(std::size_t player_index, int axis_id, float x_value, | ||
| 28 | float y_value) { | ||
| 29 | if (player_index > PlayerIndexCount) { | ||
| 30 | return; | ||
| 31 | } | ||
| 32 | const auto identifier = GetIdentifier(player_index); | ||
| 33 | SetAxis(identifier, axis_id * 2, x_value); | ||
| 34 | SetAxis(identifier, (axis_id * 2) + 1, y_value); | ||
| 35 | } | ||
| 36 | |||
| 37 | void VirtualGamepad::SetStickPosition(std::size_t player_index, VirtualStick axis_id, float x_value, | ||
| 38 | float y_value) { | ||
| 39 | SetStickPosition(player_index, static_cast<int>(axis_id), x_value, y_value); | ||
| 40 | } | ||
| 41 | |||
| 42 | void VirtualGamepad::ResetControllers() { | ||
| 43 | for (std::size_t i = 0; i < PlayerIndexCount; i++) { | ||
| 44 | SetStickPosition(i, VirtualStick::Left, 0.0f, 0.0f); | ||
| 45 | SetStickPosition(i, VirtualStick::Right, 0.0f, 0.0f); | ||
| 46 | |||
| 47 | SetButtonState(i, VirtualButton::ButtonA, false); | ||
| 48 | SetButtonState(i, VirtualButton::ButtonB, false); | ||
| 49 | SetButtonState(i, VirtualButton::ButtonX, false); | ||
| 50 | SetButtonState(i, VirtualButton::ButtonY, false); | ||
| 51 | SetButtonState(i, VirtualButton::StickL, false); | ||
| 52 | SetButtonState(i, VirtualButton::StickR, false); | ||
| 53 | SetButtonState(i, VirtualButton::TriggerL, false); | ||
| 54 | SetButtonState(i, VirtualButton::TriggerR, false); | ||
| 55 | SetButtonState(i, VirtualButton::TriggerZL, false); | ||
| 56 | SetButtonState(i, VirtualButton::TriggerZR, false); | ||
| 57 | SetButtonState(i, VirtualButton::ButtonPlus, false); | ||
| 58 | SetButtonState(i, VirtualButton::ButtonMinus, false); | ||
| 59 | SetButtonState(i, VirtualButton::ButtonLeft, false); | ||
| 60 | SetButtonState(i, VirtualButton::ButtonUp, false); | ||
| 61 | SetButtonState(i, VirtualButton::ButtonRight, false); | ||
| 62 | SetButtonState(i, VirtualButton::ButtonDown, false); | ||
| 63 | SetButtonState(i, VirtualButton::ButtonSL, false); | ||
| 64 | SetButtonState(i, VirtualButton::ButtonSR, false); | ||
| 65 | SetButtonState(i, VirtualButton::ButtonHome, false); | ||
| 66 | SetButtonState(i, VirtualButton::ButtonCapture, false); | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | PadIdentifier VirtualGamepad::GetIdentifier(std::size_t player_index) const { | ||
| 71 | return { | ||
| 72 | .guid = Common::UUID{}, | ||
| 73 | .port = player_index, | ||
| 74 | .pad = 0, | ||
| 75 | }; | ||
| 76 | } | ||
| 77 | |||
| 78 | } // namespace InputCommon | ||
diff --git a/src/input_common/drivers/virtual_gamepad.h b/src/input_common/drivers/virtual_gamepad.h new file mode 100644 index 000000000..3df91cc6f --- /dev/null +++ b/src/input_common/drivers/virtual_gamepad.h | |||
| @@ -0,0 +1,73 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "input_common/input_engine.h" | ||
| 7 | |||
| 8 | namespace InputCommon { | ||
| 9 | |||
| 10 | /** | ||
| 11 | * A virtual controller that is always assigned to the game input | ||
| 12 | */ | ||
| 13 | class VirtualGamepad final : public InputEngine { | ||
| 14 | public: | ||
| 15 | enum class VirtualButton { | ||
| 16 | ButtonA, | ||
| 17 | ButtonB, | ||
| 18 | ButtonX, | ||
| 19 | ButtonY, | ||
| 20 | StickL, | ||
| 21 | StickR, | ||
| 22 | TriggerL, | ||
| 23 | TriggerR, | ||
| 24 | TriggerZL, | ||
| 25 | TriggerZR, | ||
| 26 | ButtonPlus, | ||
| 27 | ButtonMinus, | ||
| 28 | ButtonLeft, | ||
| 29 | ButtonUp, | ||
| 30 | ButtonRight, | ||
| 31 | ButtonDown, | ||
| 32 | ButtonSL, | ||
| 33 | ButtonSR, | ||
| 34 | ButtonHome, | ||
| 35 | ButtonCapture, | ||
| 36 | }; | ||
| 37 | |||
| 38 | enum class VirtualStick { | ||
| 39 | Left = 0, | ||
| 40 | Right = 1, | ||
| 41 | }; | ||
| 42 | |||
| 43 | explicit VirtualGamepad(std::string input_engine_); | ||
| 44 | |||
| 45 | /** | ||
| 46 | * Sets the status of all buttons bound with the key to pressed | ||
| 47 | * @param player_index the player number that will take this action | ||
| 48 | * @param button_id the id of the button | ||
| 49 | * @param value indicates if the button is pressed or not | ||
| 50 | */ | ||
| 51 | void SetButtonState(std::size_t player_index, int button_id, bool value); | ||
| 52 | void SetButtonState(std::size_t player_index, VirtualButton button_id, bool value); | ||
| 53 | |||
| 54 | /** | ||
| 55 | * Sets the status of all buttons bound with the key to released | ||
| 56 | * @param player_index the player number that will take this action | ||
| 57 | * @param axis_id the id of the axis to move | ||
| 58 | * @param x_value the position of the stick in the x axis | ||
| 59 | * @param y_value the position of the stick in the y axis | ||
| 60 | */ | ||
| 61 | void SetStickPosition(std::size_t player_index, int axis_id, float x_value, float y_value); | ||
| 62 | void SetStickPosition(std::size_t player_index, VirtualStick axis_id, float x_value, | ||
| 63 | float y_value); | ||
| 64 | |||
| 65 | /// Restores all inputs into the neutral position | ||
| 66 | void ResetControllers(); | ||
| 67 | |||
| 68 | private: | ||
| 69 | /// Returns the correct identifier corresponding to the player index | ||
| 70 | PadIdentifier GetIdentifier(std::size_t player_index) const; | ||
| 71 | }; | ||
| 72 | |||
| 73 | } // namespace InputCommon | ||
diff --git a/src/input_common/input_mapping.cpp b/src/input_common/input_mapping.cpp index 0fa4b1ddb..edd5287c1 100644 --- a/src/input_common/input_mapping.cpp +++ b/src/input_common/input_mapping.cpp | |||
| @@ -200,12 +200,6 @@ bool MappingFactory::IsDriverValid(const MappingData& data) const { | |||
| 200 | return false; | 200 | return false; |
| 201 | } | 201 | } |
| 202 | // The following drivers don't need to be mapped | 202 | // The following drivers don't need to be mapped |
| 203 | if (data.engine == "tas") { | ||
| 204 | return false; | ||
| 205 | } | ||
| 206 | if (data.engine == "touch") { | ||
| 207 | return false; | ||
| 208 | } | ||
| 209 | if (data.engine == "touch_from_button") { | 203 | if (data.engine == "touch_from_button") { |
| 210 | return false; | 204 | return false; |
| 211 | } | 205 | } |
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index 942a13535..86deb4c7c 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include "input_common/drivers/touch_screen.h" | 12 | #include "input_common/drivers/touch_screen.h" |
| 13 | #include "input_common/drivers/udp_client.h" | 13 | #include "input_common/drivers/udp_client.h" |
| 14 | #include "input_common/drivers/virtual_amiibo.h" | 14 | #include "input_common/drivers/virtual_amiibo.h" |
| 15 | #include "input_common/drivers/virtual_gamepad.h" | ||
| 15 | #include "input_common/helpers/stick_from_buttons.h" | 16 | #include "input_common/helpers/stick_from_buttons.h" |
| 16 | #include "input_common/helpers/touch_from_buttons.h" | 17 | #include "input_common/helpers/touch_from_buttons.h" |
| 17 | #include "input_common/input_engine.h" | 18 | #include "input_common/input_engine.h" |
| @@ -25,73 +26,33 @@ | |||
| 25 | namespace InputCommon { | 26 | namespace InputCommon { |
| 26 | 27 | ||
| 27 | struct InputSubsystem::Impl { | 28 | struct InputSubsystem::Impl { |
| 28 | void Initialize() { | 29 | template <typename Engine> |
| 29 | mapping_factory = std::make_shared<MappingFactory>(); | 30 | void RegisterEngine(std::string name, std::shared_ptr<Engine>& engine) { |
| 30 | MappingCallback mapping_callback{[this](const MappingData& data) { RegisterInput(data); }}; | 31 | MappingCallback mapping_callback{[this](const MappingData& data) { RegisterInput(data); }}; |
| 31 | 32 | ||
| 32 | keyboard = std::make_shared<Keyboard>("keyboard"); | 33 | engine = std::make_shared<Engine>(name); |
| 33 | keyboard->SetMappingCallback(mapping_callback); | 34 | engine->SetMappingCallback(mapping_callback); |
| 34 | keyboard_factory = std::make_shared<InputFactory>(keyboard); | 35 | |
| 35 | keyboard_output_factory = std::make_shared<OutputFactory>(keyboard); | 36 | std::shared_ptr<InputFactory> input_factory = std::make_shared<InputFactory>(engine); |
| 36 | Common::Input::RegisterInputFactory(keyboard->GetEngineName(), keyboard_factory); | 37 | std::shared_ptr<OutputFactory> output_factory = std::make_shared<OutputFactory>(engine); |
| 37 | Common::Input::RegisterOutputFactory(keyboard->GetEngineName(), keyboard_output_factory); | 38 | Common::Input::RegisterInputFactory(engine->GetEngineName(), std::move(input_factory)); |
| 38 | 39 | Common::Input::RegisterOutputFactory(engine->GetEngineName(), std::move(output_factory)); | |
| 39 | mouse = std::make_shared<Mouse>("mouse"); | 40 | } |
| 40 | mouse->SetMappingCallback(mapping_callback); | 41 | |
| 41 | mouse_factory = std::make_shared<InputFactory>(mouse); | 42 | void Initialize() { |
| 42 | mouse_output_factory = std::make_shared<OutputFactory>(mouse); | 43 | mapping_factory = std::make_shared<MappingFactory>(); |
| 43 | Common::Input::RegisterInputFactory(mouse->GetEngineName(), mouse_factory); | ||
| 44 | Common::Input::RegisterOutputFactory(mouse->GetEngineName(), mouse_output_factory); | ||
| 45 | |||
| 46 | touch_screen = std::make_shared<TouchScreen>("touch"); | ||
| 47 | touch_screen_factory = std::make_shared<InputFactory>(touch_screen); | ||
| 48 | Common::Input::RegisterInputFactory(touch_screen->GetEngineName(), touch_screen_factory); | ||
| 49 | |||
| 50 | gcadapter = std::make_shared<GCAdapter>("gcpad"); | ||
| 51 | gcadapter->SetMappingCallback(mapping_callback); | ||
| 52 | gcadapter_input_factory = std::make_shared<InputFactory>(gcadapter); | ||
| 53 | gcadapter_output_factory = std::make_shared<OutputFactory>(gcadapter); | ||
| 54 | Common::Input::RegisterInputFactory(gcadapter->GetEngineName(), gcadapter_input_factory); | ||
| 55 | Common::Input::RegisterOutputFactory(gcadapter->GetEngineName(), gcadapter_output_factory); | ||
| 56 | |||
| 57 | udp_client = std::make_shared<CemuhookUDP::UDPClient>("cemuhookudp"); | ||
| 58 | udp_client->SetMappingCallback(mapping_callback); | ||
| 59 | udp_client_input_factory = std::make_shared<InputFactory>(udp_client); | ||
| 60 | udp_client_output_factory = std::make_shared<OutputFactory>(udp_client); | ||
| 61 | Common::Input::RegisterInputFactory(udp_client->GetEngineName(), udp_client_input_factory); | ||
| 62 | Common::Input::RegisterOutputFactory(udp_client->GetEngineName(), | ||
| 63 | udp_client_output_factory); | ||
| 64 | |||
| 65 | tas_input = std::make_shared<TasInput::Tas>("tas"); | ||
| 66 | tas_input->SetMappingCallback(mapping_callback); | ||
| 67 | tas_input_factory = std::make_shared<InputFactory>(tas_input); | ||
| 68 | tas_output_factory = std::make_shared<OutputFactory>(tas_input); | ||
| 69 | Common::Input::RegisterInputFactory(tas_input->GetEngineName(), tas_input_factory); | ||
| 70 | Common::Input::RegisterOutputFactory(tas_input->GetEngineName(), tas_output_factory); | ||
| 71 | |||
| 72 | camera = std::make_shared<Camera>("camera"); | ||
| 73 | camera->SetMappingCallback(mapping_callback); | ||
| 74 | camera_input_factory = std::make_shared<InputFactory>(camera); | ||
| 75 | camera_output_factory = std::make_shared<OutputFactory>(camera); | ||
| 76 | Common::Input::RegisterInputFactory(camera->GetEngineName(), camera_input_factory); | ||
| 77 | Common::Input::RegisterOutputFactory(camera->GetEngineName(), camera_output_factory); | ||
| 78 | |||
| 79 | virtual_amiibo = std::make_shared<VirtualAmiibo>("virtual_amiibo"); | ||
| 80 | virtual_amiibo->SetMappingCallback(mapping_callback); | ||
| 81 | virtual_amiibo_input_factory = std::make_shared<InputFactory>(virtual_amiibo); | ||
| 82 | virtual_amiibo_output_factory = std::make_shared<OutputFactory>(virtual_amiibo); | ||
| 83 | Common::Input::RegisterInputFactory(virtual_amiibo->GetEngineName(), | ||
| 84 | virtual_amiibo_input_factory); | ||
| 85 | Common::Input::RegisterOutputFactory(virtual_amiibo->GetEngineName(), | ||
| 86 | virtual_amiibo_output_factory); | ||
| 87 | 44 | ||
| 45 | RegisterEngine("keyboard", keyboard); | ||
| 46 | RegisterEngine("mouse", mouse); | ||
| 47 | RegisterEngine("touch", touch_screen); | ||
| 48 | RegisterEngine("gcpad", gcadapter); | ||
| 49 | RegisterEngine("cemuhookudp", udp_client); | ||
| 50 | RegisterEngine("tas", tas_input); | ||
| 51 | RegisterEngine("camera", camera); | ||
| 52 | RegisterEngine("virtual_amiibo", virtual_amiibo); | ||
| 53 | RegisterEngine("virtual_gamepad", virtual_gamepad); | ||
| 88 | #ifdef HAVE_SDL2 | 54 | #ifdef HAVE_SDL2 |
| 89 | sdl = std::make_shared<SDLDriver>("sdl"); | 55 | RegisterEngine("sdl", sdl); |
| 90 | sdl->SetMappingCallback(mapping_callback); | ||
| 91 | sdl_input_factory = std::make_shared<InputFactory>(sdl); | ||
| 92 | sdl_output_factory = std::make_shared<OutputFactory>(sdl); | ||
| 93 | Common::Input::RegisterInputFactory(sdl->GetEngineName(), sdl_input_factory); | ||
| 94 | Common::Input::RegisterOutputFactory(sdl->GetEngineName(), sdl_output_factory); | ||
| 95 | #endif | 56 | #endif |
| 96 | 57 | ||
| 97 | Common::Input::RegisterInputFactory("touch_from_button", | 58 | Common::Input::RegisterInputFactory("touch_from_button", |
| @@ -100,42 +61,25 @@ struct InputSubsystem::Impl { | |||
| 100 | std::make_shared<StickFromButton>()); | 61 | std::make_shared<StickFromButton>()); |
| 101 | } | 62 | } |
| 102 | 63 | ||
| 103 | void Shutdown() { | 64 | template <typename Engine> |
| 104 | Common::Input::UnregisterInputFactory(keyboard->GetEngineName()); | 65 | void UnregisterEngine(std::shared_ptr<Engine>& engine) { |
| 105 | Common::Input::UnregisterOutputFactory(keyboard->GetEngineName()); | 66 | Common::Input::UnregisterInputFactory(engine->GetEngineName()); |
| 106 | keyboard.reset(); | 67 | Common::Input::UnregisterOutputFactory(engine->GetEngineName()); |
| 107 | 68 | engine.reset(); | |
| 108 | Common::Input::UnregisterInputFactory(mouse->GetEngineName()); | 69 | } |
| 109 | Common::Input::UnregisterOutputFactory(mouse->GetEngineName()); | ||
| 110 | mouse.reset(); | ||
| 111 | |||
| 112 | Common::Input::UnregisterInputFactory(touch_screen->GetEngineName()); | ||
| 113 | touch_screen.reset(); | ||
| 114 | |||
| 115 | Common::Input::UnregisterInputFactory(gcadapter->GetEngineName()); | ||
| 116 | Common::Input::UnregisterOutputFactory(gcadapter->GetEngineName()); | ||
| 117 | gcadapter.reset(); | ||
| 118 | |||
| 119 | Common::Input::UnregisterInputFactory(udp_client->GetEngineName()); | ||
| 120 | Common::Input::UnregisterOutputFactory(udp_client->GetEngineName()); | ||
| 121 | udp_client.reset(); | ||
| 122 | |||
| 123 | Common::Input::UnregisterInputFactory(tas_input->GetEngineName()); | ||
| 124 | Common::Input::UnregisterOutputFactory(tas_input->GetEngineName()); | ||
| 125 | tas_input.reset(); | ||
| 126 | |||
| 127 | Common::Input::UnregisterInputFactory(camera->GetEngineName()); | ||
| 128 | Common::Input::UnregisterOutputFactory(camera->GetEngineName()); | ||
| 129 | camera.reset(); | ||
| 130 | |||
| 131 | Common::Input::UnregisterInputFactory(virtual_amiibo->GetEngineName()); | ||
| 132 | Common::Input::UnregisterOutputFactory(virtual_amiibo->GetEngineName()); | ||
| 133 | virtual_amiibo.reset(); | ||
| 134 | 70 | ||
| 71 | void Shutdown() { | ||
| 72 | UnregisterEngine(keyboard); | ||
| 73 | UnregisterEngine(mouse); | ||
| 74 | UnregisterEngine(touch_screen); | ||
| 75 | UnregisterEngine(gcadapter); | ||
| 76 | UnregisterEngine(udp_client); | ||
| 77 | UnregisterEngine(tas_input); | ||
| 78 | UnregisterEngine(camera); | ||
| 79 | UnregisterEngine(virtual_amiibo); | ||
| 80 | UnregisterEngine(virtual_gamepad); | ||
| 135 | #ifdef HAVE_SDL2 | 81 | #ifdef HAVE_SDL2 |
| 136 | Common::Input::UnregisterInputFactory(sdl->GetEngineName()); | 82 | UnregisterEngine(sdl); |
| 137 | Common::Input::UnregisterOutputFactory(sdl->GetEngineName()); | ||
| 138 | sdl.reset(); | ||
| 139 | #endif | 83 | #endif |
| 140 | 84 | ||
| 141 | Common::Input::UnregisterInputFactory("touch_from_button"); | 85 | Common::Input::UnregisterInputFactory("touch_from_button"); |
| @@ -163,117 +107,86 @@ struct InputSubsystem::Impl { | |||
| 163 | return devices; | 107 | return devices; |
| 164 | } | 108 | } |
| 165 | 109 | ||
| 166 | [[nodiscard]] AnalogMapping GetAnalogMappingForDevice( | 110 | [[nodiscard]] std::shared_ptr<InputEngine> GetInputEngine( |
| 167 | const Common::ParamPackage& params) const { | 111 | const Common::ParamPackage& params) const { |
| 168 | if (!params.Has("engine") || params.Get("engine", "") == "any") { | 112 | if (!params.Has("engine") || params.Get("engine", "") == "any") { |
| 169 | return {}; | 113 | return nullptr; |
| 170 | } | 114 | } |
| 171 | const std::string engine = params.Get("engine", ""); | 115 | const std::string engine = params.Get("engine", ""); |
| 116 | if (engine == keyboard->GetEngineName()) { | ||
| 117 | return keyboard; | ||
| 118 | } | ||
| 172 | if (engine == mouse->GetEngineName()) { | 119 | if (engine == mouse->GetEngineName()) { |
| 173 | return mouse->GetAnalogMappingForDevice(params); | 120 | return mouse; |
| 174 | } | 121 | } |
| 175 | if (engine == gcadapter->GetEngineName()) { | 122 | if (engine == gcadapter->GetEngineName()) { |
| 176 | return gcadapter->GetAnalogMappingForDevice(params); | 123 | return gcadapter; |
| 177 | } | 124 | } |
| 178 | if (engine == udp_client->GetEngineName()) { | 125 | if (engine == udp_client->GetEngineName()) { |
| 179 | return udp_client->GetAnalogMappingForDevice(params); | 126 | return udp_client; |
| 180 | } | ||
| 181 | if (engine == tas_input->GetEngineName()) { | ||
| 182 | return tas_input->GetAnalogMappingForDevice(params); | ||
| 183 | } | 127 | } |
| 184 | #ifdef HAVE_SDL2 | 128 | #ifdef HAVE_SDL2 |
| 185 | if (engine == sdl->GetEngineName()) { | 129 | if (engine == sdl->GetEngineName()) { |
| 186 | return sdl->GetAnalogMappingForDevice(params); | 130 | return sdl; |
| 187 | } | 131 | } |
| 188 | #endif | 132 | #endif |
| 189 | return {}; | 133 | return nullptr; |
| 190 | } | 134 | } |
| 191 | 135 | ||
| 192 | [[nodiscard]] ButtonMapping GetButtonMappingForDevice( | 136 | [[nodiscard]] AnalogMapping GetAnalogMappingForDevice( |
| 193 | const Common::ParamPackage& params) const { | 137 | const Common::ParamPackage& params) const { |
| 194 | if (!params.Has("engine") || params.Get("engine", "") == "any") { | 138 | const auto input_engine = GetInputEngine(params); |
| 139 | |||
| 140 | if (input_engine == nullptr) { | ||
| 195 | return {}; | 141 | return {}; |
| 196 | } | 142 | } |
| 197 | const std::string engine = params.Get("engine", ""); | 143 | |
| 198 | if (engine == gcadapter->GetEngineName()) { | 144 | return input_engine->GetAnalogMappingForDevice(params); |
| 199 | return gcadapter->GetButtonMappingForDevice(params); | 145 | } |
| 200 | } | 146 | |
| 201 | if (engine == udp_client->GetEngineName()) { | 147 | [[nodiscard]] ButtonMapping GetButtonMappingForDevice( |
| 202 | return udp_client->GetButtonMappingForDevice(params); | 148 | const Common::ParamPackage& params) const { |
| 203 | } | 149 | const auto input_engine = GetInputEngine(params); |
| 204 | if (engine == tas_input->GetEngineName()) { | 150 | |
| 205 | return tas_input->GetButtonMappingForDevice(params); | 151 | if (input_engine == nullptr) { |
| 206 | } | 152 | return {}; |
| 207 | #ifdef HAVE_SDL2 | ||
| 208 | if (engine == sdl->GetEngineName()) { | ||
| 209 | return sdl->GetButtonMappingForDevice(params); | ||
| 210 | } | 153 | } |
| 211 | #endif | 154 | |
| 212 | return {}; | 155 | return input_engine->GetButtonMappingForDevice(params); |
| 213 | } | 156 | } |
| 214 | 157 | ||
| 215 | [[nodiscard]] MotionMapping GetMotionMappingForDevice( | 158 | [[nodiscard]] MotionMapping GetMotionMappingForDevice( |
| 216 | const Common::ParamPackage& params) const { | 159 | const Common::ParamPackage& params) const { |
| 217 | if (!params.Has("engine") || params.Get("engine", "") == "any") { | 160 | const auto input_engine = GetInputEngine(params); |
| 161 | |||
| 162 | if (input_engine == nullptr) { | ||
| 218 | return {}; | 163 | return {}; |
| 219 | } | 164 | } |
| 220 | const std::string engine = params.Get("engine", ""); | 165 | |
| 221 | if (engine == udp_client->GetEngineName()) { | 166 | return input_engine->GetMotionMappingForDevice(params); |
| 222 | return udp_client->GetMotionMappingForDevice(params); | ||
| 223 | } | ||
| 224 | #ifdef HAVE_SDL2 | ||
| 225 | if (engine == sdl->GetEngineName()) { | ||
| 226 | return sdl->GetMotionMappingForDevice(params); | ||
| 227 | } | ||
| 228 | #endif | ||
| 229 | return {}; | ||
| 230 | } | 167 | } |
| 231 | 168 | ||
| 232 | Common::Input::ButtonNames GetButtonName(const Common::ParamPackage& params) const { | 169 | Common::Input::ButtonNames GetButtonName(const Common::ParamPackage& params) const { |
| 233 | if (!params.Has("engine") || params.Get("engine", "") == "any") { | 170 | if (!params.Has("engine") || params.Get("engine", "") == "any") { |
| 234 | return Common::Input::ButtonNames::Undefined; | 171 | return Common::Input::ButtonNames::Undefined; |
| 235 | } | 172 | } |
| 236 | const std::string engine = params.Get("engine", ""); | 173 | const auto input_engine = GetInputEngine(params); |
| 237 | if (engine == mouse->GetEngineName()) { | 174 | |
| 238 | return mouse->GetUIName(params); | 175 | if (input_engine == nullptr) { |
| 239 | } | 176 | return Common::Input::ButtonNames::Invalid; |
| 240 | if (engine == gcadapter->GetEngineName()) { | ||
| 241 | return gcadapter->GetUIName(params); | ||
| 242 | } | ||
| 243 | if (engine == udp_client->GetEngineName()) { | ||
| 244 | return udp_client->GetUIName(params); | ||
| 245 | } | ||
| 246 | if (engine == tas_input->GetEngineName()) { | ||
| 247 | return tas_input->GetUIName(params); | ||
| 248 | } | ||
| 249 | #ifdef HAVE_SDL2 | ||
| 250 | if (engine == sdl->GetEngineName()) { | ||
| 251 | return sdl->GetUIName(params); | ||
| 252 | } | 177 | } |
| 253 | #endif | 178 | |
| 254 | return Common::Input::ButtonNames::Invalid; | 179 | return input_engine->GetUIName(params); |
| 255 | } | 180 | } |
| 256 | 181 | ||
| 257 | bool IsStickInverted(const Common::ParamPackage& params) { | 182 | bool IsStickInverted(const Common::ParamPackage& params) { |
| 258 | const std::string engine = params.Get("engine", ""); | 183 | const auto input_engine = GetInputEngine(params); |
| 259 | if (engine == mouse->GetEngineName()) { | 184 | |
| 260 | return mouse->IsStickInverted(params); | 185 | if (input_engine == nullptr) { |
| 261 | } | 186 | return false; |
| 262 | if (engine == gcadapter->GetEngineName()) { | ||
| 263 | return gcadapter->IsStickInverted(params); | ||
| 264 | } | ||
| 265 | if (engine == udp_client->GetEngineName()) { | ||
| 266 | return udp_client->IsStickInverted(params); | ||
| 267 | } | ||
| 268 | if (engine == tas_input->GetEngineName()) { | ||
| 269 | return tas_input->IsStickInverted(params); | ||
| 270 | } | ||
| 271 | #ifdef HAVE_SDL2 | ||
| 272 | if (engine == sdl->GetEngineName()) { | ||
| 273 | return sdl->IsStickInverted(params); | ||
| 274 | } | 187 | } |
| 275 | #endif | 188 | |
| 276 | return false; | 189 | return input_engine->IsStickInverted(params); |
| 277 | } | 190 | } |
| 278 | 191 | ||
| 279 | bool IsController(const Common::ParamPackage& params) { | 192 | bool IsController(const Common::ParamPackage& params) { |
| @@ -290,6 +203,9 @@ struct InputSubsystem::Impl { | |||
| 290 | if (engine == tas_input->GetEngineName()) { | 203 | if (engine == tas_input->GetEngineName()) { |
| 291 | return true; | 204 | return true; |
| 292 | } | 205 | } |
| 206 | if (engine == virtual_gamepad->GetEngineName()) { | ||
| 207 | return true; | ||
| 208 | } | ||
| 293 | #ifdef HAVE_SDL2 | 209 | #ifdef HAVE_SDL2 |
| 294 | if (engine == sdl->GetEngineName()) { | 210 | if (engine == sdl->GetEngineName()) { |
| 295 | return true; | 211 | return true; |
| @@ -338,28 +254,10 @@ struct InputSubsystem::Impl { | |||
| 338 | std::shared_ptr<CemuhookUDP::UDPClient> udp_client; | 254 | std::shared_ptr<CemuhookUDP::UDPClient> udp_client; |
| 339 | std::shared_ptr<Camera> camera; | 255 | std::shared_ptr<Camera> camera; |
| 340 | std::shared_ptr<VirtualAmiibo> virtual_amiibo; | 256 | std::shared_ptr<VirtualAmiibo> virtual_amiibo; |
| 341 | 257 | std::shared_ptr<VirtualGamepad> virtual_gamepad; | |
| 342 | std::shared_ptr<InputFactory> keyboard_factory; | ||
| 343 | std::shared_ptr<InputFactory> mouse_factory; | ||
| 344 | std::shared_ptr<InputFactory> gcadapter_input_factory; | ||
| 345 | std::shared_ptr<InputFactory> touch_screen_factory; | ||
| 346 | std::shared_ptr<InputFactory> udp_client_input_factory; | ||
| 347 | std::shared_ptr<InputFactory> tas_input_factory; | ||
| 348 | std::shared_ptr<InputFactory> camera_input_factory; | ||
| 349 | std::shared_ptr<InputFactory> virtual_amiibo_input_factory; | ||
| 350 | |||
| 351 | std::shared_ptr<OutputFactory> keyboard_output_factory; | ||
| 352 | std::shared_ptr<OutputFactory> mouse_output_factory; | ||
| 353 | std::shared_ptr<OutputFactory> gcadapter_output_factory; | ||
| 354 | std::shared_ptr<OutputFactory> udp_client_output_factory; | ||
| 355 | std::shared_ptr<OutputFactory> tas_output_factory; | ||
| 356 | std::shared_ptr<OutputFactory> camera_output_factory; | ||
| 357 | std::shared_ptr<OutputFactory> virtual_amiibo_output_factory; | ||
| 358 | 258 | ||
| 359 | #ifdef HAVE_SDL2 | 259 | #ifdef HAVE_SDL2 |
| 360 | std::shared_ptr<SDLDriver> sdl; | 260 | std::shared_ptr<SDLDriver> sdl; |
| 361 | std::shared_ptr<InputFactory> sdl_input_factory; | ||
| 362 | std::shared_ptr<OutputFactory> sdl_output_factory; | ||
| 363 | #endif | 261 | #endif |
| 364 | }; | 262 | }; |
| 365 | 263 | ||
| @@ -423,6 +321,14 @@ const VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() const { | |||
| 423 | return impl->virtual_amiibo.get(); | 321 | return impl->virtual_amiibo.get(); |
| 424 | } | 322 | } |
| 425 | 323 | ||
| 324 | VirtualGamepad* InputSubsystem::GetVirtualGamepad() { | ||
| 325 | return impl->virtual_gamepad.get(); | ||
| 326 | } | ||
| 327 | |||
| 328 | const VirtualGamepad* InputSubsystem::GetVirtualGamepad() const { | ||
| 329 | return impl->virtual_gamepad.get(); | ||
| 330 | } | ||
| 331 | |||
| 426 | std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const { | 332 | std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const { |
| 427 | return impl->GetInputDevices(); | 333 | return impl->GetInputDevices(); |
| 428 | } | 334 | } |
diff --git a/src/input_common/main.h b/src/input_common/main.h index 6218c37f6..1207d786c 100644 --- a/src/input_common/main.h +++ b/src/input_common/main.h | |||
| @@ -34,6 +34,7 @@ class Keyboard; | |||
| 34 | class Mouse; | 34 | class Mouse; |
| 35 | class TouchScreen; | 35 | class TouchScreen; |
| 36 | class VirtualAmiibo; | 36 | class VirtualAmiibo; |
| 37 | class VirtualGamepad; | ||
| 37 | struct MappingData; | 38 | struct MappingData; |
| 38 | } // namespace InputCommon | 39 | } // namespace InputCommon |
| 39 | 40 | ||
| @@ -108,6 +109,12 @@ public: | |||
| 108 | /// Retrieves the underlying virtual amiibo input device. | 109 | /// Retrieves the underlying virtual amiibo input device. |
| 109 | [[nodiscard]] const VirtualAmiibo* GetVirtualAmiibo() const; | 110 | [[nodiscard]] const VirtualAmiibo* GetVirtualAmiibo() const; |
| 110 | 111 | ||
| 112 | /// Retrieves the underlying virtual gamepad input device. | ||
| 113 | [[nodiscard]] VirtualGamepad* GetVirtualGamepad(); | ||
| 114 | |||
| 115 | /// Retrieves the underlying virtual gamepad input device. | ||
| 116 | [[nodiscard]] const VirtualGamepad* GetVirtualGamepad() const; | ||
| 117 | |||
| 111 | /** | 118 | /** |
| 112 | * Returns all available input devices that this Factory can create a new device with. | 119 | * Returns all available input devices that this Factory can create a new device with. |
| 113 | * Each returned ParamPackage should have a `display` field used for display, a `engine` field | 120 | * Each returned ParamPackage should have a `display` field used for display, a `engine` field |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp index 01f6ec9b5..73b67f0af 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp | |||
| @@ -461,7 +461,7 @@ void EmitSetSampleMask(EmitContext& ctx, Id value) { | |||
| 461 | } | 461 | } |
| 462 | 462 | ||
| 463 | void EmitSetFragDepth(EmitContext& ctx, Id value) { | 463 | void EmitSetFragDepth(EmitContext& ctx, Id value) { |
| 464 | if (!ctx.runtime_info.convert_depth_mode) { | 464 | if (!ctx.runtime_info.convert_depth_mode || ctx.profile.support_native_ndc) { |
| 465 | ctx.OpStore(ctx.frag_depth, value); | 465 | ctx.OpStore(ctx.frag_depth, value); |
| 466 | return; | 466 | return; |
| 467 | } | 467 | } |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp index 00be1f127..9f7b6bb4b 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp | |||
| @@ -116,7 +116,8 @@ void EmitPrologue(EmitContext& ctx) { | |||
| 116 | } | 116 | } |
| 117 | 117 | ||
| 118 | void EmitEpilogue(EmitContext& ctx) { | 118 | void EmitEpilogue(EmitContext& ctx) { |
| 119 | if (ctx.stage == Stage::VertexB && ctx.runtime_info.convert_depth_mode) { | 119 | if (ctx.stage == Stage::VertexB && ctx.runtime_info.convert_depth_mode && |
| 120 | !ctx.profile.support_native_ndc) { | ||
| 120 | ConvertDepthMode(ctx); | 121 | ConvertDepthMode(ctx); |
| 121 | } | 122 | } |
| 122 | if (ctx.stage == Stage::Fragment) { | 123 | if (ctx.stage == Stage::Fragment) { |
| @@ -125,7 +126,7 @@ void EmitEpilogue(EmitContext& ctx) { | |||
| 125 | } | 126 | } |
| 126 | 127 | ||
| 127 | void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) { | 128 | void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) { |
| 128 | if (ctx.runtime_info.convert_depth_mode) { | 129 | if (ctx.runtime_info.convert_depth_mode && !ctx.profile.support_native_ndc) { |
| 129 | ConvertDepthMode(ctx); | 130 | ConvertDepthMode(ctx); |
| 130 | } | 131 | } |
| 131 | if (stream.IsImmediate()) { | 132 | if (stream.IsImmediate()) { |
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 8e3e40cd5..41dc6d031 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp | |||
| @@ -1345,8 +1345,10 @@ void EmitContext::DefineInputs(const IR::Program& program) { | |||
| 1345 | if (info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles || | 1345 | if (info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles || |
| 1346 | (profile.warp_size_potentially_larger_than_guest && | 1346 | (profile.warp_size_potentially_larger_than_guest && |
| 1347 | (info.uses_subgroup_vote || info.uses_subgroup_mask))) { | 1347 | (info.uses_subgroup_vote || info.uses_subgroup_mask))) { |
| 1348 | AddCapability(spv::Capability::GroupNonUniform); | ||
| 1348 | subgroup_local_invocation_id = | 1349 | subgroup_local_invocation_id = |
| 1349 | DefineInput(*this, U32[1], false, spv::BuiltIn::SubgroupLocalInvocationId); | 1350 | DefineInput(*this, U32[1], false, spv::BuiltIn::SubgroupLocalInvocationId); |
| 1351 | Decorate(subgroup_local_invocation_id, spv::Decoration::Flat); | ||
| 1350 | } | 1352 | } |
| 1351 | if (info.uses_fswzadd) { | 1353 | if (info.uses_fswzadd) { |
| 1352 | const Id f32_one{Const(1.0f)}; | 1354 | const Id f32_one{Const(1.0f)}; |
diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index 21d3d236b..b8841a536 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h | |||
| @@ -35,6 +35,7 @@ struct Profile { | |||
| 35 | bool support_int64_atomics{}; | 35 | bool support_int64_atomics{}; |
| 36 | bool support_derivative_control{}; | 36 | bool support_derivative_control{}; |
| 37 | bool support_geometry_shader_passthrough{}; | 37 | bool support_geometry_shader_passthrough{}; |
| 38 | bool support_native_ndc{}; | ||
| 38 | bool support_gl_nv_gpu_shader_5{}; | 39 | bool support_gl_nv_gpu_shader_5{}; |
| 39 | bool support_gl_amd_gpu_shader_half_float{}; | 40 | bool support_gl_amd_gpu_shader_half_float{}; |
| 40 | bool support_gl_texture_shadow_lod{}; | 41 | bool support_gl_texture_shadow_lod{}; |
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 348d1edf4..6a4022e45 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt | |||
| @@ -8,6 +8,7 @@ add_executable(tests | |||
| 8 | common/host_memory.cpp | 8 | common/host_memory.cpp |
| 9 | common/param_package.cpp | 9 | common/param_package.cpp |
| 10 | common/ring_buffer.cpp | 10 | common/ring_buffer.cpp |
| 11 | common/scratch_buffer.cpp | ||
| 11 | common/unique_function.cpp | 12 | common/unique_function.cpp |
| 12 | core/core_timing.cpp | 13 | core/core_timing.cpp |
| 13 | core/internal_network/network.cpp | 14 | core/internal_network/network.cpp |
diff --git a/src/tests/common/scratch_buffer.cpp b/src/tests/common/scratch_buffer.cpp new file mode 100644 index 000000000..f6e50da4a --- /dev/null +++ b/src/tests/common/scratch_buffer.cpp | |||
| @@ -0,0 +1,200 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <algorithm> | ||
| 5 | #include <array> | ||
| 6 | #include <cstring> | ||
| 7 | #include <span> | ||
| 8 | #include <catch2/catch.hpp> | ||
| 9 | #include "common/common_types.h" | ||
| 10 | #include "common/scratch_buffer.h" | ||
| 11 | |||
| 12 | namespace Common { | ||
| 13 | |||
| 14 | TEST_CASE("ScratchBuffer: Basic Test", "[common]") { | ||
| 15 | ScratchBuffer<u8> buf; | ||
| 16 | |||
| 17 | REQUIRE(buf.size() == 0U); | ||
| 18 | REQUIRE(buf.capacity() == 0U); | ||
| 19 | |||
| 20 | std::array<u8, 10> payload; | ||
| 21 | payload.fill(66); | ||
| 22 | |||
| 23 | buf.resize(payload.size()); | ||
| 24 | REQUIRE(buf.size() == payload.size()); | ||
| 25 | REQUIRE(buf.capacity() == payload.size()); | ||
| 26 | |||
| 27 | std::memcpy(buf.data(), payload.data(), payload.size()); | ||
| 28 | for (size_t i = 0; i < payload.size(); ++i) { | ||
| 29 | REQUIRE(buf[i] == payload[i]); | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | TEST_CASE("ScratchBuffer: resize_destructive Grow", "[common]") { | ||
| 34 | std::array<u8, 10> payload; | ||
| 35 | payload.fill(66); | ||
| 36 | |||
| 37 | ScratchBuffer<u8> buf(payload.size()); | ||
| 38 | REQUIRE(buf.size() == payload.size()); | ||
| 39 | REQUIRE(buf.capacity() == payload.size()); | ||
| 40 | |||
| 41 | // Increasing the size should reallocate the buffer | ||
| 42 | buf.resize_destructive(payload.size() * 2); | ||
| 43 | REQUIRE(buf.size() == payload.size() * 2); | ||
| 44 | REQUIRE(buf.capacity() == payload.size() * 2); | ||
| 45 | |||
| 46 | // Since the buffer is not value initialized, reading its data will be garbage | ||
| 47 | } | ||
| 48 | |||
| 49 | TEST_CASE("ScratchBuffer: resize_destructive Shrink", "[common]") { | ||
| 50 | std::array<u8, 10> payload; | ||
| 51 | payload.fill(66); | ||
| 52 | |||
| 53 | ScratchBuffer<u8> buf(payload.size()); | ||
| 54 | REQUIRE(buf.size() == payload.size()); | ||
| 55 | REQUIRE(buf.capacity() == payload.size()); | ||
| 56 | |||
| 57 | std::memcpy(buf.data(), payload.data(), payload.size()); | ||
| 58 | for (size_t i = 0; i < payload.size(); ++i) { | ||
| 59 | REQUIRE(buf[i] == payload[i]); | ||
| 60 | } | ||
| 61 | |||
| 62 | // Decreasing the size should not cause a buffer reallocation | ||
| 63 | // This can be tested by ensuring the buffer capacity and data has not changed, | ||
| 64 | buf.resize_destructive(1U); | ||
| 65 | REQUIRE(buf.size() == 1U); | ||
| 66 | REQUIRE(buf.capacity() == payload.size()); | ||
| 67 | |||
| 68 | for (size_t i = 0; i < payload.size(); ++i) { | ||
| 69 | REQUIRE(buf[i] == payload[i]); | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | TEST_CASE("ScratchBuffer: resize Grow u8", "[common]") { | ||
| 74 | std::array<u8, 10> payload; | ||
| 75 | payload.fill(66); | ||
| 76 | |||
| 77 | ScratchBuffer<u8> buf(payload.size()); | ||
| 78 | REQUIRE(buf.size() == payload.size()); | ||
| 79 | REQUIRE(buf.capacity() == payload.size()); | ||
| 80 | |||
| 81 | std::memcpy(buf.data(), payload.data(), payload.size()); | ||
| 82 | for (size_t i = 0; i < payload.size(); ++i) { | ||
| 83 | REQUIRE(buf[i] == payload[i]); | ||
| 84 | } | ||
| 85 | |||
| 86 | // Increasing the size should reallocate the buffer | ||
| 87 | buf.resize(payload.size() * 2); | ||
| 88 | REQUIRE(buf.size() == payload.size() * 2); | ||
| 89 | REQUIRE(buf.capacity() == payload.size() * 2); | ||
| 90 | |||
| 91 | // resize() keeps the previous data intact | ||
| 92 | for (size_t i = 0; i < payload.size(); ++i) { | ||
| 93 | REQUIRE(buf[i] == payload[i]); | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | TEST_CASE("ScratchBuffer: resize Grow u64", "[common]") { | ||
| 98 | std::array<u64, 10> payload; | ||
| 99 | payload.fill(6666); | ||
| 100 | |||
| 101 | ScratchBuffer<u64> buf(payload.size()); | ||
| 102 | REQUIRE(buf.size() == payload.size()); | ||
| 103 | REQUIRE(buf.capacity() == payload.size()); | ||
| 104 | |||
| 105 | std::memcpy(buf.data(), payload.data(), payload.size() * sizeof(u64)); | ||
| 106 | for (size_t i = 0; i < payload.size(); ++i) { | ||
| 107 | REQUIRE(buf[i] == payload[i]); | ||
| 108 | } | ||
| 109 | |||
| 110 | // Increasing the size should reallocate the buffer | ||
| 111 | buf.resize(payload.size() * 2); | ||
| 112 | REQUIRE(buf.size() == payload.size() * 2); | ||
| 113 | REQUIRE(buf.capacity() == payload.size() * 2); | ||
| 114 | |||
| 115 | // resize() keeps the previous data intact | ||
| 116 | for (size_t i = 0; i < payload.size(); ++i) { | ||
| 117 | REQUIRE(buf[i] == payload[i]); | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | TEST_CASE("ScratchBuffer: resize Shrink", "[common]") { | ||
| 122 | std::array<u8, 10> payload; | ||
| 123 | payload.fill(66); | ||
| 124 | |||
| 125 | ScratchBuffer<u8> buf(payload.size()); | ||
| 126 | REQUIRE(buf.size() == payload.size()); | ||
| 127 | REQUIRE(buf.capacity() == payload.size()); | ||
| 128 | |||
| 129 | std::memcpy(buf.data(), payload.data(), payload.size()); | ||
| 130 | for (size_t i = 0; i < payload.size(); ++i) { | ||
| 131 | REQUIRE(buf[i] == payload[i]); | ||
| 132 | } | ||
| 133 | |||
| 134 | // Decreasing the size should not cause a buffer reallocation | ||
| 135 | // This can be tested by ensuring the buffer capacity and data has not changed, | ||
| 136 | buf.resize(1U); | ||
| 137 | REQUIRE(buf.size() == 1U); | ||
| 138 | REQUIRE(buf.capacity() == payload.size()); | ||
| 139 | |||
| 140 | for (size_t i = 0; i < payload.size(); ++i) { | ||
| 141 | REQUIRE(buf[i] == payload[i]); | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | TEST_CASE("ScratchBuffer: Span Size", "[common]") { | ||
| 146 | std::array<u8, 10> payload; | ||
| 147 | payload.fill(66); | ||
| 148 | |||
| 149 | ScratchBuffer<u8> buf(payload.size()); | ||
| 150 | REQUIRE(buf.size() == payload.size()); | ||
| 151 | REQUIRE(buf.capacity() == payload.size()); | ||
| 152 | |||
| 153 | std::memcpy(buf.data(), payload.data(), payload.size()); | ||
| 154 | for (size_t i = 0; i < payload.size(); ++i) { | ||
| 155 | REQUIRE(buf[i] == payload[i]); | ||
| 156 | } | ||
| 157 | |||
| 158 | buf.resize(3U); | ||
| 159 | REQUIRE(buf.size() == 3U); | ||
| 160 | REQUIRE(buf.capacity() == payload.size()); | ||
| 161 | |||
| 162 | const auto buf_span = std::span<u8>(buf); | ||
| 163 | // The span size is the last requested size of the buffer, not its capacity | ||
| 164 | REQUIRE(buf_span.size() == buf.size()); | ||
| 165 | |||
| 166 | for (size_t i = 0; i < buf_span.size(); ++i) { | ||
| 167 | REQUIRE(buf_span[i] == buf[i]); | ||
| 168 | REQUIRE(buf_span[i] == payload[i]); | ||
| 169 | } | ||
| 170 | } | ||
| 171 | |||
| 172 | TEST_CASE("ScratchBuffer: Span Writes", "[common]") { | ||
| 173 | std::array<u8, 10> payload; | ||
| 174 | payload.fill(66); | ||
| 175 | |||
| 176 | ScratchBuffer<u8> buf(payload.size()); | ||
| 177 | REQUIRE(buf.size() == payload.size()); | ||
| 178 | REQUIRE(buf.capacity() == payload.size()); | ||
| 179 | |||
| 180 | std::memcpy(buf.data(), payload.data(), payload.size()); | ||
| 181 | for (size_t i = 0; i < payload.size(); ++i) { | ||
| 182 | REQUIRE(buf[i] == payload[i]); | ||
| 183 | } | ||
| 184 | |||
| 185 | buf.resize(3U); | ||
| 186 | REQUIRE(buf.size() == 3U); | ||
| 187 | REQUIRE(buf.capacity() == payload.size()); | ||
| 188 | |||
| 189 | const auto buf_span = std::span<u8>(buf); | ||
| 190 | REQUIRE(buf_span.size() == buf.size()); | ||
| 191 | |||
| 192 | for (size_t i = 0; i < buf_span.size(); ++i) { | ||
| 193 | const auto new_value = static_cast<u8>(i + 1U); | ||
| 194 | // Writes to a span of the scratch buffer will propogate to the buffer itself | ||
| 195 | buf_span[i] = new_value; | ||
| 196 | REQUIRE(buf[i] == new_value); | ||
| 197 | } | ||
| 198 | } | ||
| 199 | |||
| 200 | } // namespace Common | ||
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 158360830..f1c60d1f3 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h | |||
| @@ -20,6 +20,7 @@ | |||
| 20 | #include "common/lru_cache.h" | 20 | #include "common/lru_cache.h" |
| 21 | #include "common/microprofile.h" | 21 | #include "common/microprofile.h" |
| 22 | #include "common/polyfill_ranges.h" | 22 | #include "common/polyfill_ranges.h" |
| 23 | #include "common/scratch_buffer.h" | ||
| 23 | #include "common/settings.h" | 24 | #include "common/settings.h" |
| 24 | #include "core/memory.h" | 25 | #include "core/memory.h" |
| 25 | #include "video_core/buffer_cache/buffer_base.h" | 26 | #include "video_core/buffer_cache/buffer_base.h" |
| @@ -422,8 +423,7 @@ private: | |||
| 422 | IntervalSet common_ranges; | 423 | IntervalSet common_ranges; |
| 423 | std::deque<IntervalSet> committed_ranges; | 424 | std::deque<IntervalSet> committed_ranges; |
| 424 | 425 | ||
| 425 | size_t immediate_buffer_capacity = 0; | 426 | Common::ScratchBuffer<u8> immediate_buffer_alloc; |
| 426 | std::unique_ptr<u8[]> immediate_buffer_alloc; | ||
| 427 | 427 | ||
| 428 | struct LRUItemParams { | 428 | struct LRUItemParams { |
| 429 | using ObjectType = BufferId; | 429 | using ObjectType = BufferId; |
| @@ -1927,11 +1927,8 @@ std::span<const u8> BufferCache<P>::ImmediateBufferWithData(VAddr cpu_addr, size | |||
| 1927 | 1927 | ||
| 1928 | template <class P> | 1928 | template <class P> |
| 1929 | std::span<u8> BufferCache<P>::ImmediateBuffer(size_t wanted_capacity) { | 1929 | std::span<u8> BufferCache<P>::ImmediateBuffer(size_t wanted_capacity) { |
| 1930 | if (wanted_capacity > immediate_buffer_capacity) { | 1930 | immediate_buffer_alloc.resize_destructive(wanted_capacity); |
| 1931 | immediate_buffer_capacity = wanted_capacity; | 1931 | return std::span<u8>(immediate_buffer_alloc.data(), wanted_capacity); |
| 1932 | immediate_buffer_alloc = std::make_unique<u8[]>(wanted_capacity); | ||
| 1933 | } | ||
| 1934 | return std::span<u8>(immediate_buffer_alloc.get(), wanted_capacity); | ||
| 1935 | } | 1932 | } |
| 1936 | 1933 | ||
| 1937 | template <class P> | 1934 | template <class P> |
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp index 9835e3ac1..322de2606 100644 --- a/src/video_core/dma_pusher.cpp +++ b/src/video_core/dma_pusher.cpp | |||
| @@ -56,7 +56,7 @@ bool DmaPusher::Step() { | |||
| 56 | 56 | ||
| 57 | if (command_list.prefetch_command_list.size()) { | 57 | if (command_list.prefetch_command_list.size()) { |
| 58 | // Prefetched command list from nvdrv, used for things like synchronization | 58 | // Prefetched command list from nvdrv, used for things like synchronization |
| 59 | command_headers = std::move(command_list.prefetch_command_list); | 59 | ProcessCommands(command_list.prefetch_command_list); |
| 60 | dma_pushbuffer.pop(); | 60 | dma_pushbuffer.pop(); |
| 61 | } else { | 61 | } else { |
| 62 | const CommandListHeader command_list_header{ | 62 | const CommandListHeader command_list_header{ |
| @@ -74,7 +74,7 @@ bool DmaPusher::Step() { | |||
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | // Push buffer non-empty, read a word | 76 | // Push buffer non-empty, read a word |
| 77 | command_headers.resize(command_list_header.size); | 77 | command_headers.resize_destructive(command_list_header.size); |
| 78 | if (Settings::IsGPULevelHigh()) { | 78 | if (Settings::IsGPULevelHigh()) { |
| 79 | memory_manager.ReadBlock(dma_get, command_headers.data(), | 79 | memory_manager.ReadBlock(dma_get, command_headers.data(), |
| 80 | command_list_header.size * sizeof(u32)); | 80 | command_list_header.size * sizeof(u32)); |
| @@ -82,16 +82,21 @@ bool DmaPusher::Step() { | |||
| 82 | memory_manager.ReadBlockUnsafe(dma_get, command_headers.data(), | 82 | memory_manager.ReadBlockUnsafe(dma_get, command_headers.data(), |
| 83 | command_list_header.size * sizeof(u32)); | 83 | command_list_header.size * sizeof(u32)); |
| 84 | } | 84 | } |
| 85 | ProcessCommands(command_headers); | ||
| 85 | } | 86 | } |
| 86 | for (std::size_t index = 0; index < command_headers.size();) { | 87 | |
| 87 | const CommandHeader& command_header = command_headers[index]; | 88 | return true; |
| 89 | } | ||
| 90 | |||
| 91 | void DmaPusher::ProcessCommands(std::span<const CommandHeader> commands) { | ||
| 92 | for (std::size_t index = 0; index < commands.size();) { | ||
| 93 | const CommandHeader& command_header = commands[index]; | ||
| 88 | 94 | ||
| 89 | if (dma_state.method_count) { | 95 | if (dma_state.method_count) { |
| 90 | // Data word of methods command | 96 | // Data word of methods command |
| 91 | if (dma_state.non_incrementing) { | 97 | if (dma_state.non_incrementing) { |
| 92 | const u32 max_write = static_cast<u32>( | 98 | const u32 max_write = static_cast<u32>( |
| 93 | std::min<std::size_t>(index + dma_state.method_count, command_headers.size()) - | 99 | std::min<std::size_t>(index + dma_state.method_count, commands.size()) - index); |
| 94 | index); | ||
| 95 | CallMultiMethod(&command_header.argument, max_write); | 100 | CallMultiMethod(&command_header.argument, max_write); |
| 96 | dma_state.method_count -= max_write; | 101 | dma_state.method_count -= max_write; |
| 97 | dma_state.is_last_call = true; | 102 | dma_state.is_last_call = true; |
| @@ -142,8 +147,6 @@ bool DmaPusher::Step() { | |||
| 142 | } | 147 | } |
| 143 | index++; | 148 | index++; |
| 144 | } | 149 | } |
| 145 | |||
| 146 | return true; | ||
| 147 | } | 150 | } |
| 148 | 151 | ||
| 149 | void DmaPusher::SetState(const CommandHeader& command_header) { | 152 | void DmaPusher::SetState(const CommandHeader& command_header) { |
diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h index 938f0f11c..6f00de937 100644 --- a/src/video_core/dma_pusher.h +++ b/src/video_core/dma_pusher.h | |||
| @@ -4,11 +4,13 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <array> | 6 | #include <array> |
| 7 | #include <span> | ||
| 7 | #include <vector> | 8 | #include <vector> |
| 8 | #include <queue> | 9 | #include <queue> |
| 9 | 10 | ||
| 10 | #include "common/bit_field.h" | 11 | #include "common/bit_field.h" |
| 11 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 13 | #include "common/scratch_buffer.h" | ||
| 12 | #include "video_core/engines/engine_interface.h" | 14 | #include "video_core/engines/engine_interface.h" |
| 13 | #include "video_core/engines/puller.h" | 15 | #include "video_core/engines/puller.h" |
| 14 | 16 | ||
| @@ -136,13 +138,15 @@ private: | |||
| 136 | static constexpr u32 non_puller_methods = 0x40; | 138 | static constexpr u32 non_puller_methods = 0x40; |
| 137 | static constexpr u32 max_subchannels = 8; | 139 | static constexpr u32 max_subchannels = 8; |
| 138 | bool Step(); | 140 | bool Step(); |
| 141 | void ProcessCommands(std::span<const CommandHeader> commands); | ||
| 139 | 142 | ||
| 140 | void SetState(const CommandHeader& command_header); | 143 | void SetState(const CommandHeader& command_header); |
| 141 | 144 | ||
| 142 | void CallMethod(u32 argument) const; | 145 | void CallMethod(u32 argument) const; |
| 143 | void CallMultiMethod(const u32* base_start, u32 num_methods) const; | 146 | void CallMultiMethod(const u32* base_start, u32 num_methods) const; |
| 144 | 147 | ||
| 145 | std::vector<CommandHeader> command_headers; ///< Buffer for list of commands fetched at once | 148 | Common::ScratchBuffer<CommandHeader> |
| 149 | command_headers; ///< Buffer for list of commands fetched at once | ||
| 146 | 150 | ||
| 147 | std::queue<CommandList> dma_pushbuffer; ///< Queue of command lists to be processed | 151 | std::queue<CommandList> dma_pushbuffer; ///< Queue of command lists to be processed |
| 148 | std::size_t dma_pushbuffer_subindex{}; ///< Index within a command list within the pushbuffer | 152 | std::size_t dma_pushbuffer_subindex{}; ///< Index within a command list within the pushbuffer |
| @@ -159,7 +163,7 @@ private: | |||
| 159 | DmaState dma_state{}; | 163 | DmaState dma_state{}; |
| 160 | bool dma_increment_once{}; | 164 | bool dma_increment_once{}; |
| 161 | 165 | ||
| 162 | bool ib_enable{true}; ///< IB mode enabled | 166 | const bool ib_enable{true}; ///< IB mode enabled |
| 163 | 167 | ||
| 164 | std::array<Engines::EngineInterface*, max_subchannels> subchannels{}; | 168 | std::array<Engines::EngineInterface*, max_subchannels> subchannels{}; |
| 165 | 169 | ||
diff --git a/src/video_core/engines/draw_manager.cpp b/src/video_core/engines/draw_manager.cpp index b213c374f..3a78421f6 100644 --- a/src/video_core/engines/draw_manager.cpp +++ b/src/video_core/engines/draw_manager.cpp | |||
| @@ -46,21 +46,26 @@ void DrawManager::ProcessMethodCall(u32 method, u32 argument) { | |||
| 46 | SetInlineIndexBuffer(regs.inline_index_4x8.index2); | 46 | SetInlineIndexBuffer(regs.inline_index_4x8.index2); |
| 47 | SetInlineIndexBuffer(regs.inline_index_4x8.index3); | 47 | SetInlineIndexBuffer(regs.inline_index_4x8.index3); |
| 48 | break; | 48 | break; |
| 49 | case MAXWELL3D_REG_INDEX(topology_override): | 49 | case MAXWELL3D_REG_INDEX(vertex_array_instance_first): |
| 50 | use_topology_override = true; | 50 | case MAXWELL3D_REG_INDEX(vertex_array_instance_subsequent): { |
| 51 | LOG_WARNING(HW_GPU, "(STUBBED) called"); | ||
| 51 | break; | 52 | break; |
| 53 | } | ||
| 52 | default: | 54 | default: |
| 53 | break; | 55 | break; |
| 54 | } | 56 | } |
| 55 | } | 57 | } |
| 56 | 58 | ||
| 57 | void DrawManager::Clear(u32 layer_count) { | 59 | void DrawManager::Clear(u32 layer_count) { |
| 58 | maxwell3d->rasterizer->Clear(layer_count); | 60 | if (maxwell3d->ShouldExecute()) { |
| 61 | maxwell3d->rasterizer->Clear(layer_count); | ||
| 62 | } | ||
| 59 | } | 63 | } |
| 60 | 64 | ||
| 61 | void DrawManager::DrawDeferred() { | 65 | void DrawManager::DrawDeferred() { |
| 62 | if (draw_state.draw_mode != DrawMode::Instance || draw_state.instance_count == 0) | 66 | if (draw_state.draw_mode != DrawMode::Instance || draw_state.instance_count == 0) { |
| 63 | return; | 67 | return; |
| 68 | } | ||
| 64 | DrawEnd(draw_state.instance_count + 1, true); | 69 | DrawEnd(draw_state.instance_count + 1, true); |
| 65 | draw_state.instance_count = 0; | 70 | draw_state.instance_count = 0; |
| 66 | } | 71 | } |
| @@ -115,8 +120,9 @@ void DrawManager::DrawEnd(u32 instance_count, bool force_draw) { | |||
| 115 | const auto& regs{maxwell3d->regs}; | 120 | const auto& regs{maxwell3d->regs}; |
| 116 | switch (draw_state.draw_mode) { | 121 | switch (draw_state.draw_mode) { |
| 117 | case DrawMode::Instance: | 122 | case DrawMode::Instance: |
| 118 | if (!force_draw) | 123 | if (!force_draw) { |
| 119 | break; | 124 | break; |
| 125 | } | ||
| 120 | [[fallthrough]]; | 126 | [[fallthrough]]; |
| 121 | case DrawMode::General: | 127 | case DrawMode::General: |
| 122 | draw_state.base_instance = regs.global_base_instance_index; | 128 | draw_state.base_instance = regs.global_base_instance_index; |
| @@ -156,25 +162,28 @@ void DrawManager::DrawIndexSmall(u32 argument) { | |||
| 156 | ProcessDraw(true, 1); | 162 | ProcessDraw(true, 1); |
| 157 | } | 163 | } |
| 158 | 164 | ||
| 159 | void DrawManager::ProcessTopologyOverride() { | 165 | void DrawManager::UpdateTopology() { |
| 160 | if (!use_topology_override) | ||
| 161 | return; | ||
| 162 | |||
| 163 | const auto& regs{maxwell3d->regs}; | 166 | const auto& regs{maxwell3d->regs}; |
| 164 | switch (regs.topology_override) { | 167 | switch (regs.primitive_topology_control) { |
| 165 | case PrimitiveTopologyOverride::None: | 168 | case PrimitiveTopologyControl::UseInBeginMethods: |
| 166 | break; | ||
| 167 | case PrimitiveTopologyOverride::Points: | ||
| 168 | draw_state.topology = PrimitiveTopology::Points; | ||
| 169 | break; | ||
| 170 | case PrimitiveTopologyOverride::Lines: | ||
| 171 | draw_state.topology = PrimitiveTopology::Lines; | ||
| 172 | break; | ||
| 173 | case PrimitiveTopologyOverride::LineStrip: | ||
| 174 | draw_state.topology = PrimitiveTopology::LineStrip; | ||
| 175 | break; | 169 | break; |
| 176 | default: | 170 | case PrimitiveTopologyControl::UseSeparateState: |
| 177 | draw_state.topology = static_cast<PrimitiveTopology>(regs.topology_override); | 171 | switch (regs.topology_override) { |
| 172 | case PrimitiveTopologyOverride::None: | ||
| 173 | break; | ||
| 174 | case PrimitiveTopologyOverride::Points: | ||
| 175 | draw_state.topology = PrimitiveTopology::Points; | ||
| 176 | break; | ||
| 177 | case PrimitiveTopologyOverride::Lines: | ||
| 178 | draw_state.topology = PrimitiveTopology::Lines; | ||
| 179 | break; | ||
| 180 | case PrimitiveTopologyOverride::LineStrip: | ||
| 181 | draw_state.topology = PrimitiveTopology::LineStrip; | ||
| 182 | break; | ||
| 183 | default: | ||
| 184 | draw_state.topology = static_cast<PrimitiveTopology>(regs.topology_override); | ||
| 185 | break; | ||
| 186 | } | ||
| 178 | break; | 187 | break; |
| 179 | } | 188 | } |
| 180 | } | 189 | } |
| @@ -183,9 +192,10 @@ void DrawManager::ProcessDraw(bool draw_indexed, u32 instance_count) { | |||
| 183 | LOG_TRACE(HW_GPU, "called, topology={}, count={}", draw_state.topology, | 192 | LOG_TRACE(HW_GPU, "called, topology={}, count={}", draw_state.topology, |
| 184 | draw_indexed ? draw_state.index_buffer.count : draw_state.vertex_buffer.count); | 193 | draw_indexed ? draw_state.index_buffer.count : draw_state.vertex_buffer.count); |
| 185 | 194 | ||
| 186 | ProcessTopologyOverride(); | 195 | UpdateTopology(); |
| 187 | 196 | ||
| 188 | if (maxwell3d->ShouldExecute()) | 197 | if (maxwell3d->ShouldExecute()) { |
| 189 | maxwell3d->rasterizer->Draw(draw_indexed, instance_count); | 198 | maxwell3d->rasterizer->Draw(draw_indexed, instance_count); |
| 199 | } | ||
| 190 | } | 200 | } |
| 191 | } // namespace Tegra::Engines | 201 | } // namespace Tegra::Engines |
diff --git a/src/video_core/engines/draw_manager.h b/src/video_core/engines/draw_manager.h index 4f67027ca..0e6930a9c 100644 --- a/src/video_core/engines/draw_manager.h +++ b/src/video_core/engines/draw_manager.h | |||
| @@ -10,6 +10,7 @@ class RasterizerInterface; | |||
| 10 | } | 10 | } |
| 11 | 11 | ||
| 12 | namespace Tegra::Engines { | 12 | namespace Tegra::Engines { |
| 13 | using PrimitiveTopologyControl = Maxwell3D::Regs::PrimitiveTopologyControl; | ||
| 13 | using PrimitiveTopology = Maxwell3D::Regs::PrimitiveTopology; | 14 | using PrimitiveTopology = Maxwell3D::Regs::PrimitiveTopology; |
| 14 | using PrimitiveTopologyOverride = Maxwell3D::Regs::PrimitiveTopologyOverride; | 15 | using PrimitiveTopologyOverride = Maxwell3D::Regs::PrimitiveTopologyOverride; |
| 15 | using IndexBuffer = Maxwell3D::Regs::IndexBuffer; | 16 | using IndexBuffer = Maxwell3D::Regs::IndexBuffer; |
| @@ -58,12 +59,11 @@ private: | |||
| 58 | 59 | ||
| 59 | void DrawIndexSmall(u32 argument); | 60 | void DrawIndexSmall(u32 argument); |
| 60 | 61 | ||
| 61 | void ProcessTopologyOverride(); | 62 | void UpdateTopology(); |
| 62 | 63 | ||
| 63 | void ProcessDraw(bool draw_indexed, u32 instance_count); | 64 | void ProcessDraw(bool draw_indexed, u32 instance_count); |
| 64 | 65 | ||
| 65 | Maxwell3D* maxwell3d{}; | 66 | Maxwell3D* maxwell3d{}; |
| 66 | State draw_state{}; | 67 | State draw_state{}; |
| 67 | bool use_topology_override{}; | ||
| 68 | }; | 68 | }; |
| 69 | } // namespace Tegra::Engines | 69 | } // namespace Tegra::Engines |
diff --git a/src/video_core/engines/engine_upload.cpp b/src/video_core/engines/engine_upload.cpp index e4f8331ab..cea1dd8b0 100644 --- a/src/video_core/engines/engine_upload.cpp +++ b/src/video_core/engines/engine_upload.cpp | |||
| @@ -24,7 +24,7 @@ void State::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) { | |||
| 24 | void State::ProcessExec(const bool is_linear_) { | 24 | void State::ProcessExec(const bool is_linear_) { |
| 25 | write_offset = 0; | 25 | write_offset = 0; |
| 26 | copy_size = regs.line_length_in * regs.line_count; | 26 | copy_size = regs.line_length_in * regs.line_count; |
| 27 | inner_buffer.resize(copy_size); | 27 | inner_buffer.resize_destructive(copy_size); |
| 28 | is_linear = is_linear_; | 28 | is_linear = is_linear_; |
| 29 | } | 29 | } |
| 30 | 30 | ||
| @@ -70,7 +70,7 @@ void State::ProcessData(std::span<const u8> read_buffer) { | |||
| 70 | const std::size_t dst_size = Tegra::Texture::CalculateSize( | 70 | const std::size_t dst_size = Tegra::Texture::CalculateSize( |
| 71 | true, bytes_per_pixel, width, regs.dest.height, regs.dest.depth, | 71 | true, bytes_per_pixel, width, regs.dest.height, regs.dest.depth, |
| 72 | regs.dest.BlockHeight(), regs.dest.BlockDepth()); | 72 | regs.dest.BlockHeight(), regs.dest.BlockDepth()); |
| 73 | tmp_buffer.resize(dst_size); | 73 | tmp_buffer.resize_destructive(dst_size); |
| 74 | memory_manager.ReadBlock(address, tmp_buffer.data(), dst_size); | 74 | memory_manager.ReadBlock(address, tmp_buffer.data(), dst_size); |
| 75 | Tegra::Texture::SwizzleSubrect(tmp_buffer, read_buffer, bytes_per_pixel, width, | 75 | Tegra::Texture::SwizzleSubrect(tmp_buffer, read_buffer, bytes_per_pixel, width, |
| 76 | regs.dest.height, regs.dest.depth, x_offset, regs.dest.y, | 76 | regs.dest.height, regs.dest.depth, x_offset, regs.dest.y, |
diff --git a/src/video_core/engines/engine_upload.h b/src/video_core/engines/engine_upload.h index 94fafd9dc..7242d2529 100644 --- a/src/video_core/engines/engine_upload.h +++ b/src/video_core/engines/engine_upload.h | |||
| @@ -4,9 +4,10 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <span> | 6 | #include <span> |
| 7 | #include <vector> | 7 | |
| 8 | #include "common/bit_field.h" | 8 | #include "common/bit_field.h" |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "common/scratch_buffer.h" | ||
| 10 | 11 | ||
| 11 | namespace Tegra { | 12 | namespace Tegra { |
| 12 | class MemoryManager; | 13 | class MemoryManager; |
| @@ -73,8 +74,8 @@ private: | |||
| 73 | 74 | ||
| 74 | u32 write_offset = 0; | 75 | u32 write_offset = 0; |
| 75 | u32 copy_size = 0; | 76 | u32 copy_size = 0; |
| 76 | std::vector<u8> inner_buffer; | 77 | Common::ScratchBuffer<u8> inner_buffer; |
| 77 | std::vector<u8> tmp_buffer; | 78 | Common::ScratchBuffer<u8> tmp_buffer; |
| 78 | bool is_linear = false; | 79 | bool is_linear = false; |
| 79 | Registers& regs; | 80 | Registers& regs; |
| 80 | MemoryManager& memory_manager; | 81 | MemoryManager& memory_manager; |
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index a189e60ae..f73d7bf0f 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp | |||
| @@ -184,12 +184,8 @@ void MaxwellDMA::CopyBlockLinearToPitch() { | |||
| 184 | const size_t src_size = | 184 | const size_t src_size = |
| 185 | CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth); | 185 | CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth); |
| 186 | 186 | ||
| 187 | if (read_buffer.size() < src_size) { | 187 | read_buffer.resize_destructive(src_size); |
| 188 | read_buffer.resize(src_size); | 188 | write_buffer.resize_destructive(dst_size); |
| 189 | } | ||
| 190 | if (write_buffer.size() < dst_size) { | ||
| 191 | write_buffer.resize(dst_size); | ||
| 192 | } | ||
| 193 | 189 | ||
| 194 | memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); | 190 | memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); |
| 195 | memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size); | 191 | memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size); |
| @@ -235,12 +231,8 @@ void MaxwellDMA::CopyPitchToBlockLinear() { | |||
| 235 | CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth); | 231 | CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth); |
| 236 | const size_t src_size = static_cast<size_t>(regs.pitch_in) * regs.line_count; | 232 | const size_t src_size = static_cast<size_t>(regs.pitch_in) * regs.line_count; |
| 237 | 233 | ||
| 238 | if (read_buffer.size() < src_size) { | 234 | read_buffer.resize_destructive(src_size); |
| 239 | read_buffer.resize(src_size); | 235 | write_buffer.resize_destructive(dst_size); |
| 240 | } | ||
| 241 | if (write_buffer.size() < dst_size) { | ||
| 242 | write_buffer.resize(dst_size); | ||
| 243 | } | ||
| 244 | 236 | ||
| 245 | memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); | 237 | memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); |
| 246 | if (Settings::IsGPULevelExtreme()) { | 238 | if (Settings::IsGPULevelExtreme()) { |
| @@ -269,12 +261,8 @@ void MaxwellDMA::FastCopyBlockLinearToPitch() { | |||
| 269 | pos_x = pos_x % x_in_gob; | 261 | pos_x = pos_x % x_in_gob; |
| 270 | pos_y = pos_y % 8; | 262 | pos_y = pos_y % 8; |
| 271 | 263 | ||
| 272 | if (read_buffer.size() < src_size) { | 264 | read_buffer.resize_destructive(src_size); |
| 273 | read_buffer.resize(src_size); | 265 | write_buffer.resize_destructive(dst_size); |
| 274 | } | ||
| 275 | if (write_buffer.size() < dst_size) { | ||
| 276 | write_buffer.resize(dst_size); | ||
| 277 | } | ||
| 278 | 266 | ||
| 279 | if (Settings::IsGPULevelExtreme()) { | 267 | if (Settings::IsGPULevelExtreme()) { |
| 280 | memory_manager.ReadBlock(regs.offset_in + offset, read_buffer.data(), src_size); | 268 | memory_manager.ReadBlock(regs.offset_in + offset, read_buffer.data(), src_size); |
| @@ -333,14 +321,10 @@ void MaxwellDMA::CopyBlockLinearToBlockLinear() { | |||
| 333 | const u32 pitch = x_elements * bytes_per_pixel; | 321 | const u32 pitch = x_elements * bytes_per_pixel; |
| 334 | const size_t mid_buffer_size = pitch * regs.line_count; | 322 | const size_t mid_buffer_size = pitch * regs.line_count; |
| 335 | 323 | ||
| 336 | if (read_buffer.size() < src_size) { | 324 | read_buffer.resize_destructive(src_size); |
| 337 | read_buffer.resize(src_size); | 325 | write_buffer.resize_destructive(dst_size); |
| 338 | } | ||
| 339 | if (write_buffer.size() < dst_size) { | ||
| 340 | write_buffer.resize(dst_size); | ||
| 341 | } | ||
| 342 | 326 | ||
| 343 | intermediate_buffer.resize(mid_buffer_size); | 327 | intermediate_buffer.resize_destructive(mid_buffer_size); |
| 344 | 328 | ||
| 345 | memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); | 329 | memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); |
| 346 | memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size); | 330 | memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size); |
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h index d40d3d302..c88191a61 100644 --- a/src/video_core/engines/maxwell_dma.h +++ b/src/video_core/engines/maxwell_dma.h | |||
| @@ -6,8 +6,10 @@ | |||
| 6 | #include <array> | 6 | #include <array> |
| 7 | #include <cstddef> | 7 | #include <cstddef> |
| 8 | #include <vector> | 8 | #include <vector> |
| 9 | |||
| 9 | #include "common/bit_field.h" | 10 | #include "common/bit_field.h" |
| 10 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "common/scratch_buffer.h" | ||
| 11 | #include "video_core/engines/engine_interface.h" | 13 | #include "video_core/engines/engine_interface.h" |
| 12 | 14 | ||
| 13 | namespace Core { | 15 | namespace Core { |
| @@ -234,9 +236,9 @@ private: | |||
| 234 | MemoryManager& memory_manager; | 236 | MemoryManager& memory_manager; |
| 235 | VideoCore::RasterizerInterface* rasterizer = nullptr; | 237 | VideoCore::RasterizerInterface* rasterizer = nullptr; |
| 236 | 238 | ||
| 237 | std::vector<u8> read_buffer; | 239 | Common::ScratchBuffer<u8> read_buffer; |
| 238 | std::vector<u8> write_buffer; | 240 | Common::ScratchBuffer<u8> write_buffer; |
| 239 | std::vector<u8> intermediate_buffer; | 241 | Common::ScratchBuffer<u8> intermediate_buffer; |
| 240 | 242 | ||
| 241 | static constexpr std::size_t NUM_REGS = 0x800; | 243 | static constexpr std::size_t NUM_REGS = 0x800; |
| 242 | struct Regs { | 244 | struct Regs { |
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 28b38273e..c6d54be63 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -223,8 +223,6 @@ struct GPU::Impl { | |||
| 223 | /// core timing events. | 223 | /// core timing events. |
| 224 | void Start() { | 224 | void Start() { |
| 225 | gpu_thread.StartThread(*renderer, renderer->Context(), *scheduler); | 225 | gpu_thread.StartThread(*renderer, renderer->Context(), *scheduler); |
| 226 | cpu_context = renderer->GetRenderWindow().CreateSharedContext(); | ||
| 227 | cpu_context->MakeCurrent(); | ||
| 228 | } | 226 | } |
| 229 | 227 | ||
| 230 | void NotifyShutdown() { | 228 | void NotifyShutdown() { |
| @@ -235,6 +233,9 @@ struct GPU::Impl { | |||
| 235 | 233 | ||
| 236 | /// Obtain the CPU Context | 234 | /// Obtain the CPU Context |
| 237 | void ObtainContext() { | 235 | void ObtainContext() { |
| 236 | if (!cpu_context) { | ||
| 237 | cpu_context = renderer->GetRenderWindow().CreateSharedContext(); | ||
| 238 | } | ||
| 238 | cpu_context->MakeCurrent(); | 239 | cpu_context->MakeCurrent(); |
| 239 | } | 240 | } |
| 240 | 241 | ||
diff --git a/src/video_core/host1x/vic.cpp b/src/video_core/host1x/vic.cpp index ac0b7d20e..36a04e4e0 100644 --- a/src/video_core/host1x/vic.cpp +++ b/src/video_core/host1x/vic.cpp | |||
| @@ -155,7 +155,7 @@ void Vic::WriteRGBFrame(const AVFrame* frame, const VicConfig& config) { | |||
| 155 | // swizzle pitch linear to block linear | 155 | // swizzle pitch linear to block linear |
| 156 | const u32 block_height = static_cast<u32>(config.block_linear_height_log2); | 156 | const u32 block_height = static_cast<u32>(config.block_linear_height_log2); |
| 157 | const auto size = Texture::CalculateSize(true, 4, width, height, 1, block_height, 0); | 157 | const auto size = Texture::CalculateSize(true, 4, width, height, 1, block_height, 0); |
| 158 | luma_buffer.resize(size); | 158 | luma_buffer.resize_destructive(size); |
| 159 | std::span<const u8> frame_buff(converted_frame_buf_addr, 4 * width * height); | 159 | std::span<const u8> frame_buff(converted_frame_buf_addr, 4 * width * height); |
| 160 | Texture::SwizzleSubrect(luma_buffer, frame_buff, 4, width, height, 1, 0, 0, width, height, | 160 | Texture::SwizzleSubrect(luma_buffer, frame_buff, 4, width, height, 1, 0, 0, width, height, |
| 161 | block_height, 0, width * 4); | 161 | block_height, 0, width * 4); |
| @@ -181,8 +181,8 @@ void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) { | |||
| 181 | 181 | ||
| 182 | const auto stride = static_cast<size_t>(frame->linesize[0]); | 182 | const auto stride = static_cast<size_t>(frame->linesize[0]); |
| 183 | 183 | ||
| 184 | luma_buffer.resize(aligned_width * surface_height); | 184 | luma_buffer.resize_destructive(aligned_width * surface_height); |
| 185 | chroma_buffer.resize(aligned_width * surface_height / 2); | 185 | chroma_buffer.resize_destructive(aligned_width * surface_height / 2); |
| 186 | 186 | ||
| 187 | // Populate luma buffer | 187 | // Populate luma buffer |
| 188 | const u8* luma_src = frame->data[0]; | 188 | const u8* luma_src = frame->data[0]; |
diff --git a/src/video_core/host1x/vic.h b/src/video_core/host1x/vic.h index 2b78786e8..3d9753047 100644 --- a/src/video_core/host1x/vic.h +++ b/src/video_core/host1x/vic.h | |||
| @@ -4,8 +4,9 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <memory> | 6 | #include <memory> |
| 7 | #include <vector> | 7 | |
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "common/scratch_buffer.h" | ||
| 9 | 10 | ||
| 10 | struct SwsContext; | 11 | struct SwsContext; |
| 11 | 12 | ||
| @@ -49,8 +50,8 @@ private: | |||
| 49 | /// size does not change during a stream | 50 | /// size does not change during a stream |
| 50 | using AVMallocPtr = std::unique_ptr<u8, decltype(&av_free)>; | 51 | using AVMallocPtr = std::unique_ptr<u8, decltype(&av_free)>; |
| 51 | AVMallocPtr converted_frame_buffer; | 52 | AVMallocPtr converted_frame_buffer; |
| 52 | std::vector<u8> luma_buffer; | 53 | Common::ScratchBuffer<u8> luma_buffer; |
| 53 | std::vector<u8> chroma_buffer; | 54 | Common::ScratchBuffer<u8> chroma_buffer; |
| 54 | 55 | ||
| 55 | GPUVAddr config_struct_address{}; | 56 | GPUVAddr config_struct_address{}; |
| 56 | GPUVAddr output_surface_luma_address{}; | 57 | GPUVAddr output_surface_luma_address{}; |
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index e2e3dac34..cee5c3247 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp | |||
| @@ -112,7 +112,7 @@ bool IsASTCSupported() { | |||
| 112 | } | 112 | } |
| 113 | } // Anonymous namespace | 113 | } // Anonymous namespace |
| 114 | 114 | ||
| 115 | Device::Device() { | 115 | Device::Device(Core::Frontend::EmuWindow& emu_window) { |
| 116 | if (!GLAD_GL_VERSION_4_6) { | 116 | if (!GLAD_GL_VERSION_4_6) { |
| 117 | LOG_ERROR(Render_OpenGL, "OpenGL 4.6 is not available"); | 117 | LOG_ERROR(Render_OpenGL, "OpenGL 4.6 is not available"); |
| 118 | throw std::runtime_error{"Insufficient version"}; | 118 | throw std::runtime_error{"Insufficient version"}; |
| @@ -126,9 +126,9 @@ Device::Device() { | |||
| 126 | const bool is_intel = vendor_name == "Intel"; | 126 | const bool is_intel = vendor_name == "Intel"; |
| 127 | 127 | ||
| 128 | #ifdef __unix__ | 128 | #ifdef __unix__ |
| 129 | const bool is_linux = true; | 129 | constexpr bool is_linux = true; |
| 130 | #else | 130 | #else |
| 131 | const bool is_linux = false; | 131 | constexpr bool is_linux = false; |
| 132 | #endif | 132 | #endif |
| 133 | 133 | ||
| 134 | bool disable_fast_buffer_sub_data = false; | 134 | bool disable_fast_buffer_sub_data = false; |
| @@ -193,9 +193,11 @@ Device::Device() { | |||
| 193 | } | 193 | } |
| 194 | } | 194 | } |
| 195 | 195 | ||
| 196 | strict_context_required = emu_window.StrictContextRequired(); | ||
| 196 | // Blocks AMD and Intel OpenGL drivers on Windows from using asynchronous shader compilation. | 197 | // Blocks AMD and Intel OpenGL drivers on Windows from using asynchronous shader compilation. |
| 198 | // Blocks EGL on Wayland from using asynchronous shader compilation. | ||
| 197 | use_asynchronous_shaders = Settings::values.use_asynchronous_shaders.GetValue() && | 199 | use_asynchronous_shaders = Settings::values.use_asynchronous_shaders.GetValue() && |
| 198 | !(is_amd || (is_intel && !is_linux)); | 200 | !(is_amd || (is_intel && !is_linux)) && !strict_context_required; |
| 199 | use_driver_cache = is_nvidia; | 201 | use_driver_cache = is_nvidia; |
| 200 | 202 | ||
| 201 | LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi); | 203 | LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi); |
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h index 5ef51ebcf..2a72d84be 100644 --- a/src/video_core/renderer_opengl/gl_device.h +++ b/src/video_core/renderer_opengl/gl_device.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | 5 | ||
| 6 | #include <cstddef> | 6 | #include <cstddef> |
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "core/frontend/emu_window.h" | ||
| 8 | #include "shader_recompiler/stage.h" | 9 | #include "shader_recompiler/stage.h" |
| 9 | 10 | ||
| 10 | namespace Settings { | 11 | namespace Settings { |
| @@ -15,7 +16,7 @@ namespace OpenGL { | |||
| 15 | 16 | ||
| 16 | class Device { | 17 | class Device { |
| 17 | public: | 18 | public: |
| 18 | explicit Device(); | 19 | explicit Device(Core::Frontend::EmuWindow& emu_window); |
| 19 | 20 | ||
| 20 | [[nodiscard]] std::string GetVendorName() const; | 21 | [[nodiscard]] std::string GetVendorName() const; |
| 21 | 22 | ||
| @@ -173,6 +174,10 @@ public: | |||
| 173 | return can_report_memory; | 174 | return can_report_memory; |
| 174 | } | 175 | } |
| 175 | 176 | ||
| 177 | bool StrictContextRequired() const { | ||
| 178 | return strict_context_required; | ||
| 179 | } | ||
| 180 | |||
| 176 | private: | 181 | private: |
| 177 | static bool TestVariableAoffi(); | 182 | static bool TestVariableAoffi(); |
| 178 | static bool TestPreciseBug(); | 183 | static bool TestPreciseBug(); |
| @@ -216,6 +221,7 @@ private: | |||
| 216 | bool has_cbuf_ftou_bug{}; | 221 | bool has_cbuf_ftou_bug{}; |
| 217 | bool has_bool_ref_bug{}; | 222 | bool has_bool_ref_bug{}; |
| 218 | bool can_report_memory{}; | 223 | bool can_report_memory{}; |
| 224 | bool strict_context_required{}; | ||
| 219 | 225 | ||
| 220 | std::string vendor_name; | 226 | std::string vendor_name; |
| 221 | }; | 227 | }; |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 64ed6f628..a44b8c454 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -138,9 +138,6 @@ void RasterizerOpenGL::LoadDiskResources(u64 title_id, std::stop_token stop_load | |||
| 138 | 138 | ||
| 139 | void RasterizerOpenGL::Clear(u32 layer_count) { | 139 | void RasterizerOpenGL::Clear(u32 layer_count) { |
| 140 | MICROPROFILE_SCOPE(OpenGL_Clears); | 140 | MICROPROFILE_SCOPE(OpenGL_Clears); |
| 141 | if (!maxwell3d->ShouldExecute()) { | ||
| 142 | return; | ||
| 143 | } | ||
| 144 | 141 | ||
| 145 | const auto& regs = maxwell3d->regs; | 142 | const auto& regs = maxwell3d->regs; |
| 146 | bool use_color{}; | 143 | bool use_color{}; |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index a59d0d24e..f8868a012 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp | |||
| @@ -174,6 +174,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo | |||
| 174 | texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, program_manager{program_manager_}, | 174 | texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, program_manager{program_manager_}, |
| 175 | state_tracker{state_tracker_}, shader_notify{shader_notify_}, | 175 | state_tracker{state_tracker_}, shader_notify{shader_notify_}, |
| 176 | use_asynchronous_shaders{device.UseAsynchronousShaders()}, | 176 | use_asynchronous_shaders{device.UseAsynchronousShaders()}, |
| 177 | strict_context_required{device.StrictContextRequired()}, | ||
| 177 | profile{ | 178 | profile{ |
| 178 | .supported_spirv = 0x00010000, | 179 | .supported_spirv = 0x00010000, |
| 179 | 180 | ||
| @@ -203,6 +204,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo | |||
| 203 | .support_int64_atomics = false, | 204 | .support_int64_atomics = false, |
| 204 | .support_derivative_control = device.HasDerivativeControl(), | 205 | .support_derivative_control = device.HasDerivativeControl(), |
| 205 | .support_geometry_shader_passthrough = device.HasGeometryShaderPassthrough(), | 206 | .support_geometry_shader_passthrough = device.HasGeometryShaderPassthrough(), |
| 207 | .support_native_ndc = true, | ||
| 206 | .support_gl_nv_gpu_shader_5 = device.HasNvGpuShader5(), | 208 | .support_gl_nv_gpu_shader_5 = device.HasNvGpuShader5(), |
| 207 | .support_gl_amd_gpu_shader_half_float = device.HasAmdShaderHalfFloat(), | 209 | .support_gl_amd_gpu_shader_half_float = device.HasAmdShaderHalfFloat(), |
| 208 | .support_gl_texture_shadow_lod = device.HasTextureShadowLod(), | 210 | .support_gl_texture_shadow_lod = device.HasTextureShadowLod(), |
| @@ -255,9 +257,14 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading, | |||
| 255 | } | 257 | } |
| 256 | shader_cache_filename = base_dir / "opengl.bin"; | 258 | shader_cache_filename = base_dir / "opengl.bin"; |
| 257 | 259 | ||
| 258 | if (!workers) { | 260 | if (!workers && !strict_context_required) { |
| 259 | workers = CreateWorkers(); | 261 | workers = CreateWorkers(); |
| 260 | } | 262 | } |
| 263 | std::optional<Context> strict_context; | ||
| 264 | if (strict_context_required) { | ||
| 265 | strict_context.emplace(emu_window); | ||
| 266 | } | ||
| 267 | |||
| 261 | struct { | 268 | struct { |
| 262 | std::mutex mutex; | 269 | std::mutex mutex; |
| 263 | size_t total{}; | 270 | size_t total{}; |
| @@ -265,44 +272,49 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading, | |||
| 265 | bool has_loaded{}; | 272 | bool has_loaded{}; |
| 266 | } state; | 273 | } state; |
| 267 | 274 | ||
| 275 | const auto queue_work{[&](Common::UniqueFunction<void, Context*>&& work) { | ||
| 276 | if (strict_context_required) { | ||
| 277 | work(&strict_context.value()); | ||
| 278 | } else { | ||
| 279 | workers->QueueWork(std::move(work)); | ||
| 280 | } | ||
| 281 | }}; | ||
| 268 | const auto load_compute{[&](std::ifstream& file, FileEnvironment env) { | 282 | const auto load_compute{[&](std::ifstream& file, FileEnvironment env) { |
| 269 | ComputePipelineKey key; | 283 | ComputePipelineKey key; |
| 270 | file.read(reinterpret_cast<char*>(&key), sizeof(key)); | 284 | file.read(reinterpret_cast<char*>(&key), sizeof(key)); |
| 271 | workers->QueueWork( | 285 | queue_work([this, key, env = std::move(env), &state, &callback](Context* ctx) mutable { |
| 272 | [this, key, env = std::move(env), &state, &callback](Context* ctx) mutable { | 286 | ctx->pools.ReleaseContents(); |
| 273 | ctx->pools.ReleaseContents(); | 287 | auto pipeline{CreateComputePipeline(ctx->pools, key, env)}; |
| 274 | auto pipeline{CreateComputePipeline(ctx->pools, key, env)}; | 288 | std::scoped_lock lock{state.mutex}; |
| 275 | std::scoped_lock lock{state.mutex}; | 289 | if (pipeline) { |
| 276 | if (pipeline) { | 290 | compute_cache.emplace(key, std::move(pipeline)); |
| 277 | compute_cache.emplace(key, std::move(pipeline)); | 291 | } |
| 278 | } | 292 | ++state.built; |
| 279 | ++state.built; | 293 | if (state.has_loaded) { |
| 280 | if (state.has_loaded) { | 294 | callback(VideoCore::LoadCallbackStage::Build, state.built, state.total); |
| 281 | callback(VideoCore::LoadCallbackStage::Build, state.built, state.total); | 295 | } |
| 282 | } | 296 | }); |
| 283 | }); | ||
| 284 | ++state.total; | 297 | ++state.total; |
| 285 | }}; | 298 | }}; |
| 286 | const auto load_graphics{[&](std::ifstream& file, std::vector<FileEnvironment> envs) { | 299 | const auto load_graphics{[&](std::ifstream& file, std::vector<FileEnvironment> envs) { |
| 287 | GraphicsPipelineKey key; | 300 | GraphicsPipelineKey key; |
| 288 | file.read(reinterpret_cast<char*>(&key), sizeof(key)); | 301 | file.read(reinterpret_cast<char*>(&key), sizeof(key)); |
| 289 | workers->QueueWork( | 302 | queue_work([this, key, envs = std::move(envs), &state, &callback](Context* ctx) mutable { |
| 290 | [this, key, envs = std::move(envs), &state, &callback](Context* ctx) mutable { | 303 | boost::container::static_vector<Shader::Environment*, 5> env_ptrs; |
| 291 | boost::container::static_vector<Shader::Environment*, 5> env_ptrs; | 304 | for (auto& env : envs) { |
| 292 | for (auto& env : envs) { | 305 | env_ptrs.push_back(&env); |
| 293 | env_ptrs.push_back(&env); | 306 | } |
| 294 | } | 307 | ctx->pools.ReleaseContents(); |
| 295 | ctx->pools.ReleaseContents(); | 308 | auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false)}; |
| 296 | auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false)}; | 309 | std::scoped_lock lock{state.mutex}; |
| 297 | std::scoped_lock lock{state.mutex}; | 310 | if (pipeline) { |
| 298 | if (pipeline) { | 311 | graphics_cache.emplace(key, std::move(pipeline)); |
| 299 | graphics_cache.emplace(key, std::move(pipeline)); | 312 | } |
| 300 | } | 313 | ++state.built; |
| 301 | ++state.built; | 314 | if (state.has_loaded) { |
| 302 | if (state.has_loaded) { | 315 | callback(VideoCore::LoadCallbackStage::Build, state.built, state.total); |
| 303 | callback(VideoCore::LoadCallbackStage::Build, state.built, state.total); | 316 | } |
| 304 | } | 317 | }); |
| 305 | }); | ||
| 306 | ++state.total; | 318 | ++state.total; |
| 307 | }}; | 319 | }}; |
| 308 | LoadPipelines(stop_loading, shader_cache_filename, CACHE_VERSION, load_compute, load_graphics); | 320 | LoadPipelines(stop_loading, shader_cache_filename, CACHE_VERSION, load_compute, load_graphics); |
| @@ -314,6 +326,9 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading, | |||
| 314 | state.has_loaded = true; | 326 | state.has_loaded = true; |
| 315 | lock.unlock(); | 327 | lock.unlock(); |
| 316 | 328 | ||
| 329 | if (strict_context_required) { | ||
| 330 | return; | ||
| 331 | } | ||
| 317 | workers->WaitForRequests(stop_loading); | 332 | workers->WaitForRequests(stop_loading); |
| 318 | if (!use_asynchronous_shaders) { | 333 | if (!use_asynchronous_shaders) { |
| 319 | workers.reset(); | 334 | workers.reset(); |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h index 53ffea904..f82420592 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_cache.h | |||
| @@ -69,6 +69,7 @@ private: | |||
| 69 | StateTracker& state_tracker; | 69 | StateTracker& state_tracker; |
| 70 | VideoCore::ShaderNotify& shader_notify; | 70 | VideoCore::ShaderNotify& shader_notify; |
| 71 | const bool use_asynchronous_shaders; | 71 | const bool use_asynchronous_shaders; |
| 72 | const bool strict_context_required; | ||
| 72 | 73 | ||
| 73 | GraphicsPipelineKey graphics_key{}; | 74 | GraphicsPipelineKey graphics_key{}; |
| 74 | GraphicsPipeline* current_pipeline{}; | 75 | GraphicsPipeline* current_pipeline{}; |
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 5b5e178ad..bc75680f0 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp | |||
| @@ -140,8 +140,8 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_, | |||
| 140 | Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_, | 140 | Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_, |
| 141 | std::unique_ptr<Core::Frontend::GraphicsContext> context_) | 141 | std::unique_ptr<Core::Frontend::GraphicsContext> context_) |
| 142 | : RendererBase{emu_window_, std::move(context_)}, telemetry_session{telemetry_session_}, | 142 | : RendererBase{emu_window_, std::move(context_)}, telemetry_session{telemetry_session_}, |
| 143 | emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, state_tracker{}, | 143 | emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, device{emu_window_}, |
| 144 | program_manager{device}, | 144 | state_tracker{}, program_manager{device}, |
| 145 | rasterizer(emu_window, gpu, cpu_memory, device, screen_info, program_manager, state_tracker) { | 145 | rasterizer(emu_window, gpu, cpu_memory, device, screen_info, program_manager, state_tracker) { |
| 146 | if (Settings::values.renderer_debug && GLAD_GL_KHR_debug) { | 146 | if (Settings::values.renderer_debug && GLAD_GL_KHR_debug) { |
| 147 | glEnable(GL_DEBUG_OUTPUT); | 147 | glEnable(GL_DEBUG_OUTPUT); |
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 18be54729..f502a7d09 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp | |||
| @@ -139,23 +139,25 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | |||
| 139 | RenderScreenshot(*framebuffer, use_accelerated); | 139 | RenderScreenshot(*framebuffer, use_accelerated); |
| 140 | 140 | ||
| 141 | bool has_been_recreated = false; | 141 | bool has_been_recreated = false; |
| 142 | const auto recreate_swapchain = [&] { | 142 | const auto recreate_swapchain = [&](u32 width, u32 height) { |
| 143 | if (!has_been_recreated) { | 143 | if (!has_been_recreated) { |
| 144 | has_been_recreated = true; | 144 | has_been_recreated = true; |
| 145 | scheduler.Finish(); | 145 | scheduler.Finish(); |
| 146 | } | 146 | } |
| 147 | const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout(); | 147 | swapchain.Create(width, height, is_srgb); |
| 148 | swapchain.Create(layout.width, layout.height, is_srgb); | ||
| 149 | }; | 148 | }; |
| 150 | if (swapchain.NeedsRecreation(is_srgb)) { | 149 | |
| 151 | recreate_swapchain(); | 150 | const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout(); |
| 151 | if (swapchain.NeedsRecreation(is_srgb) || swapchain.GetWidth() != layout.width || | ||
| 152 | swapchain.GetHeight() != layout.height) { | ||
| 153 | recreate_swapchain(layout.width, layout.height); | ||
| 152 | } | 154 | } |
| 153 | bool is_outdated; | 155 | bool is_outdated; |
| 154 | do { | 156 | do { |
| 155 | swapchain.AcquireNextImage(); | 157 | swapchain.AcquireNextImage(); |
| 156 | is_outdated = swapchain.IsOutDated(); | 158 | is_outdated = swapchain.IsOutDated(); |
| 157 | if (is_outdated) { | 159 | if (is_outdated) { |
| 158 | recreate_swapchain(); | 160 | recreate_swapchain(layout.width, layout.height); |
| 159 | } | 161 | } |
| 160 | } while (is_outdated); | 162 | } while (is_outdated); |
| 161 | if (has_been_recreated) { | 163 | if (has_been_recreated) { |
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 54a12b35f..6b54d7111 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp | |||
| @@ -461,6 +461,9 @@ void BufferCacheRuntime::BindQuadIndexBuffer(PrimitiveTopology topology, u32 fir | |||
| 461 | 461 | ||
| 462 | void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, | 462 | void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, |
| 463 | u32 stride) { | 463 | u32 stride) { |
| 464 | if (index >= device.GetMaxVertexInputBindings()) { | ||
| 465 | return; | ||
| 466 | } | ||
| 464 | if (device.IsExtExtendedDynamicStateSupported()) { | 467 | if (device.IsExtExtendedDynamicStateSupported()) { |
| 465 | scheduler.Record([index, buffer, offset, size, stride](vk::CommandBuffer cmdbuf) { | 468 | scheduler.Record([index, buffer, offset, size, stride](vk::CommandBuffer cmdbuf) { |
| 466 | const VkDeviceSize vk_offset = buffer != VK_NULL_HANDLE ? offset : 0; | 469 | const VkDeviceSize vk_offset = buffer != VK_NULL_HANDLE ? offset : 0; |
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 006128638..515d8d869 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp | |||
| @@ -529,7 +529,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { | |||
| 529 | static_vector<VkVertexInputBindingDivisorDescriptionEXT, 32> vertex_binding_divisors; | 529 | static_vector<VkVertexInputBindingDivisorDescriptionEXT, 32> vertex_binding_divisors; |
| 530 | static_vector<VkVertexInputAttributeDescription, 32> vertex_attributes; | 530 | static_vector<VkVertexInputAttributeDescription, 32> vertex_attributes; |
| 531 | if (key.state.dynamic_vertex_input) { | 531 | if (key.state.dynamic_vertex_input) { |
| 532 | for (size_t index = 0; index < key.state.attributes.size(); ++index) { | 532 | const size_t num_vertex_arrays = std::min( |
| 533 | key.state.attributes.size(), static_cast<size_t>(device.GetMaxVertexInputBindings())); | ||
| 534 | for (size_t index = 0; index < num_vertex_arrays; ++index) { | ||
| 533 | const u32 type = key.state.DynamicAttributeType(index); | 535 | const u32 type = key.state.DynamicAttributeType(index); |
| 534 | if (!stage_infos[0].loads.Generic(index) || type == 0) { | 536 | if (!stage_infos[0].loads.Generic(index) || type == 0) { |
| 535 | continue; | 537 | continue; |
| @@ -551,7 +553,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { | |||
| 551 | }); | 553 | }); |
| 552 | } | 554 | } |
| 553 | } else { | 555 | } else { |
| 554 | for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { | 556 | const size_t num_vertex_arrays = std::min( |
| 557 | Maxwell::NumVertexArrays, static_cast<size_t>(device.GetMaxVertexInputBindings())); | ||
| 558 | for (size_t index = 0; index < num_vertex_arrays; ++index) { | ||
| 555 | const bool instanced = key.state.binding_divisors[index] != 0; | 559 | const bool instanced = key.state.binding_divisors[index] != 0; |
| 556 | const auto rate = | 560 | const auto rate = |
| 557 | instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; | 561 | instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; |
| @@ -580,6 +584,8 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { | |||
| 580 | }); | 584 | }); |
| 581 | } | 585 | } |
| 582 | } | 586 | } |
| 587 | ASSERT(vertex_attributes.size() <= device.GetMaxVertexInputAttributes()); | ||
| 588 | |||
| 583 | VkPipelineVertexInputStateCreateInfo vertex_input_ci{ | 589 | VkPipelineVertexInputStateCreateInfo vertex_input_ci{ |
| 584 | .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, | 590 | .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, |
| 585 | .pNext = nullptr, | 591 | .pNext = nullptr, |
| @@ -634,23 +640,33 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { | |||
| 634 | }; | 640 | }; |
| 635 | std::array<VkViewportSwizzleNV, Maxwell::NumViewports> swizzles; | 641 | std::array<VkViewportSwizzleNV, Maxwell::NumViewports> swizzles; |
| 636 | std::ranges::transform(key.state.viewport_swizzles, swizzles.begin(), UnpackViewportSwizzle); | 642 | std::ranges::transform(key.state.viewport_swizzles, swizzles.begin(), UnpackViewportSwizzle); |
| 637 | const VkPipelineViewportSwizzleStateCreateInfoNV swizzle_ci{ | 643 | VkPipelineViewportSwizzleStateCreateInfoNV swizzle_ci{ |
| 638 | .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_SWIZZLE_STATE_CREATE_INFO_NV, | 644 | .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_SWIZZLE_STATE_CREATE_INFO_NV, |
| 639 | .pNext = nullptr, | 645 | .pNext = nullptr, |
| 640 | .flags = 0, | 646 | .flags = 0, |
| 641 | .viewportCount = Maxwell::NumViewports, | 647 | .viewportCount = Maxwell::NumViewports, |
| 642 | .pViewportSwizzles = swizzles.data(), | 648 | .pViewportSwizzles = swizzles.data(), |
| 643 | }; | 649 | }; |
| 644 | const VkPipelineViewportStateCreateInfo viewport_ci{ | 650 | VkPipelineViewportDepthClipControlCreateInfoEXT ndc_info{ |
| 651 | .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_DEPTH_CLIP_CONTROL_CREATE_INFO_EXT, | ||
| 652 | .pNext = nullptr, | ||
| 653 | .negativeOneToOne = key.state.ndc_minus_one_to_one.Value() != 0 ? VK_TRUE : VK_FALSE, | ||
| 654 | }; | ||
| 655 | VkPipelineViewportStateCreateInfo viewport_ci{ | ||
| 645 | .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, | 656 | .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, |
| 646 | .pNext = device.IsNvViewportSwizzleSupported() ? &swizzle_ci : nullptr, | 657 | .pNext = nullptr, |
| 647 | .flags = 0, | 658 | .flags = 0, |
| 648 | .viewportCount = Maxwell::NumViewports, | 659 | .viewportCount = Maxwell::NumViewports, |
| 649 | .pViewports = nullptr, | 660 | .pViewports = nullptr, |
| 650 | .scissorCount = Maxwell::NumViewports, | 661 | .scissorCount = Maxwell::NumViewports, |
| 651 | .pScissors = nullptr, | 662 | .pScissors = nullptr, |
| 652 | }; | 663 | }; |
| 653 | 664 | if (device.IsNvViewportSwizzleSupported()) { | |
| 665 | swizzle_ci.pNext = std::exchange(viewport_ci.pNext, &swizzle_ci); | ||
| 666 | } | ||
| 667 | if (device.IsExtDepthClipControlSupported()) { | ||
| 668 | ndc_info.pNext = std::exchange(viewport_ci.pNext, &ndc_info); | ||
| 669 | } | ||
| 654 | VkPipelineRasterizationStateCreateInfo rasterization_ci{ | 670 | VkPipelineRasterizationStateCreateInfo rasterization_ci{ |
| 655 | .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, | 671 | .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, |
| 656 | .pNext = nullptr, | 672 | .pNext = nullptr, |
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 81f5f3e11..e7262420c 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | |||
| @@ -321,6 +321,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device | |||
| 321 | .support_int64_atomics = device.IsExtShaderAtomicInt64Supported(), | 321 | .support_int64_atomics = device.IsExtShaderAtomicInt64Supported(), |
| 322 | .support_derivative_control = true, | 322 | .support_derivative_control = true, |
| 323 | .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(), | 323 | .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(), |
| 324 | .support_native_ndc = device.IsExtDepthClipControlSupported(), | ||
| 324 | 325 | ||
| 325 | .warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(), | 326 | .warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(), |
| 326 | 327 | ||
| @@ -341,6 +342,15 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device | |||
| 341 | .support_snorm_render_buffer = true, | 342 | .support_snorm_render_buffer = true, |
| 342 | .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(), | 343 | .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(), |
| 343 | }; | 344 | }; |
| 345 | |||
| 346 | if (device.GetMaxVertexInputAttributes() < Maxwell::NumVertexAttributes) { | ||
| 347 | LOG_WARNING(Render_Vulkan, "maxVertexInputAttributes is too low: {} < {}", | ||
| 348 | device.GetMaxVertexInputAttributes(), Maxwell::NumVertexAttributes); | ||
| 349 | } | ||
| 350 | if (device.GetMaxVertexInputBindings() < Maxwell::NumVertexArrays) { | ||
| 351 | LOG_WARNING(Render_Vulkan, "maxVertexInputBindings is too low: {} < {}", | ||
| 352 | device.GetMaxVertexInputBindings(), Maxwell::NumVertexArrays); | ||
| 353 | } | ||
| 344 | } | 354 | } |
| 345 | 355 | ||
| 346 | PipelineCache::~PipelineCache() = default; | 356 | PipelineCache::~PipelineCache() = default; |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 3774f303a..ac1eb9895 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp | |||
| @@ -220,9 +220,6 @@ void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) { | |||
| 220 | void RasterizerVulkan::Clear(u32 layer_count) { | 220 | void RasterizerVulkan::Clear(u32 layer_count) { |
| 221 | MICROPROFILE_SCOPE(Vulkan_Clearing); | 221 | MICROPROFILE_SCOPE(Vulkan_Clearing); |
| 222 | 222 | ||
| 223 | if (!maxwell3d->ShouldExecute()) { | ||
| 224 | return; | ||
| 225 | } | ||
| 226 | FlushWork(); | 223 | FlushWork(); |
| 227 | 224 | ||
| 228 | query_cache.UpdateCounters(); | 225 | query_cache.UpdateCounters(); |
| @@ -665,8 +662,7 @@ void RasterizerVulkan::BeginTransformFeedback() { | |||
| 665 | return; | 662 | return; |
| 666 | } | 663 | } |
| 667 | UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderType::TessellationInit) || | 664 | UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderType::TessellationInit) || |
| 668 | regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation) || | 665 | regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation)); |
| 669 | regs.IsShaderConfigEnabled(Maxwell::ShaderType::Geometry)); | ||
| 670 | scheduler.Record( | 666 | scheduler.Record( |
| 671 | [](vk::CommandBuffer cmdbuf) { cmdbuf.BeginTransformFeedbackEXT(0, 0, nullptr, nullptr); }); | 667 | [](vk::CommandBuffer cmdbuf) { cmdbuf.BeginTransformFeedbackEXT(0, 0, nullptr, nullptr); }); |
| 672 | } | 668 | } |
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index d7be417f5..b6810eef9 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp | |||
| @@ -67,17 +67,19 @@ VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, u32 wi | |||
| 67 | 67 | ||
| 68 | } // Anonymous namespace | 68 | } // Anonymous namespace |
| 69 | 69 | ||
| 70 | Swapchain::Swapchain(VkSurfaceKHR surface_, const Device& device_, Scheduler& scheduler_, u32 width, | 70 | Swapchain::Swapchain(VkSurfaceKHR surface_, const Device& device_, Scheduler& scheduler_, |
| 71 | u32 height, bool srgb) | 71 | u32 width_, u32 height_, bool srgb) |
| 72 | : surface{surface_}, device{device_}, scheduler{scheduler_} { | 72 | : surface{surface_}, device{device_}, scheduler{scheduler_} { |
| 73 | Create(width, height, srgb); | 73 | Create(width_, height_, srgb); |
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | Swapchain::~Swapchain() = default; | 76 | Swapchain::~Swapchain() = default; |
| 77 | 77 | ||
| 78 | void Swapchain::Create(u32 width, u32 height, bool srgb) { | 78 | void Swapchain::Create(u32 width_, u32 height_, bool srgb) { |
| 79 | is_outdated = false; | 79 | is_outdated = false; |
| 80 | is_suboptimal = false; | 80 | is_suboptimal = false; |
| 81 | width = width_; | ||
| 82 | height = height_; | ||
| 81 | 83 | ||
| 82 | const auto physical_device = device.GetPhysical(); | 84 | const auto physical_device = device.GetPhysical(); |
| 83 | const auto capabilities{physical_device.GetSurfaceCapabilitiesKHR(surface)}; | 85 | const auto capabilities{physical_device.GetSurfaceCapabilitiesKHR(surface)}; |
| @@ -88,7 +90,7 @@ void Swapchain::Create(u32 width, u32 height, bool srgb) { | |||
| 88 | device.GetLogical().WaitIdle(); | 90 | device.GetLogical().WaitIdle(); |
| 89 | Destroy(); | 91 | Destroy(); |
| 90 | 92 | ||
| 91 | CreateSwapchain(capabilities, width, height, srgb); | 93 | CreateSwapchain(capabilities, srgb); |
| 92 | CreateSemaphores(); | 94 | CreateSemaphores(); |
| 93 | CreateImageViews(); | 95 | CreateImageViews(); |
| 94 | 96 | ||
| @@ -148,8 +150,7 @@ void Swapchain::Present(VkSemaphore render_semaphore) { | |||
| 148 | } | 150 | } |
| 149 | } | 151 | } |
| 150 | 152 | ||
| 151 | void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, u32 width, u32 height, | 153 | void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bool srgb) { |
| 152 | bool srgb) { | ||
| 153 | const auto physical_device{device.GetPhysical()}; | 154 | const auto physical_device{device.GetPhysical()}; |
| 154 | const auto formats{physical_device.GetSurfaceFormatsKHR(surface)}; | 155 | const auto formats{physical_device.GetSurfaceFormatsKHR(surface)}; |
| 155 | const auto present_modes{physical_device.GetSurfacePresentModesKHR(surface)}; | 156 | const auto present_modes{physical_device.GetSurfacePresentModesKHR(surface)}; |
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h index 111b3902d..caf1ff32b 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.h +++ b/src/video_core/renderer_vulkan/vk_swapchain.h | |||
| @@ -80,9 +80,16 @@ public: | |||
| 80 | return *present_semaphores[frame_index]; | 80 | return *present_semaphores[frame_index]; |
| 81 | } | 81 | } |
| 82 | 82 | ||
| 83 | u32 GetWidth() const { | ||
| 84 | return width; | ||
| 85 | } | ||
| 86 | |||
| 87 | u32 GetHeight() const { | ||
| 88 | return height; | ||
| 89 | } | ||
| 90 | |||
| 83 | private: | 91 | private: |
| 84 | void CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, u32 width, u32 height, | 92 | void CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bool srgb); |
| 85 | bool srgb); | ||
| 86 | void CreateSemaphores(); | 93 | void CreateSemaphores(); |
| 87 | void CreateImageViews(); | 94 | void CreateImageViews(); |
| 88 | 95 | ||
| @@ -105,6 +112,9 @@ private: | |||
| 105 | std::vector<u64> resource_ticks; | 112 | std::vector<u64> resource_ticks; |
| 106 | std::vector<vk::Semaphore> present_semaphores; | 113 | std::vector<vk::Semaphore> present_semaphores; |
| 107 | 114 | ||
| 115 | u32 width; | ||
| 116 | u32 height; | ||
| 117 | |||
| 108 | u32 image_index{}; | 118 | u32 image_index{}; |
| 109 | u32 frame_index{}; | 119 | u32 frame_index{}; |
| 110 | 120 | ||
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 8e68a2e53..27c82cd20 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h | |||
| @@ -39,6 +39,12 @@ TextureCache<P>::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface& | |||
| 39 | sampler_descriptor.mipmap_filter.Assign(Tegra::Texture::TextureMipmapFilter::Linear); | 39 | sampler_descriptor.mipmap_filter.Assign(Tegra::Texture::TextureMipmapFilter::Linear); |
| 40 | sampler_descriptor.cubemap_anisotropy.Assign(1); | 40 | sampler_descriptor.cubemap_anisotropy.Assign(1); |
| 41 | 41 | ||
| 42 | // These values were chosen based on typical peak swizzle data sizes seen in some titles | ||
| 43 | static constexpr size_t SWIZZLE_DATA_BUFFER_INITIAL_CAPACITY = 8_MiB; | ||
| 44 | static constexpr size_t UNSWIZZLE_DATA_BUFFER_INITIAL_CAPACITY = 1_MiB; | ||
| 45 | swizzle_data_buffer.resize_destructive(SWIZZLE_DATA_BUFFER_INITIAL_CAPACITY); | ||
| 46 | unswizzle_data_buffer.resize_destructive(UNSWIZZLE_DATA_BUFFER_INITIAL_CAPACITY); | ||
| 47 | |||
| 42 | // Make sure the first index is reserved for the null resources | 48 | // Make sure the first index is reserved for the null resources |
| 43 | // This way the null resource becomes a compile time constant | 49 | // This way the null resource becomes a compile time constant |
| 44 | void(slot_images.insert(NullImageParams{})); | 50 | void(slot_images.insert(NullImageParams{})); |
| @@ -90,7 +96,8 @@ void TextureCache<P>::RunGarbageCollector() { | |||
| 90 | const auto copies = FullDownloadCopies(image.info); | 96 | const auto copies = FullDownloadCopies(image.info); |
| 91 | image.DownloadMemory(map, copies); | 97 | image.DownloadMemory(map, copies); |
| 92 | runtime.Finish(); | 98 | runtime.Finish(); |
| 93 | SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span); | 99 | SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span, |
| 100 | swizzle_data_buffer); | ||
| 94 | } | 101 | } |
| 95 | if (True(image.flags & ImageFlagBits::Tracked)) { | 102 | if (True(image.flags & ImageFlagBits::Tracked)) { |
| 96 | UntrackImage(image, image_id); | 103 | UntrackImage(image, image_id); |
| @@ -461,7 +468,8 @@ void TextureCache<P>::DownloadMemory(VAddr cpu_addr, size_t size) { | |||
| 461 | const auto copies = FullDownloadCopies(image.info); | 468 | const auto copies = FullDownloadCopies(image.info); |
| 462 | image.DownloadMemory(map, copies); | 469 | image.DownloadMemory(map, copies); |
| 463 | runtime.Finish(); | 470 | runtime.Finish(); |
| 464 | SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span); | 471 | SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span, |
| 472 | swizzle_data_buffer); | ||
| 465 | } | 473 | } |
| 466 | } | 474 | } |
| 467 | 475 | ||
| @@ -672,7 +680,8 @@ void TextureCache<P>::PopAsyncFlushes() { | |||
| 672 | for (const ImageId image_id : download_ids) { | 680 | for (const ImageId image_id : download_ids) { |
| 673 | const ImageBase& image = slot_images[image_id]; | 681 | const ImageBase& image = slot_images[image_id]; |
| 674 | const auto copies = FullDownloadCopies(image.info); | 682 | const auto copies = FullDownloadCopies(image.info); |
| 675 | SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, download_span); | 683 | SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, download_span, |
| 684 | swizzle_data_buffer); | ||
| 676 | download_map.offset += image.unswizzled_size_bytes; | 685 | download_map.offset += image.unswizzled_size_bytes; |
| 677 | download_span = download_span.subspan(image.unswizzled_size_bytes); | 686 | download_span = download_span.subspan(image.unswizzled_size_bytes); |
| 678 | } | 687 | } |
| @@ -734,13 +743,21 @@ void TextureCache<P>::UploadImageContents(Image& image, StagingBuffer& staging) | |||
| 734 | gpu_memory->ReadBlockUnsafe(gpu_addr, mapped_span.data(), mapped_span.size_bytes()); | 743 | gpu_memory->ReadBlockUnsafe(gpu_addr, mapped_span.data(), mapped_span.size_bytes()); |
| 735 | const auto uploads = FullUploadSwizzles(image.info); | 744 | const auto uploads = FullUploadSwizzles(image.info); |
| 736 | runtime.AccelerateImageUpload(image, staging, uploads); | 745 | runtime.AccelerateImageUpload(image, staging, uploads); |
| 737 | } else if (True(image.flags & ImageFlagBits::Converted)) { | 746 | return; |
| 738 | std::vector<u8> unswizzled_data(image.unswizzled_size_bytes); | 747 | } |
| 739 | auto copies = UnswizzleImage(*gpu_memory, gpu_addr, image.info, unswizzled_data); | 748 | const size_t guest_size_bytes = image.guest_size_bytes; |
| 740 | ConvertImage(unswizzled_data, image.info, mapped_span, copies); | 749 | swizzle_data_buffer.resize_destructive(guest_size_bytes); |
| 750 | gpu_memory->ReadBlockUnsafe(gpu_addr, swizzle_data_buffer.data(), guest_size_bytes); | ||
| 751 | |||
| 752 | if (True(image.flags & ImageFlagBits::Converted)) { | ||
| 753 | unswizzle_data_buffer.resize_destructive(image.unswizzled_size_bytes); | ||
| 754 | auto copies = UnswizzleImage(*gpu_memory, gpu_addr, image.info, swizzle_data_buffer, | ||
| 755 | unswizzle_data_buffer); | ||
| 756 | ConvertImage(unswizzle_data_buffer, image.info, mapped_span, copies); | ||
| 741 | image.UploadMemory(staging, copies); | 757 | image.UploadMemory(staging, copies); |
| 742 | } else { | 758 | } else { |
| 743 | const auto copies = UnswizzleImage(*gpu_memory, gpu_addr, image.info, mapped_span); | 759 | const auto copies = |
| 760 | UnswizzleImage(*gpu_memory, gpu_addr, image.info, swizzle_data_buffer, mapped_span); | ||
| 744 | image.UploadMemory(staging, copies); | 761 | image.UploadMemory(staging, copies); |
| 745 | } | 762 | } |
| 746 | } | 763 | } |
| @@ -910,7 +927,7 @@ void TextureCache<P>::InvalidateScale(Image& image) { | |||
| 910 | } | 927 | } |
| 911 | 928 | ||
| 912 | template <class P> | 929 | template <class P> |
| 913 | u64 TextureCache<P>::GetScaledImageSizeBytes(ImageBase& image) { | 930 | u64 TextureCache<P>::GetScaledImageSizeBytes(const ImageBase& image) { |
| 914 | const u64 scale_up = static_cast<u64>(Settings::values.resolution_info.up_scale * | 931 | const u64 scale_up = static_cast<u64>(Settings::values.resolution_info.up_scale * |
| 915 | Settings::values.resolution_info.up_scale); | 932 | Settings::values.resolution_info.up_scale); |
| 916 | const u64 down_shift = static_cast<u64>(Settings::values.resolution_info.down_shift + | 933 | const u64 down_shift = static_cast<u64>(Settings::values.resolution_info.down_shift + |
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index 587339a31..4fd677a80 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include "common/literals.h" | 17 | #include "common/literals.h" |
| 18 | #include "common/lru_cache.h" | 18 | #include "common/lru_cache.h" |
| 19 | #include "common/polyfill_ranges.h" | 19 | #include "common/polyfill_ranges.h" |
| 20 | #include "common/scratch_buffer.h" | ||
| 20 | #include "video_core/compatible_formats.h" | 21 | #include "video_core/compatible_formats.h" |
| 21 | #include "video_core/control/channel_state_cache.h" | 22 | #include "video_core/control/channel_state_cache.h" |
| 22 | #include "video_core/delayed_destruction_ring.h" | 23 | #include "video_core/delayed_destruction_ring.h" |
| @@ -368,7 +369,7 @@ private: | |||
| 368 | void InvalidateScale(Image& image); | 369 | void InvalidateScale(Image& image); |
| 369 | bool ScaleUp(Image& image); | 370 | bool ScaleUp(Image& image); |
| 370 | bool ScaleDown(Image& image); | 371 | bool ScaleDown(Image& image); |
| 371 | u64 GetScaledImageSizeBytes(ImageBase& image); | 372 | u64 GetScaledImageSizeBytes(const ImageBase& image); |
| 372 | 373 | ||
| 373 | Runtime& runtime; | 374 | Runtime& runtime; |
| 374 | 375 | ||
| @@ -417,6 +418,9 @@ private: | |||
| 417 | 418 | ||
| 418 | std::unordered_map<GPUVAddr, ImageAllocId> image_allocs_table; | 419 | std::unordered_map<GPUVAddr, ImageAllocId> image_allocs_table; |
| 419 | 420 | ||
| 421 | Common::ScratchBuffer<u8> swizzle_data_buffer; | ||
| 422 | Common::ScratchBuffer<u8> unswizzle_data_buffer; | ||
| 423 | |||
| 420 | u64 modification_tick = 0; | 424 | u64 modification_tick = 0; |
| 421 | u64 frame_tick = 0; | 425 | u64 frame_tick = 0; |
| 422 | }; | 426 | }; |
diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp index e8c908b42..03acc68d9 100644 --- a/src/video_core/texture_cache/util.cpp +++ b/src/video_core/texture_cache/util.cpp | |||
| @@ -505,7 +505,7 @@ void SwizzlePitchLinearImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr | |||
| 505 | 505 | ||
| 506 | void SwizzleBlockLinearImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, | 506 | void SwizzleBlockLinearImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, |
| 507 | const ImageInfo& info, const BufferImageCopy& copy, | 507 | const ImageInfo& info, const BufferImageCopy& copy, |
| 508 | std::span<const u8> input) { | 508 | std::span<const u8> input, Common::ScratchBuffer<u8>& tmp_buffer) { |
| 509 | const Extent3D size = info.size; | 509 | const Extent3D size = info.size; |
| 510 | const LevelInfo level_info = MakeLevelInfo(info); | 510 | const LevelInfo level_info = MakeLevelInfo(info); |
| 511 | const Extent2D tile_size = DefaultBlockSize(info.format); | 511 | const Extent2D tile_size = DefaultBlockSize(info.format); |
| @@ -534,8 +534,8 @@ void SwizzleBlockLinearImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr | |||
| 534 | tile_size.height, info.tile_width_spacing); | 534 | tile_size.height, info.tile_width_spacing); |
| 535 | const size_t subresource_size = sizes[level]; | 535 | const size_t subresource_size = sizes[level]; |
| 536 | 536 | ||
| 537 | const auto dst_data = std::make_unique<u8[]>(subresource_size); | 537 | tmp_buffer.resize_destructive(subresource_size); |
| 538 | const std::span<u8> dst(dst_data.get(), subresource_size); | 538 | const std::span<u8> dst(tmp_buffer); |
| 539 | 539 | ||
| 540 | for (s32 layer = 0; layer < info.resources.layers; ++layer) { | 540 | for (s32 layer = 0; layer < info.resources.layers; ++layer) { |
| 541 | const std::span<const u8> src = input.subspan(host_offset); | 541 | const std::span<const u8> src = input.subspan(host_offset); |
| @@ -765,8 +765,9 @@ bool IsValidEntry(const Tegra::MemoryManager& gpu_memory, const TICEntry& config | |||
| 765 | } | 765 | } |
| 766 | 766 | ||
| 767 | std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, | 767 | std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, |
| 768 | const ImageInfo& info, std::span<u8> output) { | 768 | const ImageInfo& info, std::span<const u8> input, |
| 769 | const size_t guest_size_bytes = CalculateGuestSizeInBytes(info); | 769 | std::span<u8> output) { |
| 770 | const size_t guest_size_bytes = input.size_bytes(); | ||
| 770 | const u32 bpp_log2 = BytesPerBlockLog2(info.format); | 771 | const u32 bpp_log2 = BytesPerBlockLog2(info.format); |
| 771 | const Extent3D size = info.size; | 772 | const Extent3D size = info.size; |
| 772 | 773 | ||
| @@ -789,10 +790,6 @@ std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, GP | |||
| 789 | .image_extent = size, | 790 | .image_extent = size, |
| 790 | }}; | 791 | }}; |
| 791 | } | 792 | } |
| 792 | const auto input_data = std::make_unique<u8[]>(guest_size_bytes); | ||
| 793 | gpu_memory.ReadBlockUnsafe(gpu_addr, input_data.get(), guest_size_bytes); | ||
| 794 | const std::span<const u8> input(input_data.get(), guest_size_bytes); | ||
| 795 | |||
| 796 | const LevelInfo level_info = MakeLevelInfo(info); | 793 | const LevelInfo level_info = MakeLevelInfo(info); |
| 797 | const s32 num_layers = info.resources.layers; | 794 | const s32 num_layers = info.resources.layers; |
| 798 | const s32 num_levels = info.resources.levels; | 795 | const s32 num_levels = info.resources.levels; |
| @@ -980,13 +977,14 @@ std::vector<SwizzleParameters> FullUploadSwizzles(const ImageInfo& info) { | |||
| 980 | } | 977 | } |
| 981 | 978 | ||
| 982 | void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info, | 979 | void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info, |
| 983 | std::span<const BufferImageCopy> copies, std::span<const u8> memory) { | 980 | std::span<const BufferImageCopy> copies, std::span<const u8> memory, |
| 981 | Common::ScratchBuffer<u8>& tmp_buffer) { | ||
| 984 | const bool is_pitch_linear = info.type == ImageType::Linear; | 982 | const bool is_pitch_linear = info.type == ImageType::Linear; |
| 985 | for (const BufferImageCopy& copy : copies) { | 983 | for (const BufferImageCopy& copy : copies) { |
| 986 | if (is_pitch_linear) { | 984 | if (is_pitch_linear) { |
| 987 | SwizzlePitchLinearImage(gpu_memory, gpu_addr, info, copy, memory); | 985 | SwizzlePitchLinearImage(gpu_memory, gpu_addr, info, copy, memory); |
| 988 | } else { | 986 | } else { |
| 989 | SwizzleBlockLinearImage(gpu_memory, gpu_addr, info, copy, memory); | 987 | SwizzleBlockLinearImage(gpu_memory, gpu_addr, info, copy, memory, tmp_buffer); |
| 990 | } | 988 | } |
| 991 | } | 989 | } |
| 992 | } | 990 | } |
diff --git a/src/video_core/texture_cache/util.h b/src/video_core/texture_cache/util.h index 5e28f4ab3..d103db8ae 100644 --- a/src/video_core/texture_cache/util.h +++ b/src/video_core/texture_cache/util.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <span> | 7 | #include <span> |
| 8 | 8 | ||
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "common/scratch_buffer.h" | ||
| 10 | 11 | ||
| 11 | #include "video_core/surface.h" | 12 | #include "video_core/surface.h" |
| 12 | #include "video_core/texture_cache/image_base.h" | 13 | #include "video_core/texture_cache/image_base.h" |
| @@ -59,6 +60,7 @@ struct OverlapResult { | |||
| 59 | 60 | ||
| 60 | [[nodiscard]] std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, | 61 | [[nodiscard]] std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, |
| 61 | GPUVAddr gpu_addr, const ImageInfo& info, | 62 | GPUVAddr gpu_addr, const ImageInfo& info, |
| 63 | std::span<const u8> input, | ||
| 62 | std::span<u8> output); | 64 | std::span<u8> output); |
| 63 | 65 | ||
| 64 | [[nodiscard]] BufferCopy UploadBufferCopy(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, | 66 | [[nodiscard]] BufferCopy UploadBufferCopy(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, |
| @@ -76,7 +78,8 @@ void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8 | |||
| 76 | [[nodiscard]] std::vector<SwizzleParameters> FullUploadSwizzles(const ImageInfo& info); | 78 | [[nodiscard]] std::vector<SwizzleParameters> FullUploadSwizzles(const ImageInfo& info); |
| 77 | 79 | ||
| 78 | void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info, | 80 | void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info, |
| 79 | std::span<const BufferImageCopy> copies, std::span<const u8> memory); | 81 | std::span<const BufferImageCopy> copies, std::span<const u8> memory, |
| 82 | Common::ScratchBuffer<u8>& tmp_buffer); | ||
| 80 | 83 | ||
| 81 | [[nodiscard]] bool IsBlockLinearSizeCompatible(const ImageInfo& new_info, | 84 | [[nodiscard]] bool IsBlockLinearSizeCompatible(const ImageInfo& new_info, |
| 82 | const ImageInfo& overlap_info, u32 new_level, | 85 | const ImageInfo& overlap_info, u32 new_level, |
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 6a2ad4b1d..c4d31681a 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp | |||
| @@ -421,7 +421,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR | |||
| 421 | VkPhysicalDevice8BitStorageFeatures bit8_storage{ | 421 | VkPhysicalDevice8BitStorageFeatures bit8_storage{ |
| 422 | .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES, | 422 | .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES, |
| 423 | .pNext = nullptr, | 423 | .pNext = nullptr, |
| 424 | .storageBuffer8BitAccess = false, | 424 | .storageBuffer8BitAccess = true, |
| 425 | .uniformAndStorageBuffer8BitAccess = true, | 425 | .uniformAndStorageBuffer8BitAccess = true, |
| 426 | .storagePushConstant8 = false, | 426 | .storagePushConstant8 = false, |
| 427 | }; | 427 | }; |
| @@ -660,6 +660,16 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR | |||
| 660 | LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted"); | 660 | LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted"); |
| 661 | } | 661 | } |
| 662 | 662 | ||
| 663 | VkPhysicalDeviceDepthClipControlFeaturesEXT depth_clip_control_features; | ||
| 664 | if (ext_depth_clip_control) { | ||
| 665 | depth_clip_control_features = { | ||
| 666 | .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT, | ||
| 667 | .pNext = nullptr, | ||
| 668 | .depthClipControl = VK_TRUE, | ||
| 669 | }; | ||
| 670 | SetNext(next, depth_clip_control_features); | ||
| 671 | } | ||
| 672 | |||
| 663 | VkDeviceDiagnosticsConfigCreateInfoNV diagnostics_nv; | 673 | VkDeviceDiagnosticsConfigCreateInfoNV diagnostics_nv; |
| 664 | if (Settings::values.enable_nsight_aftermath && nv_device_diagnostics_config) { | 674 | if (Settings::values.enable_nsight_aftermath && nv_device_diagnostics_config) { |
| 665 | nsight_aftermath_tracker = std::make_unique<NsightAftermathTracker>(); | 675 | nsight_aftermath_tracker = std::make_unique<NsightAftermathTracker>(); |
| @@ -1044,6 +1054,7 @@ void Device::CheckSuitability(bool requires_swapchain) const { | |||
| 1044 | std::make_pair(bit16_storage.storageBuffer16BitAccess, "storageBuffer16BitAccess"), | 1054 | std::make_pair(bit16_storage.storageBuffer16BitAccess, "storageBuffer16BitAccess"), |
| 1045 | std::make_pair(bit16_storage.uniformAndStorageBuffer16BitAccess, | 1055 | std::make_pair(bit16_storage.uniformAndStorageBuffer16BitAccess, |
| 1046 | "uniformAndStorageBuffer16BitAccess"), | 1056 | "uniformAndStorageBuffer16BitAccess"), |
| 1057 | std::make_pair(bit8_storage.storageBuffer8BitAccess, "storageBuffer8BitAccess"), | ||
| 1047 | std::make_pair(bit8_storage.uniformAndStorageBuffer8BitAccess, | 1058 | std::make_pair(bit8_storage.uniformAndStorageBuffer8BitAccess, |
| 1048 | "uniformAndStorageBuffer8BitAccess"), | 1059 | "uniformAndStorageBuffer8BitAccess"), |
| 1049 | std::make_pair(host_query_reset.hostQueryReset, "hostQueryReset"), | 1060 | std::make_pair(host_query_reset.hostQueryReset, "hostQueryReset"), |
| @@ -1083,6 +1094,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) { | |||
| 1083 | bool has_ext_vertex_input_dynamic_state{}; | 1094 | bool has_ext_vertex_input_dynamic_state{}; |
| 1084 | bool has_ext_line_rasterization{}; | 1095 | bool has_ext_line_rasterization{}; |
| 1085 | bool has_ext_primitive_topology_list_restart{}; | 1096 | bool has_ext_primitive_topology_list_restart{}; |
| 1097 | bool has_ext_depth_clip_control{}; | ||
| 1086 | for (const std::string& extension : supported_extensions) { | 1098 | for (const std::string& extension : supported_extensions) { |
| 1087 | const auto test = [&](std::optional<std::reference_wrapper<bool>> status, const char* name, | 1099 | const auto test = [&](std::optional<std::reference_wrapper<bool>> status, const char* name, |
| 1088 | bool push) { | 1100 | bool push) { |
| @@ -1116,6 +1128,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) { | |||
| 1116 | test(ext_shader_stencil_export, VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME, true); | 1128 | test(ext_shader_stencil_export, VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME, true); |
| 1117 | test(ext_conservative_rasterization, VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME, | 1129 | test(ext_conservative_rasterization, VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME, |
| 1118 | true); | 1130 | true); |
| 1131 | test(has_ext_depth_clip_control, VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME, false); | ||
| 1119 | test(has_ext_transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME, false); | 1132 | test(has_ext_transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME, false); |
| 1120 | test(has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, false); | 1133 | test(has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, false); |
| 1121 | test(has_ext_extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false); | 1134 | test(has_ext_extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false); |
| @@ -1279,6 +1292,19 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) { | |||
| 1279 | ext_line_rasterization = true; | 1292 | ext_line_rasterization = true; |
| 1280 | } | 1293 | } |
| 1281 | } | 1294 | } |
| 1295 | if (has_ext_depth_clip_control) { | ||
| 1296 | VkPhysicalDeviceDepthClipControlFeaturesEXT depth_clip_control_features; | ||
| 1297 | depth_clip_control_features.sType = | ||
| 1298 | VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT; | ||
| 1299 | depth_clip_control_features.pNext = nullptr; | ||
| 1300 | features.pNext = &depth_clip_control_features; | ||
| 1301 | physical.GetFeatures2(features); | ||
| 1302 | |||
| 1303 | if (depth_clip_control_features.depthClipControl) { | ||
| 1304 | extensions.push_back(VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME); | ||
| 1305 | ext_depth_clip_control = true; | ||
| 1306 | } | ||
| 1307 | } | ||
| 1282 | if (has_khr_workgroup_memory_explicit_layout) { | 1308 | if (has_khr_workgroup_memory_explicit_layout) { |
| 1283 | VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR layout; | 1309 | VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR layout; |
| 1284 | layout.sType = | 1310 | layout.sType = |
| @@ -1380,6 +1406,10 @@ void Device::SetupFeatures() { | |||
| 1380 | is_shader_storage_image_multisample = features.shaderStorageImageMultisample; | 1406 | is_shader_storage_image_multisample = features.shaderStorageImageMultisample; |
| 1381 | is_blit_depth_stencil_supported = TestDepthStencilBlits(); | 1407 | is_blit_depth_stencil_supported = TestDepthStencilBlits(); |
| 1382 | is_optimal_astc_supported = IsOptimalAstcSupported(features); | 1408 | is_optimal_astc_supported = IsOptimalAstcSupported(features); |
| 1409 | |||
| 1410 | const VkPhysicalDeviceLimits& limits{properties.limits}; | ||
| 1411 | max_vertex_input_attributes = limits.maxVertexInputAttributes; | ||
| 1412 | max_vertex_input_bindings = limits.maxVertexInputBindings; | ||
| 1383 | } | 1413 | } |
| 1384 | 1414 | ||
| 1385 | void Device::SetupProperties() { | 1415 | void Device::SetupProperties() { |
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index db802437c..6a26c4e6e 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h | |||
| @@ -256,6 +256,11 @@ public: | |||
| 256 | return ext_depth_range_unrestricted; | 256 | return ext_depth_range_unrestricted; |
| 257 | } | 257 | } |
| 258 | 258 | ||
| 259 | /// Returns true if the device supports VK_EXT_depth_clip_control. | ||
| 260 | bool IsExtDepthClipControlSupported() const { | ||
| 261 | return ext_depth_clip_control; | ||
| 262 | } | ||
| 263 | |||
| 259 | /// Returns true if the device supports VK_EXT_shader_viewport_index_layer. | 264 | /// Returns true if the device supports VK_EXT_shader_viewport_index_layer. |
| 260 | bool IsExtShaderViewportIndexLayerSupported() const { | 265 | bool IsExtShaderViewportIndexLayerSupported() const { |
| 261 | return ext_shader_viewport_index_layer; | 266 | return ext_shader_viewport_index_layer; |
| @@ -368,6 +373,14 @@ public: | |||
| 368 | return must_emulate_bgr565; | 373 | return must_emulate_bgr565; |
| 369 | } | 374 | } |
| 370 | 375 | ||
| 376 | u32 GetMaxVertexInputAttributes() const { | ||
| 377 | return max_vertex_input_attributes; | ||
| 378 | } | ||
| 379 | |||
| 380 | u32 GetMaxVertexInputBindings() const { | ||
| 381 | return max_vertex_input_bindings; | ||
| 382 | } | ||
| 383 | |||
| 371 | private: | 384 | private: |
| 372 | /// Checks if the physical device is suitable. | 385 | /// Checks if the physical device is suitable. |
| 373 | void CheckSuitability(bool requires_swapchain) const; | 386 | void CheckSuitability(bool requires_swapchain) const; |
| @@ -446,6 +459,7 @@ private: | |||
| 446 | bool khr_swapchain_mutable_format{}; ///< Support for VK_KHR_swapchain_mutable_format. | 459 | bool khr_swapchain_mutable_format{}; ///< Support for VK_KHR_swapchain_mutable_format. |
| 447 | bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8. | 460 | bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8. |
| 448 | bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax. | 461 | bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax. |
| 462 | bool ext_depth_clip_control{}; ///< Support for VK_EXT_depth_clip_control | ||
| 449 | bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted. | 463 | bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted. |
| 450 | bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer. | 464 | bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer. |
| 451 | bool ext_tooling_info{}; ///< Support for VK_EXT_tooling_info. | 465 | bool ext_tooling_info{}; ///< Support for VK_EXT_tooling_info. |
| @@ -467,6 +481,8 @@ private: | |||
| 467 | bool supports_d24_depth{}; ///< Supports D24 depth buffers. | 481 | bool supports_d24_depth{}; ///< Supports D24 depth buffers. |
| 468 | bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting. | 482 | bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting. |
| 469 | bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format. | 483 | bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format. |
| 484 | u32 max_vertex_input_attributes{}; ///< Max vertex input attributes in pipeline | ||
| 485 | u32 max_vertex_input_bindings{}; ///< Max vertex input buffers in pipeline | ||
| 470 | 486 | ||
| 471 | // Telemetry parameters | 487 | // Telemetry parameters |
| 472 | std::string vendor_name; ///< Device's driver name. | 488 | std::string vendor_name; ///< Device's driver name. |
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index 483b534a0..7dca7341c 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp | |||
| @@ -314,6 +314,18 @@ const char* ToString(VkResult result) noexcept { | |||
| 314 | return "VK_ERROR_VALIDATION_FAILED_EXT"; | 314 | return "VK_ERROR_VALIDATION_FAILED_EXT"; |
| 315 | case VkResult::VK_ERROR_INVALID_SHADER_NV: | 315 | case VkResult::VK_ERROR_INVALID_SHADER_NV: |
| 316 | return "VK_ERROR_INVALID_SHADER_NV"; | 316 | return "VK_ERROR_INVALID_SHADER_NV"; |
| 317 | case VkResult::VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR: | ||
| 318 | return "VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR"; | ||
| 319 | case VkResult::VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR: | ||
| 320 | return "VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR"; | ||
| 321 | case VkResult::VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR: | ||
| 322 | return "VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR"; | ||
| 323 | case VkResult::VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR: | ||
| 324 | return "VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR"; | ||
| 325 | case VkResult::VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR: | ||
| 326 | return "VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR"; | ||
| 327 | case VkResult::VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR: | ||
| 328 | return "VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR"; | ||
| 317 | case VkResult::VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT: | 329 | case VkResult::VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT: |
| 318 | return "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT"; | 330 | return "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT"; |
| 319 | case VkResult::VK_ERROR_FRAGMENTATION_EXT: | 331 | case VkResult::VK_ERROR_FRAGMENTATION_EXT: |
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 5b5b6fed8..3d560f303 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -44,32 +44,30 @@ | |||
| 44 | #include "yuzu/bootmanager.h" | 44 | #include "yuzu/bootmanager.h" |
| 45 | #include "yuzu/main.h" | 45 | #include "yuzu/main.h" |
| 46 | 46 | ||
| 47 | EmuThread::EmuThread(Core::System& system_) : system{system_} {} | 47 | static Core::Frontend::WindowSystemType GetWindowSystemType(); |
| 48 | |||
| 49 | EmuThread::EmuThread(Core::System& system) : m_system{system} {} | ||
| 48 | 50 | ||
| 49 | EmuThread::~EmuThread() = default; | 51 | EmuThread::~EmuThread() = default; |
| 50 | 52 | ||
| 51 | void EmuThread::run() { | 53 | void EmuThread::run() { |
| 52 | std::string name = "EmuControlThread"; | 54 | const char* name = "EmuControlThread"; |
| 53 | MicroProfileOnThreadCreate(name.c_str()); | 55 | MicroProfileOnThreadCreate(name); |
| 54 | Common::SetCurrentThreadName(name.c_str()); | 56 | Common::SetCurrentThreadName(name); |
| 55 | 57 | ||
| 56 | auto& gpu = system.GPU(); | 58 | auto& gpu = m_system.GPU(); |
| 57 | auto stop_token = stop_source.get_token(); | 59 | auto stop_token = m_stop_source.get_token(); |
| 58 | bool debugger_should_start = system.DebuggerEnabled(); | ||
| 59 | 60 | ||
| 60 | system.RegisterHostThread(); | 61 | m_system.RegisterHostThread(); |
| 61 | 62 | ||
| 62 | // Main process has been loaded. Make the context current to this thread and begin GPU and CPU | 63 | // Main process has been loaded. Make the context current to this thread and begin GPU and CPU |
| 63 | // execution. | 64 | // execution. |
| 64 | gpu.Start(); | ||
| 65 | |||
| 66 | gpu.ObtainContext(); | 65 | gpu.ObtainContext(); |
| 67 | 66 | ||
| 68 | emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); | 67 | emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); |
| 69 | |||
| 70 | if (Settings::values.use_disk_shader_cache.GetValue()) { | 68 | if (Settings::values.use_disk_shader_cache.GetValue()) { |
| 71 | system.Renderer().ReadRasterizer()->LoadDiskResources( | 69 | m_system.Renderer().ReadRasterizer()->LoadDiskResources( |
| 72 | system.GetCurrentProcessProgramID(), stop_token, | 70 | m_system.GetCurrentProcessProgramID(), stop_token, |
| 73 | [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) { | 71 | [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) { |
| 74 | emit LoadProgress(stage, value, total); | 72 | emit LoadProgress(stage, value, total); |
| 75 | }); | 73 | }); |
| @@ -77,53 +75,36 @@ void EmuThread::run() { | |||
| 77 | emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); | 75 | emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); |
| 78 | 76 | ||
| 79 | gpu.ReleaseContext(); | 77 | gpu.ReleaseContext(); |
| 78 | gpu.Start(); | ||
| 80 | 79 | ||
| 81 | system.GetCpuManager().OnGpuReady(); | 80 | m_system.GetCpuManager().OnGpuReady(); |
| 82 | |||
| 83 | // Holds whether the cpu was running during the last iteration, | ||
| 84 | // so that the DebugModeLeft signal can be emitted before the | ||
| 85 | // next execution step | ||
| 86 | bool was_active = false; | ||
| 87 | while (!stop_token.stop_requested()) { | ||
| 88 | if (running) { | ||
| 89 | if (was_active) { | ||
| 90 | emit DebugModeLeft(); | ||
| 91 | } | ||
| 92 | |||
| 93 | running_guard = true; | ||
| 94 | Core::SystemResultStatus result = system.Run(); | ||
| 95 | if (result != Core::SystemResultStatus::Success) { | ||
| 96 | running_guard = false; | ||
| 97 | this->SetRunning(false); | ||
| 98 | emit ErrorThrown(result, system.GetStatusDetails()); | ||
| 99 | } | ||
| 100 | 81 | ||
| 101 | if (debugger_should_start) { | 82 | if (m_system.DebuggerEnabled()) { |
| 102 | system.InitializeDebugger(); | 83 | m_system.InitializeDebugger(); |
| 103 | debugger_should_start = false; | 84 | } |
| 104 | } | ||
| 105 | 85 | ||
| 106 | running_wait.Wait(); | 86 | while (!stop_token.stop_requested()) { |
| 107 | result = system.Pause(); | 87 | std::unique_lock lk{m_should_run_mutex}; |
| 108 | if (result != Core::SystemResultStatus::Success) { | 88 | if (m_should_run) { |
| 109 | running_guard = false; | 89 | m_system.Run(); |
| 110 | this->SetRunning(false); | 90 | m_is_running.store(true); |
| 111 | emit ErrorThrown(result, system.GetStatusDetails()); | 91 | m_is_running.notify_all(); |
| 112 | } | ||
| 113 | running_guard = false; | ||
| 114 | 92 | ||
| 115 | if (!stop_token.stop_requested()) { | 93 | Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return !m_should_run; }); |
| 116 | was_active = true; | ||
| 117 | emit DebugModeEntered(); | ||
| 118 | } | ||
| 119 | } else { | 94 | } else { |
| 120 | std::unique_lock lock{running_mutex}; | 95 | m_system.Pause(); |
| 121 | Common::CondvarWait(running_cv, lock, stop_token, [&] { return IsRunning(); }); | 96 | m_is_running.store(false); |
| 97 | m_is_running.notify_all(); | ||
| 98 | |||
| 99 | emit DebugModeEntered(); | ||
| 100 | Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return m_should_run; }); | ||
| 101 | emit DebugModeLeft(); | ||
| 122 | } | 102 | } |
| 123 | } | 103 | } |
| 124 | 104 | ||
| 125 | // Shutdown the main emulated process | 105 | // Shutdown the main emulated process |
| 126 | system.ShutdownMainProcess(); | 106 | m_system.DetachDebugger(); |
| 107 | m_system.ShutdownMainProcess(); | ||
| 127 | 108 | ||
| 128 | #if MICROPROFILE_ENABLED | 109 | #if MICROPROFILE_ENABLED |
| 129 | MicroProfileOnThreadExit(); | 110 | MicroProfileOnThreadExit(); |
| @@ -225,6 +206,9 @@ public: | |||
| 225 | explicit RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) { | 206 | explicit RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) { |
| 226 | setAttribute(Qt::WA_NativeWindow); | 207 | setAttribute(Qt::WA_NativeWindow); |
| 227 | setAttribute(Qt::WA_PaintOnScreen); | 208 | setAttribute(Qt::WA_PaintOnScreen); |
| 209 | if (GetWindowSystemType() == Core::Frontend::WindowSystemType::Wayland) { | ||
| 210 | setAttribute(Qt::WA_DontCreateNativeAncestors); | ||
| 211 | } | ||
| 228 | } | 212 | } |
| 229 | 213 | ||
| 230 | virtual ~RenderWidget() = default; | 214 | virtual ~RenderWidget() = default; |
| @@ -269,12 +253,14 @@ static Core::Frontend::WindowSystemType GetWindowSystemType() { | |||
| 269 | return Core::Frontend::WindowSystemType::X11; | 253 | return Core::Frontend::WindowSystemType::X11; |
| 270 | else if (platform_name == QStringLiteral("wayland")) | 254 | else if (platform_name == QStringLiteral("wayland")) |
| 271 | return Core::Frontend::WindowSystemType::Wayland; | 255 | return Core::Frontend::WindowSystemType::Wayland; |
| 256 | else if (platform_name == QStringLiteral("wayland-egl")) | ||
| 257 | return Core::Frontend::WindowSystemType::Wayland; | ||
| 272 | else if (platform_name == QStringLiteral("cocoa")) | 258 | else if (platform_name == QStringLiteral("cocoa")) |
| 273 | return Core::Frontend::WindowSystemType::Cocoa; | 259 | return Core::Frontend::WindowSystemType::Cocoa; |
| 274 | else if (platform_name == QStringLiteral("android")) | 260 | else if (platform_name == QStringLiteral("android")) |
| 275 | return Core::Frontend::WindowSystemType::Android; | 261 | return Core::Frontend::WindowSystemType::Android; |
| 276 | 262 | ||
| 277 | LOG_CRITICAL(Frontend, "Unknown Qt platform!"); | 263 | LOG_CRITICAL(Frontend, "Unknown Qt platform {}!", platform_name.toStdString()); |
| 278 | return Core::Frontend::WindowSystemType::Windows; | 264 | return Core::Frontend::WindowSystemType::Windows; |
| 279 | } | 265 | } |
| 280 | 266 | ||
| @@ -314,6 +300,9 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_, | |||
| 314 | input_subsystem->Initialize(); | 300 | input_subsystem->Initialize(); |
| 315 | this->setMouseTracking(true); | 301 | this->setMouseTracking(true); |
| 316 | 302 | ||
| 303 | strict_context_required = QGuiApplication::platformName() == QStringLiteral("wayland") || | ||
| 304 | QGuiApplication::platformName() == QStringLiteral("wayland-egl"); | ||
| 305 | |||
| 317 | connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); | 306 | connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); |
| 318 | connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram, | 307 | connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram, |
| 319 | Qt::QueuedConnection); | 308 | Qt::QueuedConnection); |
| @@ -750,6 +739,9 @@ void GRenderWindow::InitializeCamera() { | |||
| 750 | return; | 739 | return; |
| 751 | } | 740 | } |
| 752 | 741 | ||
| 742 | const auto camera_width = input_subsystem->GetCamera()->getImageWidth(); | ||
| 743 | const auto camera_height = input_subsystem->GetCamera()->getImageHeight(); | ||
| 744 | camera_data.resize(camera_width * camera_height); | ||
| 753 | camera_capture->setCaptureDestination(QCameraImageCapture::CaptureDestination::CaptureToBuffer); | 745 | camera_capture->setCaptureDestination(QCameraImageCapture::CaptureDestination::CaptureToBuffer); |
| 754 | connect(camera_capture.get(), &QCameraImageCapture::imageCaptured, this, | 746 | connect(camera_capture.get(), &QCameraImageCapture::imageCaptured, this, |
| 755 | &GRenderWindow::OnCameraCapture); | 747 | &GRenderWindow::OnCameraCapture); |
| @@ -805,17 +797,22 @@ void GRenderWindow::RequestCameraCapture() { | |||
| 805 | } | 797 | } |
| 806 | 798 | ||
| 807 | void GRenderWindow::OnCameraCapture(int requestId, const QImage& img) { | 799 | void GRenderWindow::OnCameraCapture(int requestId, const QImage& img) { |
| 808 | constexpr std::size_t camera_width = 320; | 800 | #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA |
| 809 | constexpr std::size_t camera_height = 240; | 801 | // TODO: Capture directly in the format and resolution needed |
| 802 | const auto camera_width = input_subsystem->GetCamera()->getImageWidth(); | ||
| 803 | const auto camera_height = input_subsystem->GetCamera()->getImageHeight(); | ||
| 810 | const auto converted = | 804 | const auto converted = |
| 811 | img.scaled(camera_width, camera_height, Qt::AspectRatioMode::IgnoreAspectRatio, | 805 | img.scaled(static_cast<int>(camera_width), static_cast<int>(camera_height), |
| 806 | Qt::AspectRatioMode::IgnoreAspectRatio, | ||
| 812 | Qt::TransformationMode::SmoothTransformation) | 807 | Qt::TransformationMode::SmoothTransformation) |
| 813 | .mirrored(false, true); | 808 | .mirrored(false, true); |
| 814 | std::vector<u32> camera_data{}; | 809 | if (camera_data.size() != camera_width * camera_height) { |
| 815 | camera_data.resize(camera_width * camera_height); | 810 | camera_data.resize(camera_width * camera_height); |
| 811 | } | ||
| 816 | std::memcpy(camera_data.data(), converted.bits(), camera_width * camera_height * sizeof(u32)); | 812 | std::memcpy(camera_data.data(), converted.bits(), camera_width * camera_height * sizeof(u32)); |
| 817 | input_subsystem->GetCamera()->SetCameraData(camera_width, camera_height, camera_data); | 813 | input_subsystem->GetCamera()->SetCameraData(camera_width, camera_height, camera_data); |
| 818 | pending_camera_snapshots = 0; | 814 | pending_camera_snapshots = 0; |
| 815 | #endif | ||
| 819 | } | 816 | } |
| 820 | 817 | ||
| 821 | bool GRenderWindow::event(QEvent* event) { | 818 | bool GRenderWindow::event(QEvent* event) { |
| @@ -952,6 +949,12 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal | |||
| 952 | 949 | ||
| 953 | bool GRenderWindow::InitializeOpenGL() { | 950 | bool GRenderWindow::InitializeOpenGL() { |
| 954 | #ifdef HAS_OPENGL | 951 | #ifdef HAS_OPENGL |
| 952 | if (!QOpenGLContext::supportsThreadedOpenGL()) { | ||
| 953 | QMessageBox::warning(this, tr("OpenGL not available!"), | ||
| 954 | tr("OpenGL shared contexts are not supported.")); | ||
| 955 | return false; | ||
| 956 | } | ||
| 957 | |||
| 955 | // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, | 958 | // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, |
| 956 | // WA_DontShowOnScreen, WA_DeleteOnClose | 959 | // WA_DontShowOnScreen, WA_DeleteOnClose |
| 957 | auto child = new OpenGLRenderWidget(this); | 960 | auto child = new OpenGLRenderWidget(this); |
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index f4deae4ee..eca16b313 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h | |||
| @@ -47,7 +47,7 @@ class EmuThread final : public QThread { | |||
| 47 | Q_OBJECT | 47 | Q_OBJECT |
| 48 | 48 | ||
| 49 | public: | 49 | public: |
| 50 | explicit EmuThread(Core::System& system_); | 50 | explicit EmuThread(Core::System& system); |
| 51 | ~EmuThread() override; | 51 | ~EmuThread() override; |
| 52 | 52 | ||
| 53 | /** | 53 | /** |
| @@ -57,48 +57,48 @@ public: | |||
| 57 | void run() override; | 57 | void run() override; |
| 58 | 58 | ||
| 59 | /** | 59 | /** |
| 60 | * Sets whether the emulation thread is running or not | 60 | * Sets whether the emulation thread should run or not |
| 61 | * @param running_ Boolean value, set the emulation thread to running if true | 61 | * @param should_run Boolean value, set the emulation thread to running if true |
| 62 | * @note This function is thread-safe | ||
| 63 | */ | 62 | */ |
| 64 | void SetRunning(bool running_) { | 63 | void SetRunning(bool should_run) { |
| 65 | std::unique_lock lock{running_mutex}; | 64 | // TODO: Prevent other threads from modifying the state until we finish. |
| 66 | running = running_; | 65 | { |
| 67 | lock.unlock(); | 66 | // Notify the running thread to change state. |
| 68 | running_cv.notify_all(); | 67 | std::unique_lock run_lk{m_should_run_mutex}; |
| 69 | if (!running) { | 68 | m_should_run = should_run; |
| 70 | running_wait.Set(); | 69 | m_should_run_cv.notify_one(); |
| 71 | /// Wait until effectively paused | 70 | } |
| 72 | while (running_guard) | 71 | |
| 73 | ; | 72 | // Wait until paused, if pausing. |
| 73 | if (!should_run) { | ||
| 74 | m_is_running.wait(true); | ||
| 74 | } | 75 | } |
| 75 | } | 76 | } |
| 76 | 77 | ||
| 77 | /** | 78 | /** |
| 78 | * Check if the emulation thread is running or not | 79 | * Check if the emulation thread is running or not |
| 79 | * @return True if the emulation thread is running, otherwise false | 80 | * @return True if the emulation thread is running, otherwise false |
| 80 | * @note This function is thread-safe | ||
| 81 | */ | 81 | */ |
| 82 | bool IsRunning() const { | 82 | bool IsRunning() const { |
| 83 | return running; | 83 | return m_is_running.load() || m_should_run; |
| 84 | } | 84 | } |
| 85 | 85 | ||
| 86 | /** | 86 | /** |
| 87 | * Requests for the emulation thread to stop running | 87 | * Requests for the emulation thread to immediately stop running |
| 88 | */ | 88 | */ |
| 89 | void RequestStop() { | 89 | void ForceStop() { |
| 90 | stop_source.request_stop(); | 90 | LOG_WARNING(Frontend, "Force stopping EmuThread"); |
| 91 | SetRunning(false); | 91 | m_stop_source.request_stop(); |
| 92 | } | 92 | } |
| 93 | 93 | ||
| 94 | private: | 94 | private: |
| 95 | bool running = false; | 95 | Core::System& m_system; |
| 96 | std::stop_source stop_source; | 96 | |
| 97 | std::mutex running_mutex; | 97 | std::stop_source m_stop_source; |
| 98 | std::condition_variable_any running_cv; | 98 | std::mutex m_should_run_mutex; |
| 99 | Common::Event running_wait{}; | 99 | std::condition_variable_any m_should_run_cv; |
| 100 | std::atomic_bool running_guard{false}; | 100 | std::atomic<bool> m_is_running{false}; |
| 101 | Core::System& system; | 101 | bool m_should_run{true}; |
| 102 | 102 | ||
| 103 | signals: | 103 | signals: |
| 104 | /** | 104 | /** |
| @@ -119,8 +119,6 @@ signals: | |||
| 119 | */ | 119 | */ |
| 120 | void DebugModeLeft(); | 120 | void DebugModeLeft(); |
| 121 | 121 | ||
| 122 | void ErrorThrown(Core::SystemResultStatus, std::string); | ||
| 123 | |||
| 124 | void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total); | 122 | void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total); |
| 125 | }; | 123 | }; |
| 126 | 124 | ||
| @@ -241,13 +239,14 @@ private: | |||
| 241 | bool first_frame = false; | 239 | bool first_frame = false; |
| 242 | InputCommon::TasInput::TasState last_tas_state; | 240 | InputCommon::TasInput::TasState last_tas_state; |
| 243 | 241 | ||
| 242 | #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA | ||
| 244 | bool is_virtual_camera; | 243 | bool is_virtual_camera; |
| 245 | int pending_camera_snapshots; | 244 | int pending_camera_snapshots; |
| 246 | #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA | 245 | std::vector<u32> camera_data; |
| 247 | std::unique_ptr<QCamera> camera; | 246 | std::unique_ptr<QCamera> camera; |
| 248 | std::unique_ptr<QCameraImageCapture> camera_capture; | 247 | std::unique_ptr<QCameraImageCapture> camera_capture; |
| 249 | #endif | ||
| 250 | std::unique_ptr<QTimer> camera_timer; | 248 | std::unique_ptr<QTimer> camera_timer; |
| 249 | #endif | ||
| 251 | 250 | ||
| 252 | Core::System& system; | 251 | Core::System& system; |
| 253 | 252 | ||
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 722fc708e..2ea4f367b 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -697,7 +697,6 @@ void Config::ReadRendererValues() { | |||
| 697 | ReadGlobalSetting(Settings::values.fsr_sharpening_slider); | 697 | ReadGlobalSetting(Settings::values.fsr_sharpening_slider); |
| 698 | ReadGlobalSetting(Settings::values.anti_aliasing); | 698 | ReadGlobalSetting(Settings::values.anti_aliasing); |
| 699 | ReadGlobalSetting(Settings::values.max_anisotropy); | 699 | ReadGlobalSetting(Settings::values.max_anisotropy); |
| 700 | ReadGlobalSetting(Settings::values.use_speed_limit); | ||
| 701 | ReadGlobalSetting(Settings::values.speed_limit); | 700 | ReadGlobalSetting(Settings::values.speed_limit); |
| 702 | ReadGlobalSetting(Settings::values.use_disk_shader_cache); | 701 | ReadGlobalSetting(Settings::values.use_disk_shader_cache); |
| 703 | ReadGlobalSetting(Settings::values.gpu_accuracy); | 702 | ReadGlobalSetting(Settings::values.gpu_accuracy); |
| @@ -796,6 +795,7 @@ void Config::ReadSystemValues() { | |||
| 796 | } else { | 795 | } else { |
| 797 | Settings::values.custom_rtc = std::nullopt; | 796 | Settings::values.custom_rtc = std::nullopt; |
| 798 | } | 797 | } |
| 798 | ReadBasicSetting(Settings::values.device_name); | ||
| 799 | } | 799 | } |
| 800 | 800 | ||
| 801 | ReadGlobalSetting(Settings::values.sound_index); | 801 | ReadGlobalSetting(Settings::values.sound_index); |
| @@ -1328,7 +1328,6 @@ void Config::SaveRendererValues() { | |||
| 1328 | static_cast<u32>(Settings::values.anti_aliasing.GetDefault()), | 1328 | static_cast<u32>(Settings::values.anti_aliasing.GetDefault()), |
| 1329 | Settings::values.anti_aliasing.UsingGlobal()); | 1329 | Settings::values.anti_aliasing.UsingGlobal()); |
| 1330 | WriteGlobalSetting(Settings::values.max_anisotropy); | 1330 | WriteGlobalSetting(Settings::values.max_anisotropy); |
| 1331 | WriteGlobalSetting(Settings::values.use_speed_limit); | ||
| 1332 | WriteGlobalSetting(Settings::values.speed_limit); | 1331 | WriteGlobalSetting(Settings::values.speed_limit); |
| 1333 | WriteGlobalSetting(Settings::values.use_disk_shader_cache); | 1332 | WriteGlobalSetting(Settings::values.use_disk_shader_cache); |
| 1334 | WriteSetting(QString::fromStdString(Settings::values.gpu_accuracy.GetLabel()), | 1333 | WriteSetting(QString::fromStdString(Settings::values.gpu_accuracy.GetLabel()), |
| @@ -1415,6 +1414,7 @@ void Config::SaveSystemValues() { | |||
| 1415 | false); | 1414 | false); |
| 1416 | WriteSetting(QStringLiteral("custom_rtc"), | 1415 | WriteSetting(QStringLiteral("custom_rtc"), |
| 1417 | QVariant::fromValue<long long>(Settings::values.custom_rtc.value_or(0)), 0); | 1416 | QVariant::fromValue<long long>(Settings::values.custom_rtc.value_or(0)), 0); |
| 1417 | WriteBasicSetting(Settings::values.device_name); | ||
| 1418 | } | 1418 | } |
| 1419 | 1419 | ||
| 1420 | WriteGlobalSetting(Settings::values.sound_index); | 1420 | WriteGlobalSetting(Settings::values.sound_index); |
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index b1575b0d3..183cbe562 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp | |||
| @@ -738,13 +738,10 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 738 | 738 | ||
| 739 | connect(ui->comboDevices, qOverload<int>(&QComboBox::activated), this, | 739 | connect(ui->comboDevices, qOverload<int>(&QComboBox::activated), this, |
| 740 | &ConfigureInputPlayer::UpdateMappingWithDefaults); | 740 | &ConfigureInputPlayer::UpdateMappingWithDefaults); |
| 741 | ui->comboDevices->installEventFilter(this); | ||
| 741 | 742 | ||
| 742 | ui->comboDevices->setCurrentIndex(-1); | 743 | ui->comboDevices->setCurrentIndex(-1); |
| 743 | 744 | ||
| 744 | ui->buttonRefreshDevices->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh"))); | ||
| 745 | connect(ui->buttonRefreshDevices, &QPushButton::clicked, | ||
| 746 | [this] { emit RefreshInputDevices(); }); | ||
| 747 | |||
| 748 | timeout_timer->setSingleShot(true); | 745 | timeout_timer->setSingleShot(true); |
| 749 | connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); }); | 746 | connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); }); |
| 750 | 747 | ||
| @@ -1479,6 +1476,13 @@ void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) { | |||
| 1479 | } | 1476 | } |
| 1480 | } | 1477 | } |
| 1481 | 1478 | ||
| 1479 | bool ConfigureInputPlayer::eventFilter(QObject* object, QEvent* event) { | ||
| 1480 | if (object == ui->comboDevices && event->type() == QEvent::MouseButtonPress) { | ||
| 1481 | RefreshInputDevices(); | ||
| 1482 | } | ||
| 1483 | return object->eventFilter(object, event); | ||
| 1484 | } | ||
| 1485 | |||
| 1482 | void ConfigureInputPlayer::CreateProfile() { | 1486 | void ConfigureInputPlayer::CreateProfile() { |
| 1483 | const auto profile_name = | 1487 | const auto profile_name = |
| 1484 | LimitableInputDialog::GetText(this, tr("New Profile"), tr("Enter a profile name:"), 1, 30, | 1488 | LimitableInputDialog::GetText(this, tr("New Profile"), tr("Enter a profile name:"), 1, 30, |
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h index 26f60d121..6d1876f2b 100644 --- a/src/yuzu/configuration/configure_input_player.h +++ b/src/yuzu/configuration/configure_input_player.h | |||
| @@ -119,6 +119,9 @@ private: | |||
| 119 | /// Handle key press events. | 119 | /// Handle key press events. |
| 120 | void keyPressEvent(QKeyEvent* event) override; | 120 | void keyPressEvent(QKeyEvent* event) override; |
| 121 | 121 | ||
| 122 | /// Handle combobox list refresh | ||
| 123 | bool eventFilter(QObject* object, QEvent* event) override; | ||
| 124 | |||
| 122 | /// Update UI to reflect current configuration. | 125 | /// Update UI to reflect current configuration. |
| 123 | void UpdateUI(); | 126 | void UpdateUI(); |
| 124 | 127 | ||
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui index a62b57501..a9567c6ee 100644 --- a/src/yuzu/configuration/configure_input_player.ui +++ b/src/yuzu/configuration/configure_input_player.ui | |||
| @@ -122,25 +122,6 @@ | |||
| 122 | </property> | 122 | </property> |
| 123 | </widget> | 123 | </widget> |
| 124 | </item> | 124 | </item> |
| 125 | <item> | ||
| 126 | <widget class="QPushButton" name="buttonRefreshDevices"> | ||
| 127 | <property name="minimumSize"> | ||
| 128 | <size> | ||
| 129 | <width>21</width> | ||
| 130 | <height>21</height> | ||
| 131 | </size> | ||
| 132 | </property> | ||
| 133 | <property name="maximumSize"> | ||
| 134 | <size> | ||
| 135 | <width>21</width> | ||
| 136 | <height>21</height> | ||
| 137 | </size> | ||
| 138 | </property> | ||
| 139 | <property name="styleSheet"> | ||
| 140 | <string notr="true"/> | ||
| 141 | </property> | ||
| 142 | </widget> | ||
| 143 | </item> | ||
| 144 | </layout> | 125 | </layout> |
| 145 | </widget> | 126 | </widget> |
| 146 | </item> | 127 | </item> |
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index bc9d9d77a..9b14e5903 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp | |||
| @@ -72,6 +72,8 @@ void ConfigureSystem::SetConfiguration() { | |||
| 72 | ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.has_value()); | 72 | ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.has_value()); |
| 73 | ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.has_value()); | 73 | ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.has_value()); |
| 74 | ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time)); | 74 | ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time)); |
| 75 | ui->device_name_edit->setText( | ||
| 76 | QString::fromUtf8(Settings::values.device_name.GetValue().c_str())); | ||
| 75 | 77 | ||
| 76 | if (Settings::IsConfiguringGlobal()) { | 78 | if (Settings::IsConfiguringGlobal()) { |
| 77 | ui->combo_language->setCurrentIndex(Settings::values.language_index.GetValue()); | 79 | ui->combo_language->setCurrentIndex(Settings::values.language_index.GetValue()); |
| @@ -115,6 +117,8 @@ void ConfigureSystem::ApplyConfiguration() { | |||
| 115 | } | 117 | } |
| 116 | } | 118 | } |
| 117 | 119 | ||
| 120 | Settings::values.device_name = ui->device_name_edit->text().toStdString(); | ||
| 121 | |||
| 118 | if (!enabled) { | 122 | if (!enabled) { |
| 119 | return; | 123 | return; |
| 120 | } | 124 | } |
diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui index b234ea87b..46892f5c1 100644 --- a/src/yuzu/configuration/configure_system.ui +++ b/src/yuzu/configuration/configure_system.ui | |||
| @@ -432,6 +432,13 @@ | |||
| 432 | </property> | 432 | </property> |
| 433 | </widget> | 433 | </widget> |
| 434 | </item> | 434 | </item> |
| 435 | <item row="7" column="0"> | ||
| 436 | <widget class="QLabel" name="device_name_label"> | ||
| 437 | <property name="text"> | ||
| 438 | <string>Device Name</string> | ||
| 439 | </property> | ||
| 440 | </widget> | ||
| 441 | </item> | ||
| 435 | <item row="3" column="1"> | 442 | <item row="3" column="1"> |
| 436 | <widget class="QComboBox" name="combo_sound"> | 443 | <widget class="QComboBox" name="combo_sound"> |
| 437 | <item> | 444 | <item> |
| @@ -476,6 +483,13 @@ | |||
| 476 | </property> | 483 | </property> |
| 477 | </widget> | 484 | </widget> |
| 478 | </item> | 485 | </item> |
| 486 | <item row="7" column="1"> | ||
| 487 | <widget class="QLineEdit" name="device_name_edit"> | ||
| 488 | <property name="maxLength"> | ||
| 489 | <number>128</number> | ||
| 490 | </property> | ||
| 491 | </widget> | ||
| 492 | </item> | ||
| 479 | <item row="6" column="1"> | 493 | <item row="6" column="1"> |
| 480 | <widget class="QLineEdit" name="rng_seed_edit"> | 494 | <widget class="QLineEdit" name="rng_seed_edit"> |
| 481 | <property name="sizePolicy"> | 495 | <property name="sizePolicy"> |
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 5c33c1b0f..22aa19c56 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp | |||
| @@ -554,6 +554,12 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri | |||
| 554 | QAction* dump_romfs_sdmc = dump_romfs_menu->addAction(tr("Dump RomFS to SDMC")); | 554 | QAction* dump_romfs_sdmc = dump_romfs_menu->addAction(tr("Dump RomFS to SDMC")); |
| 555 | QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); | 555 | QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); |
| 556 | QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); | 556 | QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); |
| 557 | #ifndef WIN32 | ||
| 558 | QMenu* shortcut_menu = context_menu.addMenu(tr("Create Shortcut")); | ||
| 559 | QAction* create_desktop_shortcut = shortcut_menu->addAction(tr("Add to Desktop")); | ||
| 560 | QAction* create_applications_menu_shortcut = | ||
| 561 | shortcut_menu->addAction(tr("Add to Applications Menu")); | ||
| 562 | #endif | ||
| 557 | context_menu.addSeparator(); | 563 | context_menu.addSeparator(); |
| 558 | QAction* properties = context_menu.addAction(tr("Properties")); | 564 | QAction* properties = context_menu.addAction(tr("Properties")); |
| 559 | 565 | ||
| @@ -619,6 +625,14 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri | |||
| 619 | connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() { | 625 | connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() { |
| 620 | emit NavigateToGamedbEntryRequested(program_id, compatibility_list); | 626 | emit NavigateToGamedbEntryRequested(program_id, compatibility_list); |
| 621 | }); | 627 | }); |
| 628 | #ifndef WIN32 | ||
| 629 | connect(create_desktop_shortcut, &QAction::triggered, [this, program_id, path]() { | ||
| 630 | emit CreateShortcut(program_id, path, GameListShortcutTarget::Desktop); | ||
| 631 | }); | ||
| 632 | connect(create_applications_menu_shortcut, &QAction::triggered, [this, program_id, path]() { | ||
| 633 | emit CreateShortcut(program_id, path, GameListShortcutTarget::Applications); | ||
| 634 | }); | ||
| 635 | #endif | ||
| 622 | connect(properties, &QAction::triggered, | 636 | connect(properties, &QAction::triggered, |
| 623 | [this, path]() { emit OpenPerGameGeneralRequested(path); }); | 637 | [this, path]() { emit OpenPerGameGeneralRequested(path); }); |
| 624 | }; | 638 | }; |
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index cdf085019..f7ff93ed9 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h | |||
| @@ -52,6 +52,11 @@ enum class DumpRomFSTarget { | |||
| 52 | SDMC, | 52 | SDMC, |
| 53 | }; | 53 | }; |
| 54 | 54 | ||
| 55 | enum class GameListShortcutTarget { | ||
| 56 | Desktop, | ||
| 57 | Applications, | ||
| 58 | }; | ||
| 59 | |||
| 55 | enum class InstalledEntryType { | 60 | enum class InstalledEntryType { |
| 56 | Game, | 61 | Game, |
| 57 | Update, | 62 | Update, |
| @@ -108,6 +113,8 @@ signals: | |||
| 108 | const std::string& game_path); | 113 | const std::string& game_path); |
| 109 | void DumpRomFSRequested(u64 program_id, const std::string& game_path, DumpRomFSTarget target); | 114 | void DumpRomFSRequested(u64 program_id, const std::string& game_path, DumpRomFSTarget target); |
| 110 | void CopyTIDRequested(u64 program_id); | 115 | void CopyTIDRequested(u64 program_id); |
| 116 | void CreateShortcut(u64 program_id, const std::string& game_path, | ||
| 117 | GameListShortcutTarget target); | ||
| 111 | void NavigateToGamedbEntryRequested(u64 program_id, | 118 | void NavigateToGamedbEntryRequested(u64 program_id, |
| 112 | const CompatibilityList& compatibility_list); | 119 | const CompatibilityList& compatibility_list); |
| 113 | void OpenPerGameGeneralRequested(const std::string& file); | 120 | void OpenPerGameGeneralRequested(const std::string& file); |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index b11b26f7b..524650144 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | #include <cinttypes> | 4 | #include <cinttypes> |
| 5 | #include <clocale> | 5 | #include <clocale> |
| 6 | #include <cmath> | 6 | #include <cmath> |
| 7 | #include <fstream> | ||
| 8 | #include <iostream> | ||
| 7 | #include <memory> | 9 | #include <memory> |
| 8 | #include <thread> | 10 | #include <thread> |
| 9 | #ifdef __APPLE__ | 11 | #ifdef __APPLE__ |
| @@ -1249,6 +1251,7 @@ void GMainWindow::ConnectWidgetEvents() { | |||
| 1249 | connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); | 1251 | connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); |
| 1250 | connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, | 1252 | connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, |
| 1251 | &GMainWindow::OnGameListNavigateToGamedbEntry); | 1253 | &GMainWindow::OnGameListNavigateToGamedbEntry); |
| 1254 | connect(game_list, &GameList::CreateShortcut, this, &GMainWindow::OnGameListCreateShortcut); | ||
| 1252 | connect(game_list, &GameList::AddDirectory, this, &GMainWindow::OnGameListAddDirectory); | 1255 | connect(game_list, &GameList::AddDirectory, this, &GMainWindow::OnGameListAddDirectory); |
| 1253 | connect(game_list_placeholder, &GameListPlaceholder::AddDirectory, this, | 1256 | connect(game_list_placeholder, &GameListPlaceholder::AddDirectory, this, |
| 1254 | &GMainWindow::OnGameListAddDirectory); | 1257 | &GMainWindow::OnGameListAddDirectory); |
| @@ -1495,7 +1498,7 @@ void GMainWindow::SetupSigInterrupts() { | |||
| 1495 | 1498 | ||
| 1496 | void GMainWindow::HandleSigInterrupt(int sig) { | 1499 | void GMainWindow::HandleSigInterrupt(int sig) { |
| 1497 | if (sig == SIGINT) { | 1500 | if (sig == SIGINT) { |
| 1498 | exit(1); | 1501 | _exit(1); |
| 1499 | } | 1502 | } |
| 1500 | 1503 | ||
| 1501 | // Calling into Qt directly from a signal handler is not safe, | 1504 | // Calling into Qt directly from a signal handler is not safe, |
| @@ -1547,8 +1550,9 @@ void GMainWindow::AllowOSSleep() { | |||
| 1547 | 1550 | ||
| 1548 | bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t program_index) { | 1551 | bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t program_index) { |
| 1549 | // Shutdown previous session if the emu thread is still active... | 1552 | // Shutdown previous session if the emu thread is still active... |
| 1550 | if (emu_thread != nullptr) | 1553 | if (emu_thread != nullptr) { |
| 1551 | ShutdownGame(); | 1554 | ShutdownGame(); |
| 1555 | } | ||
| 1552 | 1556 | ||
| 1553 | if (!render_window->InitRenderTarget()) { | 1557 | if (!render_window->InitRenderTarget()) { |
| 1554 | return false; | 1558 | return false; |
| @@ -1707,8 +1711,10 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t | |||
| 1707 | system->RegisterExecuteProgramCallback( | 1711 | system->RegisterExecuteProgramCallback( |
| 1708 | [this](std::size_t program_index_) { render_window->ExecuteProgram(program_index_); }); | 1712 | [this](std::size_t program_index_) { render_window->ExecuteProgram(program_index_); }); |
| 1709 | 1713 | ||
| 1710 | // Register an Exit callback such that Core can exit the currently running application. | 1714 | system->RegisterExitCallback([this] { |
| 1711 | system->RegisterExitCallback([this]() { render_window->Exit(); }); | 1715 | emu_thread->ForceStop(); |
| 1716 | render_window->Exit(); | ||
| 1717 | }); | ||
| 1712 | 1718 | ||
| 1713 | connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); | 1719 | connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); |
| 1714 | connect(render_window, &GRenderWindow::MouseActivity, this, &GMainWindow::OnMouseActivity); | 1720 | connect(render_window, &GRenderWindow::MouseActivity, this, &GMainWindow::OnMouseActivity); |
| @@ -1779,9 +1785,9 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t | |||
| 1779 | OnStartGame(); | 1785 | OnStartGame(); |
| 1780 | } | 1786 | } |
| 1781 | 1787 | ||
| 1782 | void GMainWindow::ShutdownGame() { | 1788 | bool GMainWindow::OnShutdownBegin() { |
| 1783 | if (!emulation_running) { | 1789 | if (!emulation_running) { |
| 1784 | return; | 1790 | return false; |
| 1785 | } | 1791 | } |
| 1786 | 1792 | ||
| 1787 | if (ui->action_Fullscreen->isChecked()) { | 1793 | if (ui->action_Fullscreen->isChecked()) { |
| @@ -1790,17 +1796,58 @@ void GMainWindow::ShutdownGame() { | |||
| 1790 | 1796 | ||
| 1791 | AllowOSSleep(); | 1797 | AllowOSSleep(); |
| 1792 | 1798 | ||
| 1799 | // Disable unlimited frame rate | ||
| 1800 | Settings::values.use_speed_limit.SetValue(true); | ||
| 1801 | |||
| 1802 | if (system->IsShuttingDown()) { | ||
| 1803 | return false; | ||
| 1804 | } | ||
| 1805 | |||
| 1793 | system->SetShuttingDown(true); | 1806 | system->SetShuttingDown(true); |
| 1794 | system->DetachDebugger(); | ||
| 1795 | discord_rpc->Pause(); | 1807 | discord_rpc->Pause(); |
| 1796 | emu_thread->RequestStop(); | 1808 | |
| 1809 | RequestGameExit(); | ||
| 1810 | emu_thread->disconnect(); | ||
| 1811 | emu_thread->SetRunning(true); | ||
| 1797 | 1812 | ||
| 1798 | emit EmulationStopping(); | 1813 | emit EmulationStopping(); |
| 1799 | 1814 | ||
| 1800 | // Wait for emulation thread to complete and delete it | 1815 | shutdown_timer.setSingleShot(true); |
| 1816 | shutdown_timer.start(system->DebuggerEnabled() ? 0 : 5000); | ||
| 1817 | connect(&shutdown_timer, &QTimer::timeout, this, &GMainWindow::OnEmulationStopTimeExpired); | ||
| 1818 | connect(emu_thread.get(), &QThread::finished, this, &GMainWindow::OnEmulationStopped); | ||
| 1819 | |||
| 1820 | // Disable everything to prevent anything from being triggered here | ||
| 1821 | ui->action_Pause->setEnabled(false); | ||
| 1822 | ui->action_Restart->setEnabled(false); | ||
| 1823 | ui->action_Stop->setEnabled(false); | ||
| 1824 | |||
| 1825 | return true; | ||
| 1826 | } | ||
| 1827 | |||
| 1828 | void GMainWindow::OnShutdownBeginDialog() { | ||
| 1829 | shutdown_dialog = new OverlayDialog(this, *system, QString{}, tr("Closing software..."), | ||
| 1830 | QString{}, QString{}, Qt::AlignHCenter | Qt::AlignVCenter); | ||
| 1831 | shutdown_dialog->open(); | ||
| 1832 | } | ||
| 1833 | |||
| 1834 | void GMainWindow::OnEmulationStopTimeExpired() { | ||
| 1835 | if (emu_thread) { | ||
| 1836 | emu_thread->ForceStop(); | ||
| 1837 | } | ||
| 1838 | } | ||
| 1839 | |||
| 1840 | void GMainWindow::OnEmulationStopped() { | ||
| 1841 | shutdown_timer.stop(); | ||
| 1842 | emu_thread->disconnect(); | ||
| 1801 | emu_thread->wait(); | 1843 | emu_thread->wait(); |
| 1802 | emu_thread = nullptr; | 1844 | emu_thread = nullptr; |
| 1803 | 1845 | ||
| 1846 | if (shutdown_dialog) { | ||
| 1847 | shutdown_dialog->deleteLater(); | ||
| 1848 | shutdown_dialog = nullptr; | ||
| 1849 | } | ||
| 1850 | |||
| 1804 | emulation_running = false; | 1851 | emulation_running = false; |
| 1805 | 1852 | ||
| 1806 | discord_rpc->Update(); | 1853 | discord_rpc->Update(); |
| @@ -1846,6 +1893,20 @@ void GMainWindow::ShutdownGame() { | |||
| 1846 | 1893 | ||
| 1847 | // When closing the game, destroy the GLWindow to clear the context after the game is closed | 1894 | // When closing the game, destroy the GLWindow to clear the context after the game is closed |
| 1848 | render_window->ReleaseRenderTarget(); | 1895 | render_window->ReleaseRenderTarget(); |
| 1896 | |||
| 1897 | Settings::RestoreGlobalState(system->IsPoweredOn()); | ||
| 1898 | system->HIDCore().ReloadInputDevices(); | ||
| 1899 | UpdateStatusButtons(); | ||
| 1900 | } | ||
| 1901 | |||
| 1902 | void GMainWindow::ShutdownGame() { | ||
| 1903 | if (!emulation_running) { | ||
| 1904 | return; | ||
| 1905 | } | ||
| 1906 | |||
| 1907 | OnShutdownBegin(); | ||
| 1908 | OnEmulationStopTimeExpired(); | ||
| 1909 | OnEmulationStopped(); | ||
| 1849 | } | 1910 | } |
| 1850 | 1911 | ||
| 1851 | void GMainWindow::StoreRecentFile(const QString& filename) { | 1912 | void GMainWindow::StoreRecentFile(const QString& filename) { |
| @@ -2375,6 +2436,152 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id, | |||
| 2375 | QDesktopServices::openUrl(QUrl(QStringLiteral("https://yuzu-emu.org/game/") + directory)); | 2436 | QDesktopServices::openUrl(QUrl(QStringLiteral("https://yuzu-emu.org/game/") + directory)); |
| 2376 | } | 2437 | } |
| 2377 | 2438 | ||
| 2439 | void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path, | ||
| 2440 | GameListShortcutTarget target) { | ||
| 2441 | // Get path to yuzu executable | ||
| 2442 | const QStringList args = QApplication::arguments(); | ||
| 2443 | std::filesystem::path yuzu_command = args[0].toStdString(); | ||
| 2444 | |||
| 2445 | #if defined(__linux__) || defined(__FreeBSD__) | ||
| 2446 | // If relative path, make it an absolute path | ||
| 2447 | if (yuzu_command.c_str()[0] == '.') { | ||
| 2448 | yuzu_command = Common::FS::GetCurrentDir() / yuzu_command; | ||
| 2449 | } | ||
| 2450 | |||
| 2451 | #if defined(__linux__) | ||
| 2452 | // Warn once if we are making a shortcut to a volatile AppImage | ||
| 2453 | const std::string appimage_ending = | ||
| 2454 | std::string(Common::g_scm_rev).substr(0, 9).append(".AppImage"); | ||
| 2455 | if (yuzu_command.string().ends_with(appimage_ending) && | ||
| 2456 | !UISettings::values.shortcut_already_warned) { | ||
| 2457 | if (QMessageBox::warning(this, tr("Create Shortcut"), | ||
| 2458 | tr("This will create a shortcut to the current AppImage. This may " | ||
| 2459 | "not work well if you update. Continue?"), | ||
| 2460 | QMessageBox::StandardButton::Ok | | ||
| 2461 | QMessageBox::StandardButton::Cancel) == | ||
| 2462 | QMessageBox::StandardButton::Cancel) { | ||
| 2463 | return; | ||
| 2464 | } | ||
| 2465 | UISettings::values.shortcut_already_warned = true; | ||
| 2466 | } | ||
| 2467 | #endif // __linux__ | ||
| 2468 | #endif // __linux__ || __FreeBSD__ | ||
| 2469 | |||
| 2470 | std::filesystem::path target_directory{}; | ||
| 2471 | // Determine target directory for shortcut | ||
| 2472 | #if defined(__linux__) || defined(__FreeBSD__) | ||
| 2473 | const char* home = std::getenv("HOME"); | ||
| 2474 | const std::filesystem::path home_path = (home == nullptr ? "~" : home); | ||
| 2475 | const char* xdg_data_home = std::getenv("XDG_DATA_HOME"); | ||
| 2476 | |||
| 2477 | if (target == GameListShortcutTarget::Desktop) { | ||
| 2478 | target_directory = home_path / "Desktop"; | ||
| 2479 | if (!Common::FS::IsDir(target_directory)) { | ||
| 2480 | QMessageBox::critical( | ||
| 2481 | this, tr("Create Shortcut"), | ||
| 2482 | tr("Cannot create shortcut on desktop. Path \"%1\" does not exist.") | ||
| 2483 | .arg(QString::fromStdString(target_directory)), | ||
| 2484 | QMessageBox::StandardButton::Ok); | ||
| 2485 | return; | ||
| 2486 | } | ||
| 2487 | } else if (target == GameListShortcutTarget::Applications) { | ||
| 2488 | target_directory = (xdg_data_home == nullptr ? home_path / ".local/share" : xdg_data_home) / | ||
| 2489 | "applications"; | ||
| 2490 | if (!Common::FS::CreateDirs(target_directory)) { | ||
| 2491 | QMessageBox::critical(this, tr("Create Shortcut"), | ||
| 2492 | tr("Cannot create shortcut in applications menu. Path \"%1\" " | ||
| 2493 | "does not exist and cannot be created.") | ||
| 2494 | .arg(QString::fromStdString(target_directory)), | ||
| 2495 | QMessageBox::StandardButton::Ok); | ||
| 2496 | return; | ||
| 2497 | } | ||
| 2498 | } | ||
| 2499 | #endif | ||
| 2500 | |||
| 2501 | const std::string game_file_name = std::filesystem::path(game_path).filename().string(); | ||
| 2502 | // Determine full paths for icon and shortcut | ||
| 2503 | #if defined(__linux__) || defined(__FreeBSD__) | ||
| 2504 | std::filesystem::path system_icons_path = | ||
| 2505 | (xdg_data_home == nullptr ? home_path / ".local/share/" : xdg_data_home) / | ||
| 2506 | "icons/hicolor/256x256"; | ||
| 2507 | if (!Common::FS::CreateDirs(system_icons_path)) { | ||
| 2508 | QMessageBox::critical( | ||
| 2509 | this, tr("Create Icon"), | ||
| 2510 | tr("Cannot create icon file. Path \"%1\" does not exist and cannot be created.") | ||
| 2511 | .arg(QString::fromStdString(system_icons_path)), | ||
| 2512 | QMessageBox::StandardButton::Ok); | ||
| 2513 | return; | ||
| 2514 | } | ||
| 2515 | std::filesystem::path icon_path = | ||
| 2516 | system_icons_path / (program_id == 0 ? fmt::format("yuzu-{}.png", game_file_name) | ||
| 2517 | : fmt::format("yuzu-{:016X}.png", program_id)); | ||
| 2518 | const std::filesystem::path shortcut_path = | ||
| 2519 | target_directory / (program_id == 0 ? fmt::format("yuzu-{}.desktop", game_file_name) | ||
| 2520 | : fmt::format("yuzu-{:016X}.desktop", program_id)); | ||
| 2521 | #else | ||
| 2522 | const std::filesystem::path icon_path{}; | ||
| 2523 | const std::filesystem::path shortcut_path{}; | ||
| 2524 | #endif | ||
| 2525 | |||
| 2526 | // Get title from game file | ||
| 2527 | const FileSys::PatchManager pm{program_id, system->GetFileSystemController(), | ||
| 2528 | system->GetContentProvider()}; | ||
| 2529 | const auto control = pm.GetControlMetadata(); | ||
| 2530 | const auto loader = Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read)); | ||
| 2531 | |||
| 2532 | std::string title{fmt::format("{:016X}", program_id)}; | ||
| 2533 | |||
| 2534 | if (control.first != nullptr) { | ||
| 2535 | title = control.first->GetApplicationName(); | ||
| 2536 | } else { | ||
| 2537 | loader->ReadTitle(title); | ||
| 2538 | } | ||
| 2539 | |||
| 2540 | // Get icon from game file | ||
| 2541 | std::vector<u8> icon_image_file{}; | ||
| 2542 | if (control.second != nullptr) { | ||
| 2543 | icon_image_file = control.second->ReadAllBytes(); | ||
| 2544 | } else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) { | ||
| 2545 | LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path); | ||
| 2546 | } | ||
| 2547 | |||
| 2548 | QImage icon_jpeg = | ||
| 2549 | QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size())); | ||
| 2550 | #if defined(__linux__) || defined(__FreeBSD__) | ||
| 2551 | // Convert and write the icon as a PNG | ||
| 2552 | if (!icon_jpeg.save(QString::fromStdString(icon_path.string()))) { | ||
| 2553 | LOG_ERROR(Frontend, "Could not write icon as PNG to file"); | ||
| 2554 | } else { | ||
| 2555 | LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string()); | ||
| 2556 | } | ||
| 2557 | #endif // __linux__ | ||
| 2558 | |||
| 2559 | #if defined(__linux__) || defined(__FreeBSD__) | ||
| 2560 | const std::string comment = | ||
| 2561 | tr("Start %1 with the yuzu Emulator").arg(QString::fromStdString(title)).toStdString(); | ||
| 2562 | const std::string arguments = fmt::format("-g \"{:s}\"", game_path); | ||
| 2563 | const std::string categories = "Game;Emulator;Qt;"; | ||
| 2564 | const std::string keywords = "Switch;Nintendo;"; | ||
| 2565 | #else | ||
| 2566 | const std::string comment{}; | ||
| 2567 | const std::string arguments{}; | ||
| 2568 | const std::string categories{}; | ||
| 2569 | const std::string keywords{}; | ||
| 2570 | #endif | ||
| 2571 | if (!CreateShortcut(shortcut_path.string(), title, comment, icon_path.string(), | ||
| 2572 | yuzu_command.string(), arguments, categories, keywords)) { | ||
| 2573 | QMessageBox::critical(this, tr("Create Shortcut"), | ||
| 2574 | tr("Failed to create a shortcut at %1") | ||
| 2575 | .arg(QString::fromStdString(shortcut_path.string()))); | ||
| 2576 | return; | ||
| 2577 | } | ||
| 2578 | |||
| 2579 | LOG_INFO(Frontend, "Wrote a shortcut to {}", shortcut_path.string()); | ||
| 2580 | QMessageBox::information( | ||
| 2581 | this, tr("Create Shortcut"), | ||
| 2582 | tr("Successfully created a shortcut to %1").arg(QString::fromStdString(title))); | ||
| 2583 | } | ||
| 2584 | |||
| 2378 | void GMainWindow::OnGameListOpenDirectory(const QString& directory) { | 2585 | void GMainWindow::OnGameListOpenDirectory(const QString& directory) { |
| 2379 | std::filesystem::path fs_path; | 2586 | std::filesystem::path fs_path; |
| 2380 | if (directory == QStringLiteral("SDMC")) { | 2587 | if (directory == QStringLiteral("SDMC")) { |
| @@ -2508,6 +2715,9 @@ void GMainWindow::OnMenuInstallToNAND() { | |||
| 2508 | return; | 2715 | return; |
| 2509 | } | 2716 | } |
| 2510 | 2717 | ||
| 2718 | // Save folder location of the first selected file | ||
| 2719 | UISettings::values.roms_path = QFileInfo(filenames[0]).path(); | ||
| 2720 | |||
| 2511 | int remaining = filenames.size(); | 2721 | int remaining = filenames.size(); |
| 2512 | 2722 | ||
| 2513 | // This would only overflow above 2^43 bytes (8.796 TB) | 2723 | // This would only overflow above 2^43 bytes (8.796 TB) |
| @@ -2763,8 +2973,6 @@ void GMainWindow::OnStartGame() { | |||
| 2763 | 2973 | ||
| 2764 | emu_thread->SetRunning(true); | 2974 | emu_thread->SetRunning(true); |
| 2765 | 2975 | ||
| 2766 | connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError); | ||
| 2767 | |||
| 2768 | UpdateMenuState(); | 2976 | UpdateMenuState(); |
| 2769 | OnTasStateChanged(); | 2977 | OnTasStateChanged(); |
| 2770 | 2978 | ||
| @@ -2801,11 +3009,9 @@ void GMainWindow::OnStopGame() { | |||
| 2801 | return; | 3009 | return; |
| 2802 | } | 3010 | } |
| 2803 | 3011 | ||
| 2804 | ShutdownGame(); | 3012 | if (OnShutdownBegin()) { |
| 2805 | 3013 | OnShutdownBeginDialog(); | |
| 2806 | Settings::RestoreGlobalState(system->IsPoweredOn()); | 3014 | } |
| 2807 | system->HIDCore().ReloadInputDevices(); | ||
| 2808 | UpdateStatusButtons(); | ||
| 2809 | } | 3015 | } |
| 2810 | 3016 | ||
| 2811 | void GMainWindow::OnLoadComplete() { | 3017 | void GMainWindow::OnLoadComplete() { |
| @@ -2912,9 +3118,15 @@ static QScreen* GuessCurrentScreen(QWidget* window) { | |||
| 2912 | }); | 3118 | }); |
| 2913 | } | 3119 | } |
| 2914 | 3120 | ||
| 3121 | bool GMainWindow::UsingExclusiveFullscreen() { | ||
| 3122 | return Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive || | ||
| 3123 | QGuiApplication::platformName() == QStringLiteral("wayland") || | ||
| 3124 | QGuiApplication::platformName() == QStringLiteral("wayland-egl"); | ||
| 3125 | } | ||
| 3126 | |||
| 2915 | void GMainWindow::ShowFullscreen() { | 3127 | void GMainWindow::ShowFullscreen() { |
| 2916 | const auto show_fullscreen = [](QWidget* window) { | 3128 | const auto show_fullscreen = [this](QWidget* window) { |
| 2917 | if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) { | 3129 | if (UsingExclusiveFullscreen()) { |
| 2918 | window->showFullScreen(); | 3130 | window->showFullScreen(); |
| 2919 | return; | 3131 | return; |
| 2920 | } | 3132 | } |
| @@ -2942,7 +3154,7 @@ void GMainWindow::ShowFullscreen() { | |||
| 2942 | 3154 | ||
| 2943 | void GMainWindow::HideFullscreen() { | 3155 | void GMainWindow::HideFullscreen() { |
| 2944 | if (ui->action_Single_Window_Mode->isChecked()) { | 3156 | if (ui->action_Single_Window_Mode->isChecked()) { |
| 2945 | if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) { | 3157 | if (UsingExclusiveFullscreen()) { |
| 2946 | showNormal(); | 3158 | showNormal(); |
| 2947 | restoreGeometry(UISettings::values.geometry); | 3159 | restoreGeometry(UISettings::values.geometry); |
| 2948 | } else { | 3160 | } else { |
| @@ -2956,7 +3168,7 @@ void GMainWindow::HideFullscreen() { | |||
| 2956 | statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked()); | 3168 | statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked()); |
| 2957 | ui->menubar->show(); | 3169 | ui->menubar->show(); |
| 2958 | } else { | 3170 | } else { |
| 2959 | if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) { | 3171 | if (UsingExclusiveFullscreen()) { |
| 2960 | render_window->showNormal(); | 3172 | render_window->showNormal(); |
| 2961 | render_window->restoreGeometry(UISettings::values.renderwindow_geometry); | 3173 | render_window->restoreGeometry(UISettings::values.renderwindow_geometry); |
| 2962 | } else { | 3174 | } else { |
| @@ -3293,6 +3505,38 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file | |||
| 3293 | } | 3505 | } |
| 3294 | } | 3506 | } |
| 3295 | 3507 | ||
| 3508 | bool GMainWindow::CreateShortcut(const std::string& shortcut_path, const std::string& title, | ||
| 3509 | const std::string& comment, const std::string& icon_path, | ||
| 3510 | const std::string& command, const std::string& arguments, | ||
| 3511 | const std::string& categories, const std::string& keywords) { | ||
| 3512 | #if defined(__linux__) || defined(__FreeBSD__) | ||
| 3513 | // This desktop file template was writting referencing | ||
| 3514 | // https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.0.html | ||
| 3515 | std::string shortcut_contents{}; | ||
| 3516 | shortcut_contents.append("[Desktop Entry]\n"); | ||
| 3517 | shortcut_contents.append("Type=Application\n"); | ||
| 3518 | shortcut_contents.append("Version=1.0\n"); | ||
| 3519 | shortcut_contents.append(fmt::format("Name={:s}\n", title)); | ||
| 3520 | shortcut_contents.append(fmt::format("Comment={:s}\n", comment)); | ||
| 3521 | shortcut_contents.append(fmt::format("Icon={:s}\n", icon_path)); | ||
| 3522 | shortcut_contents.append(fmt::format("TryExec={:s}\n", command)); | ||
| 3523 | shortcut_contents.append(fmt::format("Exec={:s} {:s}\n", command, arguments)); | ||
| 3524 | shortcut_contents.append(fmt::format("Categories={:s}\n", categories)); | ||
| 3525 | shortcut_contents.append(fmt::format("Keywords={:s}\n", keywords)); | ||
| 3526 | |||
| 3527 | std::ofstream shortcut_stream(shortcut_path); | ||
| 3528 | if (!shortcut_stream.is_open()) { | ||
| 3529 | LOG_WARNING(Common, "Failed to create file {:s}", shortcut_path); | ||
| 3530 | return false; | ||
| 3531 | } | ||
| 3532 | shortcut_stream << shortcut_contents; | ||
| 3533 | shortcut_stream.close(); | ||
| 3534 | |||
| 3535 | return true; | ||
| 3536 | #endif | ||
| 3537 | return false; | ||
| 3538 | } | ||
| 3539 | |||
| 3296 | void GMainWindow::OnLoadAmiibo() { | 3540 | void GMainWindow::OnLoadAmiibo() { |
| 3297 | if (emu_thread == nullptr || !emu_thread->IsRunning()) { | 3541 | if (emu_thread == nullptr || !emu_thread->IsRunning()) { |
| 3298 | return; | 3542 | return; |
| @@ -3710,79 +3954,6 @@ void GMainWindow::OnMouseActivity() { | |||
| 3710 | mouse_center_timer.stop(); | 3954 | mouse_center_timer.stop(); |
| 3711 | } | 3955 | } |
| 3712 | 3956 | ||
| 3713 | void GMainWindow::OnCoreError(Core::SystemResultStatus result, std::string details) { | ||
| 3714 | QMessageBox::StandardButton answer; | ||
| 3715 | QString status_message; | ||
| 3716 | const QString common_message = | ||
| 3717 | tr("The game you are trying to load requires additional files from your Switch to be " | ||
| 3718 | "dumped " | ||
| 3719 | "before playing.<br/><br/>For more information on dumping these files, please see the " | ||
| 3720 | "following wiki page: <a " | ||
| 3721 | "href='https://yuzu-emu.org/wiki/" | ||
| 3722 | "dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System " | ||
| 3723 | "Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to " | ||
| 3724 | "quit " | ||
| 3725 | "back to the game list? Continuing emulation may result in crashes, corrupted save " | ||
| 3726 | "data, or other bugs."); | ||
| 3727 | switch (result) { | ||
| 3728 | case Core::SystemResultStatus::ErrorSystemFiles: { | ||
| 3729 | QString message; | ||
| 3730 | if (details.empty()) { | ||
| 3731 | message = | ||
| 3732 | tr("yuzu was unable to locate a Switch system archive. %1").arg(common_message); | ||
| 3733 | } else { | ||
| 3734 | message = tr("yuzu was unable to locate a Switch system archive: %1. %2") | ||
| 3735 | .arg(QString::fromStdString(details), common_message); | ||
| 3736 | } | ||
| 3737 | |||
| 3738 | answer = QMessageBox::question(this, tr("System Archive Not Found"), message, | ||
| 3739 | QMessageBox::Yes | QMessageBox::No, QMessageBox::No); | ||
| 3740 | status_message = tr("System Archive Missing"); | ||
| 3741 | break; | ||
| 3742 | } | ||
| 3743 | |||
| 3744 | case Core::SystemResultStatus::ErrorSharedFont: { | ||
| 3745 | const QString message = | ||
| 3746 | tr("yuzu was unable to locate the Switch shared fonts. %1").arg(common_message); | ||
| 3747 | answer = QMessageBox::question(this, tr("Shared Fonts Not Found"), message, | ||
| 3748 | QMessageBox::Yes | QMessageBox::No, QMessageBox::No); | ||
| 3749 | status_message = tr("Shared Font Missing"); | ||
| 3750 | break; | ||
| 3751 | } | ||
| 3752 | |||
| 3753 | default: | ||
| 3754 | answer = QMessageBox::question( | ||
| 3755 | this, tr("Fatal Error"), | ||
| 3756 | tr("yuzu has encountered a fatal error, please see the log for more details. " | ||
| 3757 | "For more information on accessing the log, please see the following page: " | ||
| 3758 | "<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How " | ||
| 3759 | "to " | ||
| 3760 | "Upload the Log File</a>.<br/><br/>Would you like to quit back to the game " | ||
| 3761 | "list? " | ||
| 3762 | "Continuing emulation may result in crashes, corrupted save data, or other " | ||
| 3763 | "bugs."), | ||
| 3764 | QMessageBox::Yes | QMessageBox::No, QMessageBox::No); | ||
| 3765 | status_message = tr("Fatal Error encountered"); | ||
| 3766 | break; | ||
| 3767 | } | ||
| 3768 | |||
| 3769 | if (answer == QMessageBox::Yes) { | ||
| 3770 | if (emu_thread) { | ||
| 3771 | ShutdownGame(); | ||
| 3772 | |||
| 3773 | Settings::RestoreGlobalState(system->IsPoweredOn()); | ||
| 3774 | system->HIDCore().ReloadInputDevices(); | ||
| 3775 | UpdateStatusButtons(); | ||
| 3776 | } | ||
| 3777 | } else { | ||
| 3778 | // Only show the message if the game is still running. | ||
| 3779 | if (emu_thread) { | ||
| 3780 | emu_thread->SetRunning(true); | ||
| 3781 | message_label->setText(status_message); | ||
| 3782 | } | ||
| 3783 | } | ||
| 3784 | } | ||
| 3785 | |||
| 3786 | void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { | 3957 | void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { |
| 3787 | if (behavior == ReinitializeKeyBehavior::Warning) { | 3958 | if (behavior == ReinitializeKeyBehavior::Warning) { |
| 3788 | const auto res = QMessageBox::information( | 3959 | const auto res = QMessageBox::information( |
| @@ -3927,10 +4098,6 @@ void GMainWindow::closeEvent(QCloseEvent* event) { | |||
| 3927 | // Shutdown session if the emu thread is active... | 4098 | // Shutdown session if the emu thread is active... |
| 3928 | if (emu_thread != nullptr) { | 4099 | if (emu_thread != nullptr) { |
| 3929 | ShutdownGame(); | 4100 | ShutdownGame(); |
| 3930 | |||
| 3931 | Settings::RestoreGlobalState(system->IsPoweredOn()); | ||
| 3932 | system->HIDCore().ReloadInputDevices(); | ||
| 3933 | UpdateStatusButtons(); | ||
| 3934 | } | 4101 | } |
| 3935 | 4102 | ||
| 3936 | render_window->close(); | 4103 | render_window->close(); |
| @@ -4023,6 +4190,10 @@ bool GMainWindow::ConfirmForceLockedExit() { | |||
| 4023 | } | 4190 | } |
| 4024 | 4191 | ||
| 4025 | void GMainWindow::RequestGameExit() { | 4192 | void GMainWindow::RequestGameExit() { |
| 4193 | if (!system->IsPoweredOn()) { | ||
| 4194 | return; | ||
| 4195 | } | ||
| 4196 | |||
| 4026 | auto& sm{system->ServiceManager()}; | 4197 | auto& sm{system->ServiceManager()}; |
| 4027 | auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE"); | 4198 | auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE"); |
| 4028 | auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE"); | 4199 | auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE"); |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 62d629973..db318485d 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -29,6 +29,7 @@ class GImageInfo; | |||
| 29 | class GRenderWindow; | 29 | class GRenderWindow; |
| 30 | class LoadingScreen; | 30 | class LoadingScreen; |
| 31 | class MicroProfileDialog; | 31 | class MicroProfileDialog; |
| 32 | class OverlayDialog; | ||
| 32 | class ProfilerWidget; | 33 | class ProfilerWidget; |
| 33 | class ControllerDialog; | 34 | class ControllerDialog; |
| 34 | class QLabel; | 35 | class QLabel; |
| @@ -38,6 +39,7 @@ class QProgressDialog; | |||
| 38 | class WaitTreeWidget; | 39 | class WaitTreeWidget; |
| 39 | enum class GameListOpenTarget; | 40 | enum class GameListOpenTarget; |
| 40 | enum class GameListRemoveTarget; | 41 | enum class GameListRemoveTarget; |
| 42 | enum class GameListShortcutTarget; | ||
| 41 | enum class DumpRomFSTarget; | 43 | enum class DumpRomFSTarget; |
| 42 | enum class InstalledEntryType; | 44 | enum class InstalledEntryType; |
| 43 | class GameListPlaceholder; | 45 | class GameListPlaceholder; |
| @@ -293,6 +295,8 @@ private slots: | |||
| 293 | void OnGameListCopyTID(u64 program_id); | 295 | void OnGameListCopyTID(u64 program_id); |
| 294 | void OnGameListNavigateToGamedbEntry(u64 program_id, | 296 | void OnGameListNavigateToGamedbEntry(u64 program_id, |
| 295 | const CompatibilityList& compatibility_list); | 297 | const CompatibilityList& compatibility_list); |
| 298 | void OnGameListCreateShortcut(u64 program_id, const std::string& game_path, | ||
| 299 | GameListShortcutTarget target); | ||
| 296 | void OnGameListOpenDirectory(const QString& directory); | 300 | void OnGameListOpenDirectory(const QString& directory); |
| 297 | void OnGameListAddDirectory(); | 301 | void OnGameListAddDirectory(); |
| 298 | void OnGameListShowList(bool show); | 302 | void OnGameListShowList(bool show); |
| @@ -320,6 +324,7 @@ private slots: | |||
| 320 | void OnDisplayTitleBars(bool); | 324 | void OnDisplayTitleBars(bool); |
| 321 | void InitializeHotkeys(); | 325 | void InitializeHotkeys(); |
| 322 | void ToggleFullscreen(); | 326 | void ToggleFullscreen(); |
| 327 | bool UsingExclusiveFullscreen(); | ||
| 323 | void ShowFullscreen(); | 328 | void ShowFullscreen(); |
| 324 | void HideFullscreen(); | 329 | void HideFullscreen(); |
| 325 | void ToggleWindowMode(); | 330 | void ToggleWindowMode(); |
| @@ -328,10 +333,13 @@ private slots: | |||
| 328 | void ResetWindowSize900(); | 333 | void ResetWindowSize900(); |
| 329 | void ResetWindowSize1080(); | 334 | void ResetWindowSize1080(); |
| 330 | void OnCaptureScreenshot(); | 335 | void OnCaptureScreenshot(); |
| 331 | void OnCoreError(Core::SystemResultStatus, std::string); | ||
| 332 | void OnReinitializeKeys(ReinitializeKeyBehavior behavior); | 336 | void OnReinitializeKeys(ReinitializeKeyBehavior behavior); |
| 333 | void OnLanguageChanged(const QString& locale); | 337 | void OnLanguageChanged(const QString& locale); |
| 334 | void OnMouseActivity(); | 338 | void OnMouseActivity(); |
| 339 | bool OnShutdownBegin(); | ||
| 340 | void OnShutdownBeginDialog(); | ||
| 341 | void OnEmulationStopped(); | ||
| 342 | void OnEmulationStopTimeExpired(); | ||
| 335 | 343 | ||
| 336 | private: | 344 | private: |
| 337 | QString GetGameListErrorRemoving(InstalledEntryType type) const; | 345 | QString GetGameListErrorRemoving(InstalledEntryType type) const; |
| @@ -365,6 +373,10 @@ private: | |||
| 365 | bool CheckDarkMode(); | 373 | bool CheckDarkMode(); |
| 366 | 374 | ||
| 367 | QString GetTasStateDescription() const; | 375 | QString GetTasStateDescription() const; |
| 376 | bool CreateShortcut(const std::string& shortcut_path, const std::string& title, | ||
| 377 | const std::string& comment, const std::string& icon_path, | ||
| 378 | const std::string& command, const std::string& arguments, | ||
| 379 | const std::string& categories, const std::string& keywords); | ||
| 368 | 380 | ||
| 369 | std::unique_ptr<Ui::MainWindow> ui; | 381 | std::unique_ptr<Ui::MainWindow> ui; |
| 370 | 382 | ||
| @@ -377,6 +389,8 @@ private: | |||
| 377 | GRenderWindow* render_window; | 389 | GRenderWindow* render_window; |
| 378 | GameList* game_list; | 390 | GameList* game_list; |
| 379 | LoadingScreen* loading_screen; | 391 | LoadingScreen* loading_screen; |
| 392 | QTimer shutdown_timer; | ||
| 393 | OverlayDialog* shutdown_dialog{}; | ||
| 380 | 394 | ||
| 381 | GameListPlaceholder* game_list_placeholder; | 395 | GameListPlaceholder* game_list_placeholder; |
| 382 | 396 | ||
diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp index 563818362..9f702fe95 100644 --- a/src/yuzu/startup_checks.cpp +++ b/src/yuzu/startup_checks.cpp | |||
| @@ -186,7 +186,7 @@ pid_t SpawnChild(const char* arg0) { | |||
| 186 | return pid; | 186 | return pid; |
| 187 | } else if (pid == 0) { | 187 | } else if (pid == 0) { |
| 188 | // child | 188 | // child |
| 189 | execl(arg0, arg0, nullptr); | 189 | execlp(arg0, arg0, nullptr); |
| 190 | const int err = errno; | 190 | const int err = errno; |
| 191 | fmt::print(stderr, "execl failed with error {}\n", err); | 191 | fmt::print(stderr, "execl failed with error {}\n", err); |
| 192 | _exit(0); | 192 | _exit(0); |
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index 452038cd9..2006b883e 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h | |||
| @@ -138,6 +138,7 @@ struct Values { | |||
| 138 | 138 | ||
| 139 | bool configuration_applied; | 139 | bool configuration_applied; |
| 140 | bool reset_to_defaults; | 140 | bool reset_to_defaults; |
| 141 | bool shortcut_already_warned{false}; | ||
| 141 | Settings::Setting<bool> disable_web_applet{true, "disable_web_applet"}; | 142 | Settings::Setting<bool> disable_web_applet{true, "disable_web_applet"}; |
| 142 | }; | 143 | }; |
| 143 | 144 | ||
diff --git a/src/yuzu/util/overlay_dialog.cpp b/src/yuzu/util/overlay_dialog.cpp index b27954512..796f5bf41 100644 --- a/src/yuzu/util/overlay_dialog.cpp +++ b/src/yuzu/util/overlay_dialog.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include <QKeyEvent> | 4 | #include <QKeyEvent> |
| 5 | #include <QScreen> | 5 | #include <QScreen> |
| 6 | #include <QWindow> | ||
| 6 | 7 | ||
| 7 | #include "core/core.h" | 8 | #include "core/core.h" |
| 8 | #include "core/hid/hid_types.h" | 9 | #include "core/hid/hid_types.h" |
| @@ -42,7 +43,7 @@ OverlayDialog::OverlayDialog(QWidget* parent, Core::System& system, const QStrin | |||
| 42 | MoveAndResizeWindow(); | 43 | MoveAndResizeWindow(); |
| 43 | 44 | ||
| 44 | // TODO (Morph): Remove this when InputInterpreter no longer relies on the HID backend | 45 | // TODO (Morph): Remove this when InputInterpreter no longer relies on the HID backend |
| 45 | if (system.IsPoweredOn()) { | 46 | if (system.IsPoweredOn() && !ui->buttonsDialog->isHidden()) { |
| 46 | input_interpreter = std::make_unique<InputInterpreter>(system); | 47 | input_interpreter = std::make_unique<InputInterpreter>(system); |
| 47 | 48 | ||
| 48 | StartInputThread(); | 49 | StartInputThread(); |
| @@ -83,6 +84,11 @@ void OverlayDialog::InitializeRegularTextDialog(const QString& title_text, const | |||
| 83 | ui->button_ok_label->setEnabled(false); | 84 | ui->button_ok_label->setEnabled(false); |
| 84 | } | 85 | } |
| 85 | 86 | ||
| 87 | if (ui->button_cancel->isHidden() && ui->button_ok_label->isHidden()) { | ||
| 88 | ui->buttonsDialog->hide(); | ||
| 89 | return; | ||
| 90 | } | ||
| 91 | |||
| 86 | connect( | 92 | connect( |
| 87 | ui->button_cancel, &QPushButton::clicked, this, | 93 | ui->button_cancel, &QPushButton::clicked, this, |
| 88 | [this](bool) { | 94 | [this](bool) { |
| @@ -130,6 +136,11 @@ void OverlayDialog::InitializeRichTextDialog(const QString& title_text, const QS | |||
| 130 | ui->button_ok_rich->setEnabled(false); | 136 | ui->button_ok_rich->setEnabled(false); |
| 131 | } | 137 | } |
| 132 | 138 | ||
| 139 | if (ui->button_cancel_rich->isHidden() && ui->button_ok_rich->isHidden()) { | ||
| 140 | ui->buttonsRichDialog->hide(); | ||
| 141 | return; | ||
| 142 | } | ||
| 143 | |||
| 133 | connect( | 144 | connect( |
| 134 | ui->button_cancel_rich, &QPushButton::clicked, this, | 145 | ui->button_cancel_rich, &QPushButton::clicked, this, |
| 135 | [this](bool) { | 146 | [this](bool) { |
| @@ -152,7 +163,7 @@ void OverlayDialog::MoveAndResizeWindow() { | |||
| 152 | const auto height = static_cast<float>(parentWidget()->height()); | 163 | const auto height = static_cast<float>(parentWidget()->height()); |
| 153 | 164 | ||
| 154 | // High DPI | 165 | // High DPI |
| 155 | const float dpi_scale = qApp->screenAt(pos)->logicalDotsPerInch() / 96.0f; | 166 | const float dpi_scale = parentWidget()->windowHandle()->screen()->logicalDotsPerInch() / 96.0f; |
| 156 | 167 | ||
| 157 | const auto title_text_font_size = BASE_TITLE_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale; | 168 | const auto title_text_font_size = BASE_TITLE_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale; |
| 158 | const auto body_text_font_size = | 169 | const auto body_text_font_size = |
| @@ -249,3 +260,9 @@ void OverlayDialog::InputThread() { | |||
| 249 | std::this_thread::sleep_for(std::chrono::milliseconds(50)); | 260 | std::this_thread::sleep_for(std::chrono::milliseconds(50)); |
| 250 | } | 261 | } |
| 251 | } | 262 | } |
| 263 | |||
| 264 | void OverlayDialog::keyPressEvent(QKeyEvent* e) { | ||
| 265 | if (!ui->buttonsDialog->isHidden() || e->key() != Qt::Key_Escape) { | ||
| 266 | QDialog::keyPressEvent(e); | ||
| 267 | } | ||
| 268 | } | ||
diff --git a/src/yuzu/util/overlay_dialog.h b/src/yuzu/util/overlay_dialog.h index 39c44393c..872283d61 100644 --- a/src/yuzu/util/overlay_dialog.h +++ b/src/yuzu/util/overlay_dialog.h | |||
| @@ -94,6 +94,7 @@ private: | |||
| 94 | 94 | ||
| 95 | /// The thread where input is being polled and processed. | 95 | /// The thread where input is being polled and processed. |
| 96 | void InputThread(); | 96 | void InputThread(); |
| 97 | void keyPressEvent(QKeyEvent* e) override; | ||
| 97 | 98 | ||
| 98 | std::unique_ptr<Ui::OverlayDialog> ui; | 99 | std::unique_ptr<Ui::OverlayDialog> ui; |
| 99 | 100 | ||
diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt index f6eeb9d8d..61b6cc4e0 100644 --- a/src/yuzu_cmd/CMakeLists.txt +++ b/src/yuzu_cmd/CMakeLists.txt | |||
| @@ -49,6 +49,15 @@ if(UNIX AND NOT APPLE) | |||
| 49 | install(TARGETS yuzu-cmd) | 49 | install(TARGETS yuzu-cmd) |
| 50 | endif() | 50 | endif() |
| 51 | 51 | ||
| 52 | if(WIN32) | ||
| 53 | # compile as a win32 gui application instead of a console application | ||
| 54 | if(MSVC) | ||
| 55 | set_target_properties(yuzu-cmd PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup") | ||
| 56 | elseif(MINGW) | ||
| 57 | set_target_properties(yuzu-cmd PROPERTIES LINK_FLAGS_RELEASE "-Wl,--subsystem,windows") | ||
| 58 | endif() | ||
| 59 | endif() | ||
| 60 | |||
| 52 | if (MSVC) | 61 | if (MSVC) |
| 53 | include(CopyYuzuSDLDeps) | 62 | include(CopyYuzuSDLDeps) |
| 54 | copy_yuzu_SDL_deps(yuzu-cmd) | 63 | copy_yuzu_SDL_deps(yuzu-cmd) |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index 37dd1747c..31f28a507 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp | |||
| @@ -115,7 +115,7 @@ bool EmuWindow_SDL2::IsShown() const { | |||
| 115 | 115 | ||
| 116 | void EmuWindow_SDL2::OnResize() { | 116 | void EmuWindow_SDL2::OnResize() { |
| 117 | int width, height; | 117 | int width, height; |
| 118 | SDL_GetWindowSize(render_window, &width, &height); | 118 | SDL_GL_GetDrawableSize(render_window, &width, &height); |
| 119 | UpdateCurrentFramebufferLayout(width, height); | 119 | UpdateCurrentFramebufferLayout(width, height); |
| 120 | } | 120 | } |
| 121 | 121 | ||
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp index 9b660c13c..ddcb048d6 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp | |||
| @@ -104,6 +104,8 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsyste | |||
| 104 | exit(1); | 104 | exit(1); |
| 105 | } | 105 | } |
| 106 | 106 | ||
| 107 | strict_context_required = strcmp(SDL_GetCurrentVideoDriver(), "wayland") == 0; | ||
| 108 | |||
| 107 | SetWindowIcon(); | 109 | SetWindowIcon(); |
| 108 | 110 | ||
| 109 | if (fullscreen) { | 111 | if (fullscreen) { |
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index a80649703..91133569d 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -174,6 +174,13 @@ static void OnStatusMessageReceived(const Network::StatusMessageEntry& msg) { | |||
| 174 | 174 | ||
| 175 | /// Application entry point | 175 | /// Application entry point |
| 176 | int main(int argc, char** argv) { | 176 | int main(int argc, char** argv) { |
| 177 | #ifdef _WIN32 | ||
| 178 | if (AttachConsole(ATTACH_PARENT_PROCESS)) { | ||
| 179 | freopen("CONOUT$", "wb", stdout); | ||
| 180 | freopen("CONOUT$", "wb", stderr); | ||
| 181 | } | ||
| 182 | #endif | ||
| 183 | |||
| 177 | Common::Log::Initialize(); | 184 | Common::Log::Initialize(); |
| 178 | Common::Log::SetColorConsoleBackendEnabled(true); | 185 | Common::Log::SetColorConsoleBackendEnabled(true); |
| 179 | Common::Log::Start(); | 186 | Common::Log::Start(); |