summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/settings.h181
-rw-r--r--src/core/memory.cpp552
-rw-r--r--src/core/memory.h102
-rw-r--r--src/input_common/main.cpp8
-rw-r--r--src/input_common/mouse/mouse_poller.cpp1
-rw-r--r--src/input_common/sdl/sdl_impl.cpp87
-rw-r--r--src/video_core/CMakeLists.txt3
-rw-r--r--src/video_core/command_classes/codecs/vp9.cpp4
-rw-r--r--src/video_core/command_classes/codecs/vp9_types.h2
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp4
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h2
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache_base.cpp10
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache_base.cpp10
-rw-r--r--src/video_core/texture_cache/image_view_info.cpp4
-rw-r--r--src/video_core/texture_cache/texture_cache.h419
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h385
-rw-r--r--src/video_core/vulkan_common/vulkan_memory_allocator.cpp8
-rw-r--r--src/video_core/vulkan_common/vulkan_memory_allocator.h2
-rw-r--r--src/yuzu/configuration/configure_general.ui60
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.ui7
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp13
-rw-r--r--src/yuzu/main.cpp7
-rw-r--r--src/yuzu_cmd/CMakeLists.txt1
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp1
29 files changed, 862 insertions, 1021 deletions
diff --git a/src/common/settings.h b/src/common/settings.h
index a88ee045d..1ba9b606c 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <algorithm>
7#include <array> 8#include <array>
8#include <atomic> 9#include <atomic>
9#include <chrono> 10#include <chrono>
@@ -74,14 +75,14 @@ public:
74 */ 75 */
75 explicit BasicSetting(const Type& default_val, const std::string& name) 76 explicit BasicSetting(const Type& default_val, const std::string& name)
76 : default_value{default_val}, global{default_val}, label{name} {} 77 : default_value{default_val}, global{default_val}, label{name} {}
77 ~BasicSetting() = default; 78 virtual ~BasicSetting() = default;
78 79
79 /** 80 /**
80 * Returns a reference to the setting's value. 81 * Returns a reference to the setting's value.
81 * 82 *
82 * @returns A reference to the setting 83 * @returns A reference to the setting
83 */ 84 */
84 [[nodiscard]] const Type& GetValue() const { 85 [[nodiscard]] virtual const Type& GetValue() const {
85 return global; 86 return global;
86 } 87 }
87 88
@@ -90,7 +91,7 @@ public:
90 * 91 *
91 * @param value The desired value 92 * @param value The desired value
92 */ 93 */
93 void SetValue(const Type& value) { 94 virtual void SetValue(const Type& value) {
94 Type temp{value}; 95 Type temp{value};
95 std::swap(global, temp); 96 std::swap(global, temp);
96 } 97 }
@@ -120,7 +121,7 @@ public:
120 * 121 *
121 * @returns A reference to the setting 122 * @returns A reference to the setting
122 */ 123 */
123 const Type& operator=(const Type& value) { 124 virtual const Type& operator=(const Type& value) {
124 Type temp{value}; 125 Type temp{value};
125 std::swap(global, temp); 126 std::swap(global, temp);
126 return global; 127 return global;
@@ -131,7 +132,7 @@ public:
131 * 132 *
132 * @returns A reference to the setting 133 * @returns A reference to the setting
133 */ 134 */
134 explicit operator const Type&() const { 135 explicit virtual operator const Type&() const {
135 return global; 136 return global;
136 } 137 }
137 138
@@ -142,6 +143,51 @@ protected:
142}; 143};
143 144
144/** 145/**
146 * BasicRangedSetting class is intended for use with quantifiable settings that need a more
147 * restrictive range than implicitly defined by its type. Implements a minimum and maximum that is
148 * simply used to sanitize SetValue and the assignment overload.
149 */
150template <typename Type>
151class BasicRangedSetting : virtual public BasicSetting<Type> {
152public:
153 /**
154 * Sets a default value, minimum value, maximum value, and label.
155 *
156 * @param default_val Intial value of the setting, and default value of the setting
157 * @param min_val Sets the minimum allowed value of the setting
158 * @param max_val Sets the maximum allowed value of the setting
159 * @param name Label for the setting
160 */
161 explicit BasicRangedSetting(const Type& default_val, const Type& min_val, const Type& max_val,
162 const std::string& name)
163 : BasicSetting<Type>{default_val, name}, minimum{min_val}, maximum{max_val} {}
164 virtual ~BasicRangedSetting() = default;
165
166 /**
167 * Like BasicSetting's SetValue, except value is clamped to the range of the setting.
168 *
169 * @param value The desired value
170 */
171 void SetValue(const Type& value) override {
172 this->global = std::clamp(value, minimum, maximum);
173 }
174
175 /**
176 * Like BasicSetting's assignment overload, except value is clamped to the range of the setting.
177 *
178 * @param value The desired value
179 * @returns A reference to the setting's value
180 */
181 const Type& operator=(const Type& value) override {
182 this->global = std::clamp(value, minimum, maximum);
183 return this->global;
184 }
185
186 const Type minimum; ///< Minimum allowed value of the setting
187 const Type maximum; ///< Maximum allowed value of the setting
188};
189
190/**
145 * The Setting class is a slightly more complex version of the BasicSetting class. This adds a 191 * The Setting class is a slightly more complex version of the BasicSetting class. This adds a
146 * custom setting to switch to when a guest application specifically requires it. The effect is that 192 * custom setting to switch to when a guest application specifically requires it. The effect is that
147 * other components of the emulator can access the setting's intended value without any need for the 193 * other components of the emulator can access the setting's intended value without any need for the
@@ -152,7 +198,7 @@ protected:
152 * Like the BasicSetting, this requires setting a default value and label to use. 198 * Like the BasicSetting, this requires setting a default value and label to use.
153 */ 199 */
154template <typename Type> 200template <typename Type>
155class Setting final : public BasicSetting<Type> { 201class Setting : virtual public BasicSetting<Type> {
156public: 202public:
157 /** 203 /**
158 * Sets a default value, label, and setting value. 204 * Sets a default value, label, and setting value.
@@ -162,7 +208,7 @@ public:
162 */ 208 */
163 explicit Setting(const Type& default_val, const std::string& name) 209 explicit Setting(const Type& default_val, const std::string& name)
164 : BasicSetting<Type>(default_val, name) {} 210 : BasicSetting<Type>(default_val, name) {}
165 ~Setting() = default; 211 virtual ~Setting() = default;
166 212
167 /** 213 /**
168 * Tells this setting to represent either the global or custom setting when other member 214 * Tells this setting to represent either the global or custom setting when other member
@@ -191,7 +237,13 @@ public:
191 * 237 *
192 * @returns The required value of the setting 238 * @returns The required value of the setting
193 */ 239 */
194 [[nodiscard]] const Type& GetValue(bool need_global = false) const { 240 [[nodiscard]] virtual const Type& GetValue() const override {
241 if (use_global) {
242 return this->global;
243 }
244 return custom;
245 }
246 [[nodiscard]] virtual const Type& GetValue(bool need_global) const {
195 if (use_global || need_global) { 247 if (use_global || need_global) {
196 return this->global; 248 return this->global;
197 } 249 }
@@ -203,7 +255,7 @@ public:
203 * 255 *
204 * @param value The new value 256 * @param value The new value
205 */ 257 */
206 void SetValue(const Type& value) { 258 void SetValue(const Type& value) override {
207 Type temp{value}; 259 Type temp{value};
208 if (use_global) { 260 if (use_global) {
209 std::swap(this->global, temp); 261 std::swap(this->global, temp);
@@ -219,7 +271,7 @@ public:
219 * 271 *
220 * @returns A reference to the current setting value 272 * @returns A reference to the current setting value
221 */ 273 */
222 const Type& operator=(const Type& value) { 274 const Type& operator=(const Type& value) override {
223 Type temp{value}; 275 Type temp{value};
224 if (use_global) { 276 if (use_global) {
225 std::swap(this->global, temp); 277 std::swap(this->global, temp);
@@ -234,19 +286,88 @@ public:
234 * 286 *
235 * @returns A reference to the current setting value 287 * @returns A reference to the current setting value
236 */ 288 */
237 explicit operator const Type&() const { 289 virtual explicit operator const Type&() const override {
238 if (use_global) { 290 if (use_global) {
239 return this->global; 291 return this->global;
240 } 292 }
241 return custom; 293 return custom;
242 } 294 }
243 295
244private: 296protected:
245 bool use_global{true}; ///< The setting's global state 297 bool use_global{true}; ///< The setting's global state
246 Type custom{}; ///< The custom value of the setting 298 Type custom{}; ///< The custom value of the setting
247}; 299};
248 300
249/** 301/**
302 * RangedSetting is a Setting that implements a maximum and minimum value for its setting. Intended
303 * for use with quantifiable settings.
304 */
305template <typename Type>
306class RangedSetting final : public BasicRangedSetting<Type>, public Setting<Type> {
307public:
308 /**
309 * Sets a default value, minimum value, maximum value, and label.
310 *
311 * @param default_val Intial value of the setting, and default value of the setting
312 * @param min_val Sets the minimum allowed value of the setting
313 * @param max_val Sets the maximum allowed value of the setting
314 * @param name Label for the setting
315 */
316 explicit RangedSetting(const Type& default_val, const Type& min_val, const Type& max_val,
317 const std::string& name)
318 : BasicSetting<Type>{default_val, name},
319 BasicRangedSetting<Type>{default_val, min_val, max_val, name}, Setting<Type>{default_val,
320 name} {}
321 virtual ~RangedSetting() = default;
322
323 // The following are needed to avoid a MSVC bug
324 // (source: https://stackoverflow.com/questions/469508)
325 [[nodiscard]] const Type& GetValue() const override {
326 return Setting<Type>::GetValue();
327 }
328 [[nodiscard]] const Type& GetValue(bool need_global) const override {
329 return Setting<Type>::GetValue(need_global);
330 }
331 explicit operator const Type&() const override {
332 if (this->use_global) {
333 return this->global;
334 }
335 return this->custom;
336 }
337
338 /**
339 * Like BasicSetting's SetValue, except value is clamped to the range of the setting. Sets the
340 * appropriate value depending on the global state.
341 *
342 * @param value The desired value
343 */
344 void SetValue(const Type& value) override {
345 const Type temp = std::clamp(value, this->minimum, this->maximum);
346 if (this->use_global) {
347 this->global = temp;
348 }
349 this->custom = temp;
350 }
351
352 /**
353 * Like BasicSetting's assignment overload, except value is clamped to the range of the setting.
354 * Uses the appropriate value depending on the global state.
355 *
356 * @param value The desired value
357 * @returns A reference to the setting's value
358 */
359 const Type& operator=(const Type& value) override {
360 const Type temp = std::clamp(value, this->minimum, this->maximum);
361 if (this->use_global) {
362 this->global = temp;
363 return this->global;
364 }
365 this->custom = temp;
366 return this->custom;
367 }
368};
369
370/**
250 * The InputSetting class allows for getting a reference to either the global or custom members. 371 * The InputSetting class allows for getting a reference to either the global or custom members.
251 * This is required as we cannot easily modify the values of user-defined types within containers 372 * This is required as we cannot easily modify the values of user-defined types within containers
252 * using the SetValue() member function found in the Setting class. The primary purpose of this 373 * using the SetValue() member function found in the Setting class. The primary purpose of this
@@ -289,13 +410,14 @@ struct Values {
289 BasicSetting<std::string> sink_id{"auto", "output_engine"}; 410 BasicSetting<std::string> sink_id{"auto", "output_engine"};
290 BasicSetting<bool> audio_muted{false, "audio_muted"}; 411 BasicSetting<bool> audio_muted{false, "audio_muted"};
291 Setting<bool> enable_audio_stretching{true, "enable_audio_stretching"}; 412 Setting<bool> enable_audio_stretching{true, "enable_audio_stretching"};
292 Setting<u8> volume{100, "volume"}; 413 RangedSetting<u8> volume{100, 0, 100, "volume"};
293 414
294 // Core 415 // Core
295 Setting<bool> use_multi_core{true, "use_multi_core"}; 416 Setting<bool> use_multi_core{true, "use_multi_core"};
296 417
297 // Cpu 418 // Cpu
298 Setting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, "cpu_accuracy"}; 419 RangedSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto,
420 CPUAccuracy::Unsafe, "cpu_accuracy"};
299 // TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021 421 // TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021
300 BasicSetting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"}; 422 BasicSetting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"};
301 BasicSetting<bool> cpu_debug_mode{false, "cpu_debug_mode"}; 423 BasicSetting<bool> cpu_debug_mode{false, "cpu_debug_mode"};
@@ -317,7 +439,8 @@ struct Values {
317 Setting<bool> cpuopt_unsafe_fastmem_check{true, "cpuopt_unsafe_fastmem_check"}; 439 Setting<bool> cpuopt_unsafe_fastmem_check{true, "cpuopt_unsafe_fastmem_check"};
318 440
319 // Renderer 441 // Renderer
320 Setting<RendererBackend> renderer_backend{RendererBackend::OpenGL, "backend"}; 442 RangedSetting<RendererBackend> renderer_backend{
443 RendererBackend::OpenGL, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"};
321 BasicSetting<bool> renderer_debug{false, "debug"}; 444 BasicSetting<bool> renderer_debug{false, "debug"};
322 BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"}; 445 BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"};
323 BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"}; 446 BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"};
@@ -328,26 +451,28 @@ struct Values {
328 Setting<u16> resolution_factor{1, "resolution_factor"}; 451 Setting<u16> resolution_factor{1, "resolution_factor"};
329 // *nix platforms may have issues with the borderless windowed fullscreen mode. 452 // *nix platforms may have issues with the borderless windowed fullscreen mode.
330 // Default to exclusive fullscreen on these platforms for now. 453 // Default to exclusive fullscreen on these platforms for now.
331 Setting<FullscreenMode> fullscreen_mode{ 454 RangedSetting<FullscreenMode> fullscreen_mode{
332#ifdef _WIN32 455#ifdef _WIN32
333 FullscreenMode::Borderless, 456 FullscreenMode::Borderless,
334#else 457#else
335 FullscreenMode::Exclusive, 458 FullscreenMode::Exclusive,
336#endif 459#endif
337 "fullscreen_mode"}; 460 FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"};
338 Setting<int> aspect_ratio{0, "aspect_ratio"}; 461 RangedSetting<int> aspect_ratio{0, 0, 3, "aspect_ratio"};
339 Setting<int> max_anisotropy{0, "max_anisotropy"}; 462 RangedSetting<int> max_anisotropy{0, 0, 4, "max_anisotropy"};
340 Setting<bool> use_speed_limit{true, "use_speed_limit"}; 463 Setting<bool> use_speed_limit{true, "use_speed_limit"};
341 Setting<u16> speed_limit{100, "speed_limit"}; 464 RangedSetting<u16> speed_limit{100, 0, 9999, "speed_limit"};
342 Setting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"}; 465 Setting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"};
343 Setting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, "gpu_accuracy"}; 466 RangedSetting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, GPUAccuracy::Normal,
467 GPUAccuracy::Extreme, "gpu_accuracy"};
344 Setting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"}; 468 Setting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"};
345 Setting<bool> use_nvdec_emulation{true, "use_nvdec_emulation"}; 469 Setting<bool> use_nvdec_emulation{true, "use_nvdec_emulation"};
346 Setting<bool> accelerate_astc{true, "accelerate_astc"}; 470 Setting<bool> accelerate_astc{true, "accelerate_astc"};
347 Setting<bool> use_vsync{true, "use_vsync"}; 471 Setting<bool> use_vsync{true, "use_vsync"};
348 BasicSetting<u16> fps_cap{1000, "fps_cap"}; 472 BasicRangedSetting<u16> fps_cap{1000, 1, 1000, "fps_cap"};
349 BasicSetting<bool> disable_fps_limit{false, "disable_fps_limit"}; 473 BasicSetting<bool> disable_fps_limit{false, "disable_fps_limit"};
350 Setting<ShaderBackend> shader_backend{ShaderBackend::GLASM, "shader_backend"}; 474 RangedSetting<ShaderBackend> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL,
475 ShaderBackend::SPIRV, "shader_backend"};
351 Setting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; 476 Setting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
352 Setting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; 477 Setting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
353 Setting<bool> use_caches_gc{false, "use_caches_gc"}; 478 Setting<bool> use_caches_gc{false, "use_caches_gc"};
@@ -364,10 +489,10 @@ struct Values {
364 std::chrono::seconds custom_rtc_differential; 489 std::chrono::seconds custom_rtc_differential;
365 490
366 BasicSetting<s32> current_user{0, "current_user"}; 491 BasicSetting<s32> current_user{0, "current_user"};
367 Setting<s32> language_index{1, "language_index"}; 492 RangedSetting<s32> language_index{1, 0, 16, "language_index"};
368 Setting<s32> region_index{1, "region_index"}; 493 RangedSetting<s32> region_index{1, 0, 6, "region_index"};
369 Setting<s32> time_zone_index{0, "time_zone_index"}; 494 RangedSetting<s32> time_zone_index{0, 0, 45, "time_zone_index"};
370 Setting<s32> sound_index{1, "sound_index"}; 495 RangedSetting<s32> sound_index{1, 0, 2, "sound_index"};
371 496
372 // Controls 497 // Controls
373 InputSetting<std::array<PlayerInput, 10>> players; 498 InputSetting<std::array<PlayerInput, 10>> players;
@@ -384,7 +509,7 @@ struct Values {
384 "udp_input_servers"}; 509 "udp_input_servers"};
385 510
386 BasicSetting<bool> mouse_panning{false, "mouse_panning"}; 511 BasicSetting<bool> mouse_panning{false, "mouse_panning"};
387 BasicSetting<u8> mouse_panning_sensitivity{10, "mouse_panning_sensitivity"}; 512 BasicRangedSetting<u8> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"};
388 BasicSetting<bool> mouse_enabled{false, "mouse_enabled"}; 513 BasicSetting<bool> mouse_enabled{false, "mouse_enabled"};
389 std::string mouse_device; 514 std::string mouse_device;
390 MouseButtonsRaw mouse_buttons; 515 MouseButtonsRaw mouse_buttons;
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index f285c6f63..51c4dea26 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -4,8 +4,6 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <cstring> 6#include <cstring>
7#include <optional>
8#include <utility>
9 7
10#include "common/assert.h" 8#include "common/assert.h"
11#include "common/atomic_ops.h" 9#include "common/atomic_ops.h"
@@ -14,12 +12,10 @@
14#include "common/page_table.h" 12#include "common/page_table.h"
15#include "common/settings.h" 13#include "common/settings.h"
16#include "common/swap.h" 14#include "common/swap.h"
17#include "core/arm/arm_interface.h"
18#include "core/core.h" 15#include "core/core.h"
19#include "core/device_memory.h" 16#include "core/device_memory.h"
20#include "core/hle/kernel/k_page_table.h" 17#include "core/hle/kernel/k_page_table.h"
21#include "core/hle/kernel/k_process.h" 18#include "core/hle/kernel/k_process.h"
22#include "core/hle/kernel/physical_memory.h"
23#include "core/memory.h" 19#include "core/memory.h"
24#include "video_core/gpu.h" 20#include "video_core/gpu.h"
25 21
@@ -62,17 +58,7 @@ struct Memory::Impl {
62 } 58 }
63 } 59 }
64 60
65 bool IsValidVirtualAddress(const Kernel::KProcess& process, const VAddr vaddr) const { 61 [[nodiscard]] u8* GetPointerFromRasterizerCachedMemory(VAddr vaddr) const {
66 const auto& page_table = process.PageTable().PageTableImpl();
67 const auto [pointer, type] = page_table.pointers[vaddr >> PAGE_BITS].PointerType();
68 return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory;
69 }
70
71 bool IsValidVirtualAddress(VAddr vaddr) const {
72 return IsValidVirtualAddress(*system.CurrentProcess(), vaddr);
73 }
74
75 u8* GetPointerFromRasterizerCachedMemory(VAddr vaddr) const {
76 const PAddr paddr{current_page_table->backing_addr[vaddr >> PAGE_BITS]}; 62 const PAddr paddr{current_page_table->backing_addr[vaddr >> PAGE_BITS]};
77 63
78 if (!paddr) { 64 if (!paddr) {
@@ -82,18 +68,6 @@ struct Memory::Impl {
82 return system.DeviceMemory().GetPointer(paddr) + vaddr; 68 return system.DeviceMemory().GetPointer(paddr) + vaddr;
83 } 69 }
84 70
85 u8* GetPointer(const VAddr vaddr) const {
86 const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
87 if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
88 return pointer + vaddr;
89 }
90 const auto type = Common::PageTable::PageInfo::ExtractType(raw_pointer);
91 if (type == Common::PageType::RasterizerCachedMemory) {
92 return GetPointerFromRasterizerCachedMemory(vaddr);
93 }
94 return nullptr;
95 }
96
97 u8 Read8(const VAddr addr) { 71 u8 Read8(const VAddr addr) {
98 return Read<u8>(addr); 72 return Read<u8>(addr);
99 } 73 }
@@ -179,7 +153,7 @@ struct Memory::Impl {
179 std::string string; 153 std::string string;
180 string.reserve(max_length); 154 string.reserve(max_length);
181 for (std::size_t i = 0; i < max_length; ++i) { 155 for (std::size_t i = 0; i < max_length; ++i) {
182 const char c = Read8(vaddr); 156 const char c = Read<s8>(vaddr);
183 if (c == '\0') { 157 if (c == '\0') {
184 break; 158 break;
185 } 159 }
@@ -190,15 +164,14 @@ struct Memory::Impl {
190 return string; 164 return string;
191 } 165 }
192 166
193 void ReadBlock(const Kernel::KProcess& process, const VAddr src_addr, void* dest_buffer, 167 void WalkBlock(const Kernel::KProcess& process, const VAddr addr, const std::size_t size,
194 const std::size_t size) { 168 auto on_unmapped, auto on_memory, auto on_rasterizer, auto increment) {
195 const auto& page_table = process.PageTable().PageTableImpl(); 169 const auto& page_table = process.PageTable().PageTableImpl();
196
197 std::size_t remaining_size = size; 170 std::size_t remaining_size = size;
198 std::size_t page_index = src_addr >> PAGE_BITS; 171 std::size_t page_index = addr >> PAGE_BITS;
199 std::size_t page_offset = src_addr & PAGE_MASK; 172 std::size_t page_offset = addr & PAGE_MASK;
200 173
201 while (remaining_size > 0) { 174 while (remaining_size) {
202 const std::size_t copy_amount = 175 const std::size_t copy_amount =
203 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); 176 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
204 const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); 177 const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
@@ -206,22 +179,18 @@ struct Memory::Impl {
206 const auto [pointer, type] = page_table.pointers[page_index].PointerType(); 179 const auto [pointer, type] = page_table.pointers[page_index].PointerType();
207 switch (type) { 180 switch (type) {
208 case Common::PageType::Unmapped: { 181 case Common::PageType::Unmapped: {
209 LOG_ERROR(HW_Memory, 182 on_unmapped(copy_amount, current_vaddr);
210 "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
211 current_vaddr, src_addr, size);
212 std::memset(dest_buffer, 0, copy_amount);
213 break; 183 break;
214 } 184 }
215 case Common::PageType::Memory: { 185 case Common::PageType::Memory: {
216 DEBUG_ASSERT(pointer); 186 DEBUG_ASSERT(pointer);
217 const u8* const src_ptr = pointer + page_offset + (page_index << PAGE_BITS); 187 u8* mem_ptr = pointer + page_offset + (page_index << PAGE_BITS);
218 std::memcpy(dest_buffer, src_ptr, copy_amount); 188 on_memory(copy_amount, mem_ptr);
219 break; 189 break;
220 } 190 }
221 case Common::PageType::RasterizerCachedMemory: { 191 case Common::PageType::RasterizerCachedMemory: {
222 const u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)}; 192 u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)};
223 system.GPU().FlushRegion(current_vaddr, copy_amount); 193 on_rasterizer(current_vaddr, copy_amount, host_ptr);
224 std::memcpy(dest_buffer, host_ptr, copy_amount);
225 break; 194 break;
226 } 195 }
227 default: 196 default:
@@ -230,248 +199,122 @@ struct Memory::Impl {
230 199
231 page_index++; 200 page_index++;
232 page_offset = 0; 201 page_offset = 0;
233 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount; 202 increment(copy_amount);
234 remaining_size -= copy_amount; 203 remaining_size -= copy_amount;
235 } 204 }
236 } 205 }
237 206
238 void ReadBlockUnsafe(const Kernel::KProcess& process, const VAddr src_addr, void* dest_buffer, 207 template <bool UNSAFE>
239 const std::size_t size) { 208 void ReadBlockImpl(const Kernel::KProcess& process, const VAddr src_addr, void* dest_buffer,
240 const auto& page_table = process.PageTable().PageTableImpl(); 209 const std::size_t size) {
241 210 WalkBlock(
242 std::size_t remaining_size = size; 211 process, src_addr, size,
243 std::size_t page_index = src_addr >> PAGE_BITS; 212 [src_addr, size, &dest_buffer](const std::size_t copy_amount,
244 std::size_t page_offset = src_addr & PAGE_MASK; 213 const VAddr current_vaddr) {
245
246 while (remaining_size > 0) {
247 const std::size_t copy_amount =
248 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
249 const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
250
251 const auto [pointer, type] = page_table.pointers[page_index].PointerType();
252 switch (type) {
253 case Common::PageType::Unmapped: {
254 LOG_ERROR(HW_Memory, 214 LOG_ERROR(HW_Memory,
255 "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 215 "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
256 current_vaddr, src_addr, size); 216 current_vaddr, src_addr, size);
257 std::memset(dest_buffer, 0, copy_amount); 217 std::memset(dest_buffer, 0, copy_amount);
258 break; 218 },
259 } 219 [&dest_buffer](const std::size_t copy_amount, const u8* const src_ptr) {
260 case Common::PageType::Memory: {
261 DEBUG_ASSERT(pointer);
262 const u8* const src_ptr = pointer + page_offset + (page_index << PAGE_BITS);
263 std::memcpy(dest_buffer, src_ptr, copy_amount); 220 std::memcpy(dest_buffer, src_ptr, copy_amount);
264 break; 221 },
265 } 222 [&system = system, &dest_buffer](const VAddr current_vaddr,
266 case Common::PageType::RasterizerCachedMemory: { 223 const std::size_t copy_amount,
267 const u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)}; 224 const u8* const host_ptr) {
225 if constexpr (!UNSAFE) {
226 system.GPU().FlushRegion(current_vaddr, copy_amount);
227 }
268 std::memcpy(dest_buffer, host_ptr, copy_amount); 228 std::memcpy(dest_buffer, host_ptr, copy_amount);
269 break; 229 },
270 } 230 [&dest_buffer](const std::size_t copy_amount) {
271 default: 231 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
272 UNREACHABLE(); 232 });
273 }
274
275 page_index++;
276 page_offset = 0;
277 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
278 remaining_size -= copy_amount;
279 }
280 } 233 }
281 234
282 void ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) { 235 void ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) {
283 ReadBlock(*system.CurrentProcess(), src_addr, dest_buffer, size); 236 ReadBlockImpl<false>(*system.CurrentProcess(), src_addr, dest_buffer, size);
284 } 237 }
285 238
286 void ReadBlockUnsafe(const VAddr src_addr, void* dest_buffer, const std::size_t size) { 239 void ReadBlockUnsafe(const VAddr src_addr, void* dest_buffer, const std::size_t size) {
287 ReadBlockUnsafe(*system.CurrentProcess(), src_addr, dest_buffer, size); 240 ReadBlockImpl<true>(*system.CurrentProcess(), src_addr, dest_buffer, size);
288 } 241 }
289 242
290 void WriteBlock(const Kernel::KProcess& process, const VAddr dest_addr, const void* src_buffer, 243 template <bool UNSAFE>
291 const std::size_t size) { 244 void WriteBlockImpl(const Kernel::KProcess& process, const VAddr dest_addr,
292 const auto& page_table = process.PageTable().PageTableImpl(); 245 const void* src_buffer, const std::size_t size) {
293 std::size_t remaining_size = size; 246 WalkBlock(
294 std::size_t page_index = dest_addr >> PAGE_BITS; 247 process, dest_addr, size,
295 std::size_t page_offset = dest_addr & PAGE_MASK; 248 [dest_addr, size](const std::size_t copy_amount, const VAddr current_vaddr) {
296
297 while (remaining_size > 0) {
298 const std::size_t copy_amount =
299 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
300 const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
301
302 const auto [pointer, type] = page_table.pointers[page_index].PointerType();
303 switch (type) {
304 case Common::PageType::Unmapped: {
305 LOG_ERROR(HW_Memory, 249 LOG_ERROR(HW_Memory,
306 "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 250 "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
307 current_vaddr, dest_addr, size); 251 current_vaddr, dest_addr, size);
308 break; 252 },
309 } 253 [&src_buffer](const std::size_t copy_amount, u8* const dest_ptr) {
310 case Common::PageType::Memory: {
311 DEBUG_ASSERT(pointer);
312 u8* const dest_ptr = pointer + page_offset + (page_index << PAGE_BITS);
313 std::memcpy(dest_ptr, src_buffer, copy_amount); 254 std::memcpy(dest_ptr, src_buffer, copy_amount);
314 break; 255 },
315 } 256 [&system = system, &src_buffer](const VAddr current_vaddr,
316 case Common::PageType::RasterizerCachedMemory: { 257 const std::size_t copy_amount, u8* const host_ptr) {
317 u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)}; 258 if constexpr (!UNSAFE) {
318 system.GPU().InvalidateRegion(current_vaddr, copy_amount); 259 system.GPU().InvalidateRegion(current_vaddr, copy_amount);
319 std::memcpy(host_ptr, src_buffer, copy_amount); 260 }
320 break;
321 }
322 default:
323 UNREACHABLE();
324 }
325
326 page_index++;
327 page_offset = 0;
328 src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
329 remaining_size -= copy_amount;
330 }
331 }
332
333 void WriteBlockUnsafe(const Kernel::KProcess& process, const VAddr dest_addr,
334 const void* src_buffer, const std::size_t size) {
335 const auto& page_table = process.PageTable().PageTableImpl();
336 std::size_t remaining_size = size;
337 std::size_t page_index = dest_addr >> PAGE_BITS;
338 std::size_t page_offset = dest_addr & PAGE_MASK;
339
340 while (remaining_size > 0) {
341 const std::size_t copy_amount =
342 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
343 const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
344
345 const auto [pointer, type] = page_table.pointers[page_index].PointerType();
346 switch (type) {
347 case Common::PageType::Unmapped: {
348 LOG_ERROR(HW_Memory,
349 "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
350 current_vaddr, dest_addr, size);
351 break;
352 }
353 case Common::PageType::Memory: {
354 DEBUG_ASSERT(pointer);
355 u8* const dest_ptr = pointer + page_offset + (page_index << PAGE_BITS);
356 std::memcpy(dest_ptr, src_buffer, copy_amount);
357 break;
358 }
359 case Common::PageType::RasterizerCachedMemory: {
360 u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)};
361 std::memcpy(host_ptr, src_buffer, copy_amount); 261 std::memcpy(host_ptr, src_buffer, copy_amount);
362 break; 262 },
363 } 263 [&src_buffer](const std::size_t copy_amount) {
364 default: 264 src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
365 UNREACHABLE(); 265 });
366 }
367
368 page_index++;
369 page_offset = 0;
370 src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
371 remaining_size -= copy_amount;
372 }
373 } 266 }
374 267
375 void WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) { 268 void WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) {
376 WriteBlock(*system.CurrentProcess(), dest_addr, src_buffer, size); 269 WriteBlockImpl<false>(*system.CurrentProcess(), dest_addr, src_buffer, size);
377 } 270 }
378 271
379 void WriteBlockUnsafe(const VAddr dest_addr, const void* src_buffer, const std::size_t size) { 272 void WriteBlockUnsafe(const VAddr dest_addr, const void* src_buffer, const std::size_t size) {
380 WriteBlockUnsafe(*system.CurrentProcess(), dest_addr, src_buffer, size); 273 WriteBlockImpl<true>(*system.CurrentProcess(), dest_addr, src_buffer, size);
381 } 274 }
382 275
383 void ZeroBlock(const Kernel::KProcess& process, const VAddr dest_addr, const std::size_t size) { 276 void ZeroBlock(const Kernel::KProcess& process, const VAddr dest_addr, const std::size_t size) {
384 const auto& page_table = process.PageTable().PageTableImpl(); 277 WalkBlock(
385 std::size_t remaining_size = size; 278 process, dest_addr, size,
386 std::size_t page_index = dest_addr >> PAGE_BITS; 279 [dest_addr, size](const std::size_t copy_amount, const VAddr current_vaddr) {
387 std::size_t page_offset = dest_addr & PAGE_MASK;
388
389 while (remaining_size > 0) {
390 const std::size_t copy_amount =
391 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
392 const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
393
394 const auto [pointer, type] = page_table.pointers[page_index].PointerType();
395 switch (type) {
396 case Common::PageType::Unmapped: {
397 LOG_ERROR(HW_Memory, 280 LOG_ERROR(HW_Memory,
398 "Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 281 "Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
399 current_vaddr, dest_addr, size); 282 current_vaddr, dest_addr, size);
400 break; 283 },
401 } 284 [](const std::size_t copy_amount, u8* const dest_ptr) {
402 case Common::PageType::Memory: {
403 DEBUG_ASSERT(pointer);
404 u8* const dest_ptr = pointer + page_offset + (page_index << PAGE_BITS);
405 std::memset(dest_ptr, 0, copy_amount); 285 std::memset(dest_ptr, 0, copy_amount);
406 break; 286 },
407 } 287 [&system = system](const VAddr current_vaddr, const std::size_t copy_amount,
408 case Common::PageType::RasterizerCachedMemory: { 288 u8* const host_ptr) {
409 u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)};
410 system.GPU().InvalidateRegion(current_vaddr, copy_amount); 289 system.GPU().InvalidateRegion(current_vaddr, copy_amount);
411 std::memset(host_ptr, 0, copy_amount); 290 std::memset(host_ptr, 0, copy_amount);
412 break; 291 },
413 } 292 [](const std::size_t copy_amount) {});
414 default:
415 UNREACHABLE();
416 }
417
418 page_index++;
419 page_offset = 0;
420 remaining_size -= copy_amount;
421 }
422 }
423
424 void ZeroBlock(const VAddr dest_addr, const std::size_t size) {
425 ZeroBlock(*system.CurrentProcess(), dest_addr, size);
426 } 293 }
427 294
428 void CopyBlock(const Kernel::KProcess& process, VAddr dest_addr, VAddr src_addr, 295 void CopyBlock(const Kernel::KProcess& process, VAddr dest_addr, VAddr src_addr,
429 const std::size_t size) { 296 const std::size_t size) {
430 const auto& page_table = process.PageTable().PageTableImpl(); 297 WalkBlock(
431 std::size_t remaining_size = size; 298 process, dest_addr, size,
432 std::size_t page_index = src_addr >> PAGE_BITS; 299 [this, &process, &dest_addr, &src_addr, size](const std::size_t copy_amount,
433 std::size_t page_offset = src_addr & PAGE_MASK; 300 const VAddr current_vaddr) {
434
435 while (remaining_size > 0) {
436 const std::size_t copy_amount =
437 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
438 const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
439
440 const auto [pointer, type] = page_table.pointers[page_index].PointerType();
441 switch (type) {
442 case Common::PageType::Unmapped: {
443 LOG_ERROR(HW_Memory, 301 LOG_ERROR(HW_Memory,
444 "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 302 "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
445 current_vaddr, src_addr, size); 303 current_vaddr, src_addr, size);
446 ZeroBlock(process, dest_addr, copy_amount); 304 ZeroBlock(process, dest_addr, copy_amount);
447 break; 305 },
448 } 306 [this, &process, &dest_addr](const std::size_t copy_amount, const u8* const src_ptr) {
449 case Common::PageType::Memory: { 307 WriteBlockImpl<false>(process, dest_addr, src_ptr, copy_amount);
450 DEBUG_ASSERT(pointer); 308 },
451 const u8* src_ptr = pointer + page_offset + (page_index << PAGE_BITS); 309 [this, &system = system, &process, &dest_addr](
452 WriteBlock(process, dest_addr, src_ptr, copy_amount); 310 const VAddr current_vaddr, const std::size_t copy_amount, u8* const host_ptr) {
453 break;
454 }
455 case Common::PageType::RasterizerCachedMemory: {
456 const u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)};
457 system.GPU().FlushRegion(current_vaddr, copy_amount); 311 system.GPU().FlushRegion(current_vaddr, copy_amount);
458 WriteBlock(process, dest_addr, host_ptr, copy_amount); 312 WriteBlockImpl<false>(process, dest_addr, host_ptr, copy_amount);
459 break; 313 },
460 } 314 [&dest_addr, &src_addr](const std::size_t copy_amount) {
461 default: 315 dest_addr += static_cast<VAddr>(copy_amount);
462 UNREACHABLE(); 316 src_addr += static_cast<VAddr>(copy_amount);
463 } 317 });
464
465 page_index++;
466 page_offset = 0;
467 dest_addr += static_cast<VAddr>(copy_amount);
468 src_addr += static_cast<VAddr>(copy_amount);
469 remaining_size -= copy_amount;
470 }
471 }
472
473 void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size) {
474 return CopyBlock(*system.CurrentProcess(), dest_addr, src_addr, size);
475 } 318 }
476 319
477 void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) { 320 void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
@@ -514,7 +357,7 @@ struct Memory::Impl {
514 } else { 357 } else {
515 // Switch page type to uncached if now uncached 358 // Switch page type to uncached if now uncached
516 switch (page_type) { 359 switch (page_type) {
517 case Common::PageType::Unmapped: 360 case Common::PageType::Unmapped: // NOLINT(bugprone-branch-clone)
518 // It is not necessary for a process to have this region mapped into its address 361 // It is not necessary for a process to have this region mapped into its address
519 // space, for example, a system module need not have a VRAM mapping. 362 // space, for example, a system module need not have a VRAM mapping.
520 break; 363 break;
@@ -597,52 +440,68 @@ struct Memory::Impl {
597 } 440 }
598 } 441 }
599 442
600 /** 443 [[nodiscard]] u8* GetPointerImpl(VAddr vaddr, auto on_unmapped, auto on_rasterizer) const {
601 * Reads a particular data type out of memory at the given virtual address.
602 *
603 * @param vaddr The virtual address to read the data type from.
604 *
605 * @tparam T The data type to read out of memory. This type *must* be
606 * trivially copyable, otherwise the behavior of this function
607 * is undefined.
608 *
609 * @returns The instance of T read from the specified virtual address.
610 */
611 template <typename T>
612 T Read(VAddr vaddr) {
613 // AARCH64 masks the upper 16 bit of all memory accesses 444 // AARCH64 masks the upper 16 bit of all memory accesses
614 vaddr &= 0xffffffffffffLL; 445 vaddr &= 0xffffffffffffLL;
615 446
616 if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) { 447 if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) {
617 LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr); 448 on_unmapped();
618 return 0; 449 return nullptr;
619 } 450 }
620 451
621 // Avoid adding any extra logic to this fast-path block 452 // Avoid adding any extra logic to this fast-path block
622 const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw(); 453 const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
623 if (const u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { 454 if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
624 T value; 455 return &pointer[vaddr];
625 std::memcpy(&value, &pointer[vaddr], sizeof(T));
626 return value;
627 } 456 }
628 switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) { 457 switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
629 case Common::PageType::Unmapped: 458 case Common::PageType::Unmapped:
630 LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr); 459 on_unmapped();
631 return 0; 460 return nullptr;
632 case Common::PageType::Memory: 461 case Common::PageType::Memory:
633 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); 462 ASSERT_MSG(false, "Mapped memory page without a pointer @ 0x{:016X}", vaddr);
634 break; 463 return nullptr;
635 case Common::PageType::RasterizerCachedMemory: { 464 case Common::PageType::RasterizerCachedMemory: {
636 const u8* const host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; 465 u8* const host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
637 system.GPU().FlushRegion(vaddr, sizeof(T)); 466 on_rasterizer();
638 T value; 467 return host_ptr;
639 std::memcpy(&value, host_ptr, sizeof(T));
640 return value;
641 } 468 }
642 default: 469 default:
643 UNREACHABLE(); 470 UNREACHABLE();
644 } 471 }
645 return {}; 472 return nullptr;
473 }
474
475 [[nodiscard]] u8* GetPointer(const VAddr vaddr) const {
476 return GetPointerImpl(
477 vaddr, [vaddr]() { LOG_ERROR(HW_Memory, "Unmapped GetPointer @ 0x{:016X}", vaddr); },
478 []() {});
479 }
480
481 /**
482 * Reads a particular data type out of memory at the given virtual address.
483 *
484 * @param vaddr The virtual address to read the data type from.
485 *
486 * @tparam T The data type to read out of memory. This type *must* be
487 * trivially copyable, otherwise the behavior of this function
488 * is undefined.
489 *
490 * @returns The instance of T read from the specified virtual address.
491 */
492 template <typename T>
493 T Read(VAddr vaddr) {
494 T result = 0;
495 const u8* const ptr = GetPointerImpl(
496 vaddr,
497 [vaddr]() {
498 LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:016X}", sizeof(T) * 8, vaddr);
499 },
500 [&system = system, vaddr]() { system.GPU().FlushRegion(vaddr, sizeof(T)); });
501 if (ptr) {
502 std::memcpy(&result, ptr, sizeof(T));
503 }
504 return result;
646 } 505 }
647 506
648 /** 507 /**
@@ -656,110 +515,46 @@ struct Memory::Impl {
656 */ 515 */
657 template <typename T> 516 template <typename T>
658 void Write(VAddr vaddr, const T data) { 517 void Write(VAddr vaddr, const T data) {
659 // AARCH64 masks the upper 16 bit of all memory accesses 518 u8* const ptr = GetPointerImpl(
660 vaddr &= 0xffffffffffffLL; 519 vaddr,
661 520 [vaddr, data]() {
662 if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) { 521 LOG_ERROR(HW_Memory, "Unmapped Write{} @ 0x{:016X} = 0x{:016X}", sizeof(T) * 8,
663 LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, 522 vaddr, static_cast<u64>(data));
664 static_cast<u32>(data), vaddr); 523 },
665 return; 524 [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); });
666 } 525 if (ptr) {
667 526 std::memcpy(ptr, &data, sizeof(T));
668 // Avoid adding any extra logic to this fast-path block
669 const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
670 if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
671 std::memcpy(&pointer[vaddr], &data, sizeof(T));
672 return;
673 }
674 switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
675 case Common::PageType::Unmapped:
676 LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
677 static_cast<u32>(data), vaddr);
678 return;
679 case Common::PageType::Memory:
680 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
681 break;
682 case Common::PageType::RasterizerCachedMemory: {
683 u8* const host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
684 system.GPU().InvalidateRegion(vaddr, sizeof(T));
685 std::memcpy(host_ptr, &data, sizeof(T));
686 break;
687 }
688 default:
689 UNREACHABLE();
690 } 527 }
691 } 528 }
692 529
693 template <typename T> 530 template <typename T>
694 bool WriteExclusive(VAddr vaddr, const T data, const T expected) { 531 bool WriteExclusive(VAddr vaddr, const T data, const T expected) {
695 // AARCH64 masks the upper 16 bit of all memory accesses 532 u8* const ptr = GetPointerImpl(
696 vaddr &= 0xffffffffffffLL; 533 vaddr,
697 534 [vaddr, data]() {
698 if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) { 535 LOG_ERROR(HW_Memory, "Unmapped WriteExclusive{} @ 0x{:016X} = 0x{:016X}",
699 LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, 536 sizeof(T) * 8, vaddr, static_cast<u64>(data));
700 static_cast<u32>(data), vaddr); 537 },
701 return true; 538 [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); });
702 } 539 if (ptr) {
703 540 const auto volatile_pointer = reinterpret_cast<volatile T*>(ptr);
704 const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
705 if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
706 // NOTE: Avoid adding any extra logic to this fast-path block
707 const auto volatile_pointer = reinterpret_cast<volatile T*>(&pointer[vaddr]);
708 return Common::AtomicCompareAndSwap(volatile_pointer, data, expected); 541 return Common::AtomicCompareAndSwap(volatile_pointer, data, expected);
709 } 542 }
710 switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
711 case Common::PageType::Unmapped:
712 LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
713 static_cast<u32>(data), vaddr);
714 return true;
715 case Common::PageType::Memory:
716 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
717 break;
718 case Common::PageType::RasterizerCachedMemory: {
719 u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
720 system.GPU().InvalidateRegion(vaddr, sizeof(T));
721 auto* pointer = reinterpret_cast<volatile T*>(&host_ptr);
722 return Common::AtomicCompareAndSwap(pointer, data, expected);
723 }
724 default:
725 UNREACHABLE();
726 }
727 return true; 543 return true;
728 } 544 }
729 545
730 bool WriteExclusive128(VAddr vaddr, const u128 data, const u128 expected) { 546 bool WriteExclusive128(VAddr vaddr, const u128 data, const u128 expected) {
731 // AARCH64 masks the upper 16 bit of all memory accesses 547 u8* const ptr = GetPointerImpl(
732 vaddr &= 0xffffffffffffLL; 548 vaddr,
733 549 [vaddr, data]() {
734 if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) { 550 LOG_ERROR(HW_Memory, "Unmapped WriteExclusive128 @ 0x{:016X} = 0x{:016X}{:016X}",
735 LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, 551 vaddr, static_cast<u64>(data[1]), static_cast<u64>(data[0]));
736 static_cast<u32>(data[0]), vaddr); 552 },
737 return true; 553 [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(u128)); });
738 } 554 if (ptr) {
739 555 const auto volatile_pointer = reinterpret_cast<volatile u64*>(ptr);
740 const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
741 if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
742 // NOTE: Avoid adding any extra logic to this fast-path block
743 const auto volatile_pointer = reinterpret_cast<volatile u64*>(&pointer[vaddr]);
744 return Common::AtomicCompareAndSwap(volatile_pointer, data, expected); 556 return Common::AtomicCompareAndSwap(volatile_pointer, data, expected);
745 } 557 }
746 switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
747 case Common::PageType::Unmapped:
748 LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}{:016X}", sizeof(data) * 8,
749 static_cast<u64>(data[1]), static_cast<u64>(data[0]), vaddr);
750 return true;
751 case Common::PageType::Memory:
752 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
753 break;
754 case Common::PageType::RasterizerCachedMemory: {
755 u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
756 system.GPU().InvalidateRegion(vaddr, sizeof(u128));
757 auto* pointer = reinterpret_cast<volatile u64*>(&host_ptr);
758 return Common::AtomicCompareAndSwap(pointer, data, expected);
759 }
760 default:
761 UNREACHABLE();
762 }
763 return true; 558 return true;
764 } 559 }
765 560
@@ -789,12 +584,11 @@ void Memory::UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
789 impl->UnmapRegion(page_table, base, size); 584 impl->UnmapRegion(page_table, base, size);
790} 585}
791 586
792bool Memory::IsValidVirtualAddress(const Kernel::KProcess& process, const VAddr vaddr) const {
793 return impl->IsValidVirtualAddress(process, vaddr);
794}
795
796bool Memory::IsValidVirtualAddress(const VAddr vaddr) const { 587bool Memory::IsValidVirtualAddress(const VAddr vaddr) const {
797 return impl->IsValidVirtualAddress(vaddr); 588 const Kernel::KProcess& process = *system.CurrentProcess();
589 const auto& page_table = process.PageTable().PageTableImpl();
590 const auto [pointer, type] = page_table.pointers[vaddr >> PAGE_BITS].PointerType();
591 return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory;
798} 592}
799 593
800u8* Memory::GetPointer(VAddr vaddr) { 594u8* Memory::GetPointer(VAddr vaddr) {
@@ -863,64 +657,38 @@ std::string Memory::ReadCString(VAddr vaddr, std::size_t max_length) {
863 657
864void Memory::ReadBlock(const Kernel::KProcess& process, const VAddr src_addr, void* dest_buffer, 658void Memory::ReadBlock(const Kernel::KProcess& process, const VAddr src_addr, void* dest_buffer,
865 const std::size_t size) { 659 const std::size_t size) {
866 impl->ReadBlock(process, src_addr, dest_buffer, size); 660 impl->ReadBlockImpl<false>(process, src_addr, dest_buffer, size);
867} 661}
868 662
869void Memory::ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) { 663void Memory::ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) {
870 impl->ReadBlock(src_addr, dest_buffer, size); 664 impl->ReadBlock(src_addr, dest_buffer, size);
871} 665}
872 666
873void Memory::ReadBlockUnsafe(const Kernel::KProcess& process, const VAddr src_addr,
874 void* dest_buffer, const std::size_t size) {
875 impl->ReadBlockUnsafe(process, src_addr, dest_buffer, size);
876}
877
878void Memory::ReadBlockUnsafe(const VAddr src_addr, void* dest_buffer, const std::size_t size) { 667void Memory::ReadBlockUnsafe(const VAddr src_addr, void* dest_buffer, const std::size_t size) {
879 impl->ReadBlockUnsafe(src_addr, dest_buffer, size); 668 impl->ReadBlockUnsafe(src_addr, dest_buffer, size);
880} 669}
881 670
882void Memory::WriteBlock(const Kernel::KProcess& process, VAddr dest_addr, const void* src_buffer, 671void Memory::WriteBlock(const Kernel::KProcess& process, VAddr dest_addr, const void* src_buffer,
883 std::size_t size) { 672 std::size_t size) {
884 impl->WriteBlock(process, dest_addr, src_buffer, size); 673 impl->WriteBlockImpl<false>(process, dest_addr, src_buffer, size);
885} 674}
886 675
887void Memory::WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) { 676void Memory::WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) {
888 impl->WriteBlock(dest_addr, src_buffer, size); 677 impl->WriteBlock(dest_addr, src_buffer, size);
889} 678}
890 679
891void Memory::WriteBlockUnsafe(const Kernel::KProcess& process, VAddr dest_addr,
892 const void* src_buffer, std::size_t size) {
893 impl->WriteBlockUnsafe(process, dest_addr, src_buffer, size);
894}
895
896void Memory::WriteBlockUnsafe(const VAddr dest_addr, const void* src_buffer, 680void Memory::WriteBlockUnsafe(const VAddr dest_addr, const void* src_buffer,
897 const std::size_t size) { 681 const std::size_t size) {
898 impl->WriteBlockUnsafe(dest_addr, src_buffer, size); 682 impl->WriteBlockUnsafe(dest_addr, src_buffer, size);
899} 683}
900 684
901void Memory::ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size) {
902 impl->ZeroBlock(process, dest_addr, size);
903}
904
905void Memory::ZeroBlock(VAddr dest_addr, std::size_t size) {
906 impl->ZeroBlock(dest_addr, size);
907}
908
909void Memory::CopyBlock(const Kernel::KProcess& process, VAddr dest_addr, VAddr src_addr, 685void Memory::CopyBlock(const Kernel::KProcess& process, VAddr dest_addr, VAddr src_addr,
910 const std::size_t size) { 686 const std::size_t size) {
911 impl->CopyBlock(process, dest_addr, src_addr, size); 687 impl->CopyBlock(process, dest_addr, src_addr, size);
912} 688}
913 689
914void Memory::CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size) {
915 impl->CopyBlock(dest_addr, src_addr, size);
916}
917
918void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) { 690void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
919 impl->RasterizerMarkRegionCached(vaddr, size, cached); 691 impl->RasterizerMarkRegionCached(vaddr, size, cached);
920} 692}
921 693
922bool IsKernelVirtualAddress(const VAddr vaddr) {
923 return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END;
924}
925
926} // namespace Core::Memory 694} // namespace Core::Memory
diff --git a/src/core/memory.h b/src/core/memory.h
index c91eeced9..b5721b740 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -39,11 +39,6 @@ enum : VAddr {
39 39
40 /// Application stack 40 /// Application stack
41 DEFAULT_STACK_SIZE = 0x100000, 41 DEFAULT_STACK_SIZE = 0x100000,
42
43 /// Kernel Virtual Address Range
44 KERNEL_REGION_VADDR = 0xFFFFFF8000000000,
45 KERNEL_REGION_SIZE = 0x7FFFE00000,
46 KERNEL_REGION_END = KERNEL_REGION_VADDR + KERNEL_REGION_SIZE,
47}; 42};
48 43
49/// Central class that handles all memory operations and state. 44/// Central class that handles all memory operations and state.
@@ -56,7 +51,7 @@ public:
56 Memory& operator=(const Memory&) = delete; 51 Memory& operator=(const Memory&) = delete;
57 52
58 Memory(Memory&&) = default; 53 Memory(Memory&&) = default;
59 Memory& operator=(Memory&&) = default; 54 Memory& operator=(Memory&&) = delete;
60 55
61 /** 56 /**
62 * Resets the state of the Memory system. 57 * Resets the state of the Memory system.
@@ -92,24 +87,13 @@ public:
92 87
93 /** 88 /**
94 * Checks whether or not the supplied address is a valid virtual 89 * Checks whether or not the supplied address is a valid virtual
95 * address for the given process.
96 *
97 * @param process The emulated process to check the address against.
98 * @param vaddr The virtual address to check the validity of.
99 *
100 * @returns True if the given virtual address is valid, false otherwise.
101 */
102 bool IsValidVirtualAddress(const Kernel::KProcess& process, VAddr vaddr) const;
103
104 /**
105 * Checks whether or not the supplied address is a valid virtual
106 * address for the current process. 90 * address for the current process.
107 * 91 *
108 * @param vaddr The virtual address to check the validity of. 92 * @param vaddr The virtual address to check the validity of.
109 * 93 *
110 * @returns True if the given virtual address is valid, false otherwise. 94 * @returns True if the given virtual address is valid, false otherwise.
111 */ 95 */
112 bool IsValidVirtualAddress(VAddr vaddr) const; 96 [[nodiscard]] bool IsValidVirtualAddress(VAddr vaddr) const;
113 97
114 /** 98 /**
115 * Gets a pointer to the given address. 99 * Gets a pointer to the given address.
@@ -134,7 +118,7 @@ public:
134 * @returns The pointer to the given address, if the address is valid. 118 * @returns The pointer to the given address, if the address is valid.
135 * If the address is not valid, nullptr will be returned. 119 * If the address is not valid, nullptr will be returned.
136 */ 120 */
137 const u8* GetPointer(VAddr vaddr) const; 121 [[nodiscard]] const u8* GetPointer(VAddr vaddr) const;
138 122
139 template <typename T> 123 template <typename T>
140 const T* GetPointer(VAddr vaddr) const { 124 const T* GetPointer(VAddr vaddr) const {
@@ -328,27 +312,6 @@ public:
328 std::size_t size); 312 std::size_t size);
329 313
330 /** 314 /**
331 * Reads a contiguous block of bytes from a specified process' address space.
332 * This unsafe version does not trigger GPU flushing.
333 *
334 * @param process The process to read the data from.
335 * @param src_addr The virtual address to begin reading from.
336 * @param dest_buffer The buffer to place the read bytes into.
337 * @param size The amount of data to read, in bytes.
338 *
339 * @note If a size of 0 is specified, then this function reads nothing and
340 * no attempts to access memory are made at all.
341 *
342 * @pre dest_buffer must be at least size bytes in length, otherwise a
343 * buffer overrun will occur.
344 *
345 * @post The range [dest_buffer, size) contains the read bytes from the
346 * process' address space.
347 */
348 void ReadBlockUnsafe(const Kernel::KProcess& process, VAddr src_addr, void* dest_buffer,
349 std::size_t size);
350
351 /**
352 * Reads a contiguous block of bytes from the current process' address space. 315 * Reads a contiguous block of bytes from the current process' address space.
353 * 316 *
354 * @param src_addr The virtual address to begin reading from. 317 * @param src_addr The virtual address to begin reading from.
@@ -409,26 +372,6 @@ public:
409 std::size_t size); 372 std::size_t size);
410 373
411 /** 374 /**
412 * Writes a range of bytes into a given process' address space at the specified
413 * virtual address.
414 * This unsafe version does not invalidate GPU Memory.
415 *
416 * @param process The process to write data into the address space of.
417 * @param dest_addr The destination virtual address to begin writing the data at.
418 * @param src_buffer The data to write into the process' address space.
419 * @param size The size of the data to write, in bytes.
420 *
421 * @post The address range [dest_addr, size) in the process' address space
422 * contains the data that was within src_buffer.
423 *
424 * @post If an attempt is made to write into an unmapped region of memory, the writes
425 * will be ignored and an error will be logged.
426 *
427 */
428 void WriteBlockUnsafe(const Kernel::KProcess& process, VAddr dest_addr, const void* src_buffer,
429 std::size_t size);
430
431 /**
432 * Writes a range of bytes into the current process' address space at the specified 375 * Writes a range of bytes into the current process' address space at the specified
433 * virtual address. 376 * virtual address.
434 * 377 *
@@ -468,29 +411,6 @@ public:
468 void WriteBlockUnsafe(VAddr dest_addr, const void* src_buffer, std::size_t size); 411 void WriteBlockUnsafe(VAddr dest_addr, const void* src_buffer, std::size_t size);
469 412
470 /** 413 /**
471 * Fills the specified address range within a process' address space with zeroes.
472 *
473 * @param process The process that will have a portion of its memory zeroed out.
474 * @param dest_addr The starting virtual address of the range to zero out.
475 * @param size The size of the address range to zero out, in bytes.
476 *
477 * @post The range [dest_addr, size) within the process' address space is
478 * filled with zeroes.
479 */
480 void ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size);
481
482 /**
483 * Fills the specified address range within the current process' address space with zeroes.
484 *
485 * @param dest_addr The starting virtual address of the range to zero out.
486 * @param size The size of the address range to zero out, in bytes.
487 *
488 * @post The range [dest_addr, size) within the current process' address space is
489 * filled with zeroes.
490 */
491 void ZeroBlock(VAddr dest_addr, std::size_t size);
492
493 /**
494 * Copies data within a process' address space to another location within the 414 * Copies data within a process' address space to another location within the
495 * same address space. 415 * same address space.
496 * 416 *
@@ -506,19 +426,6 @@ public:
506 std::size_t size); 426 std::size_t size);
507 427
508 /** 428 /**
509 * Copies data within the current process' address space to another location within the
510 * same address space.
511 *
512 * @param dest_addr The destination virtual address to begin copying the data into.
513 * @param src_addr The source virtual address to begin copying the data from.
514 * @param size The size of the data to copy, in bytes.
515 *
516 * @post The range [dest_addr, size) within the current process' address space
517 * contains the same data within the range [src_addr, size).
518 */
519 void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size);
520
521 /**
522 * Marks each page within the specified address range as cached or uncached. 429 * Marks each page within the specified address range as cached or uncached.
523 * 430 *
524 * @param vaddr The virtual address indicating the start of the address range. 431 * @param vaddr The virtual address indicating the start of the address range.
@@ -535,7 +442,4 @@ private:
535 std::unique_ptr<Impl> impl; 442 std::unique_ptr<Impl> impl;
536}; 443};
537 444
538/// Determines if the given VAddr is a kernel address
539bool IsKernelVirtualAddress(VAddr vaddr);
540
541} // namespace Core::Memory 445} // namespace Core::Memory
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index 8de3d4520..ff23230f0 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -304,10 +304,10 @@ std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers([
304} 304}
305 305
306std::string GenerateKeyboardParam(int key_code) { 306std::string GenerateKeyboardParam(int key_code) {
307 Common::ParamPackage param{ 307 Common::ParamPackage param;
308 {"engine", "keyboard"}, 308 param.Set("engine", "keyboard");
309 {"code", std::to_string(key_code)}, 309 param.Set("code", key_code);
310 }; 310 param.Set("toggle", false);
311 return param.Serialize(); 311 return param.Serialize();
312} 312}
313 313
diff --git a/src/input_common/mouse/mouse_poller.cpp b/src/input_common/mouse/mouse_poller.cpp
index efcdd85d2..090b26972 100644
--- a/src/input_common/mouse/mouse_poller.cpp
+++ b/src/input_common/mouse/mouse_poller.cpp
@@ -57,6 +57,7 @@ Common::ParamPackage MouseButtonFactory::GetNextInput() const {
57 if (pad.button != MouseInput::MouseButton::Undefined) { 57 if (pad.button != MouseInput::MouseButton::Undefined) {
58 params.Set("engine", "mouse"); 58 params.Set("engine", "mouse");
59 params.Set("button", static_cast<u16>(pad.button)); 59 params.Set("button", static_cast<u16>(pad.button));
60 params.Set("toggle", false);
60 return params; 61 return params;
61 } 62 }
62 } 63 }
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index 70a0ba09c..f1f950d8a 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -82,6 +82,12 @@ public:
82 state.buttons.insert_or_assign(button, value); 82 state.buttons.insert_or_assign(button, value);
83 } 83 }
84 84
85 void PreSetButton(int button) {
86 if (!state.buttons.contains(button)) {
87 SetButton(button, false);
88 }
89 }
90
85 void SetMotion(SDL_ControllerSensorEvent event) { 91 void SetMotion(SDL_ControllerSensorEvent event) {
86 constexpr float gravity_constant = 9.80665f; 92 constexpr float gravity_constant = 9.80665f;
87 std::lock_guard lock{mutex}; 93 std::lock_guard lock{mutex};
@@ -155,9 +161,16 @@ public:
155 state.axes.insert_or_assign(axis, value); 161 state.axes.insert_or_assign(axis, value);
156 } 162 }
157 163
158 float GetAxis(int axis, float range) const { 164 void PreSetAxis(int axis) {
165 if (!state.axes.contains(axis)) {
166 SetAxis(axis, 0);
167 }
168 }
169
170 float GetAxis(int axis, float range, float offset) const {
159 std::lock_guard lock{mutex}; 171 std::lock_guard lock{mutex};
160 return static_cast<float>(state.axes.at(axis)) / (32767.0f * range); 172 const float value = static_cast<float>(state.axes.at(axis)) / 32767.0f;
173 return (value + offset) / range;
161 } 174 }
162 175
163 bool RumblePlay(u16 amp_low, u16 amp_high) { 176 bool RumblePlay(u16 amp_low, u16 amp_high) {
@@ -174,9 +187,10 @@ public:
174 return false; 187 return false;
175 } 188 }
176 189
177 std::tuple<float, float> GetAnalog(int axis_x, int axis_y, float range) const { 190 std::tuple<float, float> GetAnalog(int axis_x, int axis_y, float range, float offset_x,
178 float x = GetAxis(axis_x, range); 191 float offset_y) const {
179 float y = GetAxis(axis_y, range); 192 float x = GetAxis(axis_x, range, offset_x);
193 float y = GetAxis(axis_y, range, offset_y);
180 y = -y; // 3DS uses an y-axis inverse from SDL 194 y = -y; // 3DS uses an y-axis inverse from SDL
181 195
182 // Make sure the coordinates are in the unit circle, 196 // Make sure the coordinates are in the unit circle,
@@ -483,7 +497,7 @@ public:
483 trigger_if_greater(trigger_if_greater_) {} 497 trigger_if_greater(trigger_if_greater_) {}
484 498
485 bool GetStatus() const override { 499 bool GetStatus() const override {
486 const float axis_value = joystick->GetAxis(axis, 1.0f); 500 const float axis_value = joystick->GetAxis(axis, 1.0f, 0.0f);
487 if (trigger_if_greater) { 501 if (trigger_if_greater) {
488 return axis_value > threshold; 502 return axis_value > threshold;
489 } 503 }
@@ -500,12 +514,14 @@ private:
500class SDLAnalog final : public Input::AnalogDevice { 514class SDLAnalog final : public Input::AnalogDevice {
501public: 515public:
502 explicit SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_, 516 explicit SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_,
503 bool invert_x_, bool invert_y_, float deadzone_, float range_) 517 bool invert_x_, bool invert_y_, float deadzone_, float range_,
518 float offset_x_, float offset_y_)
504 : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), invert_x(invert_x_), 519 : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), invert_x(invert_x_),
505 invert_y(invert_y_), deadzone(deadzone_), range(range_) {} 520 invert_y(invert_y_), deadzone(deadzone_), range(range_), offset_x(offset_x_),
521 offset_y(offset_y_) {}
506 522
507 std::tuple<float, float> GetStatus() const override { 523 std::tuple<float, float> GetStatus() const override {
508 auto [x, y] = joystick->GetAnalog(axis_x, axis_y, range); 524 auto [x, y] = joystick->GetAnalog(axis_x, axis_y, range, offset_x, offset_y);
509 const float r = std::sqrt((x * x) + (y * y)); 525 const float r = std::sqrt((x * x) + (y * y));
510 if (invert_x) { 526 if (invert_x) {
511 x = -x; 527 x = -x;
@@ -522,8 +538,8 @@ public:
522 } 538 }
523 539
524 std::tuple<float, float> GetRawStatus() const override { 540 std::tuple<float, float> GetRawStatus() const override {
525 const float x = joystick->GetAxis(axis_x, range); 541 const float x = joystick->GetAxis(axis_x, range, offset_x);
526 const float y = joystick->GetAxis(axis_y, range); 542 const float y = joystick->GetAxis(axis_y, range, offset_y);
527 return {x, -y}; 543 return {x, -y};
528 } 544 }
529 545
@@ -555,6 +571,8 @@ private:
555 const bool invert_y; 571 const bool invert_y;
556 const float deadzone; 572 const float deadzone;
557 const float range; 573 const float range;
574 const float offset_x;
575 const float offset_y;
558}; 576};
559 577
560class SDLVibration final : public Input::VibrationDevice { 578class SDLVibration final : public Input::VibrationDevice {
@@ -621,7 +639,7 @@ public:
621 trigger_if_greater(trigger_if_greater_) {} 639 trigger_if_greater(trigger_if_greater_) {}
622 640
623 Input::MotionStatus GetStatus() const override { 641 Input::MotionStatus GetStatus() const override {
624 const float axis_value = joystick->GetAxis(axis, 1.0f); 642 const float axis_value = joystick->GetAxis(axis, 1.0f, 0.0f);
625 bool trigger = axis_value < threshold; 643 bool trigger = axis_value < threshold;
626 if (trigger_if_greater) { 644 if (trigger_if_greater) {
627 trigger = axis_value > threshold; 645 trigger = axis_value > threshold;
@@ -720,13 +738,13 @@ public:
720 LOG_ERROR(Input, "Unknown direction {}", direction_name); 738 LOG_ERROR(Input, "Unknown direction {}", direction_name);
721 } 739 }
722 // This is necessary so accessing GetAxis with axis won't crash 740 // This is necessary so accessing GetAxis with axis won't crash
723 joystick->SetAxis(axis, 0); 741 joystick->PreSetAxis(axis);
724 return std::make_unique<SDLAxisButton>(joystick, axis, threshold, trigger_if_greater); 742 return std::make_unique<SDLAxisButton>(joystick, axis, threshold, trigger_if_greater);
725 } 743 }
726 744
727 const int button = params.Get("button", 0); 745 const int button = params.Get("button", 0);
728 // This is necessary so accessing GetButton with button won't crash 746 // This is necessary so accessing GetButton with button won't crash
729 joystick->SetButton(button, false); 747 joystick->PreSetButton(button);
730 return std::make_unique<SDLButton>(joystick, button, toggle); 748 return std::make_unique<SDLButton>(joystick, button, toggle);
731 } 749 }
732 750
@@ -757,13 +775,15 @@ public:
757 const std::string invert_y_value = params.Get("invert_y", "+"); 775 const std::string invert_y_value = params.Get("invert_y", "+");
758 const bool invert_x = invert_x_value == "-"; 776 const bool invert_x = invert_x_value == "-";
759 const bool invert_y = invert_y_value == "-"; 777 const bool invert_y = invert_y_value == "-";
778 const float offset_x = params.Get("offset_x", 0.0f);
779 const float offset_y = params.Get("offset_y", 0.0f);
760 auto joystick = state.GetSDLJoystickByGUID(guid, port); 780 auto joystick = state.GetSDLJoystickByGUID(guid, port);
761 781
762 // This is necessary so accessing GetAxis with axis_x and axis_y won't crash 782 // This is necessary so accessing GetAxis with axis_x and axis_y won't crash
763 joystick->SetAxis(axis_x, 0); 783 joystick->PreSetAxis(axis_x);
764 joystick->SetAxis(axis_y, 0); 784 joystick->PreSetAxis(axis_y);
765 return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, invert_x, invert_y, deadzone, 785 return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, invert_x, invert_y, deadzone,
766 range); 786 range, offset_x, offset_y);
767 } 787 }
768 788
769private: 789private:
@@ -844,13 +864,13 @@ public:
844 LOG_ERROR(Input, "Unknown direction {}", direction_name); 864 LOG_ERROR(Input, "Unknown direction {}", direction_name);
845 } 865 }
846 // This is necessary so accessing GetAxis with axis won't crash 866 // This is necessary so accessing GetAxis with axis won't crash
847 joystick->SetAxis(axis, 0); 867 joystick->PreSetAxis(axis);
848 return std::make_unique<SDLAxisMotion>(joystick, axis, threshold, trigger_if_greater); 868 return std::make_unique<SDLAxisMotion>(joystick, axis, threshold, trigger_if_greater);
849 } 869 }
850 870
851 const int button = params.Get("button", 0); 871 const int button = params.Get("button", 0);
852 // This is necessary so accessing GetButton with button won't crash 872 // This is necessary so accessing GetButton with button won't crash
853 joystick->SetButton(button, false); 873 joystick->PreSetButton(button);
854 return std::make_unique<SDLButtonMotion>(joystick, button); 874 return std::make_unique<SDLButtonMotion>(joystick, button);
855 } 875 }
856 876
@@ -995,6 +1015,7 @@ Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid
995 params.Set("port", port); 1015 params.Set("port", port);
996 params.Set("guid", std::move(guid)); 1016 params.Set("guid", std::move(guid));
997 params.Set("button", button); 1017 params.Set("button", button);
1018 params.Set("toggle", false);
998 return params; 1019 return params;
999} 1020}
1000 1021
@@ -1134,13 +1155,15 @@ Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& gu
1134} 1155}
1135 1156
1136Common::ParamPackage BuildParamPackageForAnalog(int port, const std::string& guid, int axis_x, 1157Common::ParamPackage BuildParamPackageForAnalog(int port, const std::string& guid, int axis_x,
1137 int axis_y) { 1158 int axis_y, float offset_x, float offset_y) {
1138 Common::ParamPackage params; 1159 Common::ParamPackage params;
1139 params.Set("engine", "sdl"); 1160 params.Set("engine", "sdl");
1140 params.Set("port", port); 1161 params.Set("port", port);
1141 params.Set("guid", guid); 1162 params.Set("guid", guid);
1142 params.Set("axis_x", axis_x); 1163 params.Set("axis_x", axis_x);
1143 params.Set("axis_y", axis_y); 1164 params.Set("axis_y", axis_y);
1165 params.Set("offset_x", offset_x);
1166 params.Set("offset_y", offset_y);
1144 params.Set("invert_x", "+"); 1167 params.Set("invert_x", "+");
1145 params.Set("invert_y", "+"); 1168 params.Set("invert_y", "+");
1146 return params; 1169 return params;
@@ -1342,24 +1365,39 @@ AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& pa
1342 const auto& binding_left_y = 1365 const auto& binding_left_y =
1343 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY); 1366 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
1344 if (params.Has("guid2")) { 1367 if (params.Has("guid2")) {
1368 joystick2->PreSetAxis(binding_left_x.value.axis);
1369 joystick2->PreSetAxis(binding_left_y.value.axis);
1370 const auto left_offset_x = -joystick2->GetAxis(binding_left_x.value.axis, 1.0f, 0);
1371 const auto left_offset_y = -joystick2->GetAxis(binding_left_y.value.axis, 1.0f, 0);
1345 mapping.insert_or_assign( 1372 mapping.insert_or_assign(
1346 Settings::NativeAnalog::LStick, 1373 Settings::NativeAnalog::LStick,
1347 BuildParamPackageForAnalog(joystick2->GetPort(), joystick2->GetGUID(), 1374 BuildParamPackageForAnalog(joystick2->GetPort(), joystick2->GetGUID(),
1348 binding_left_x.value.axis, binding_left_y.value.axis)); 1375 binding_left_x.value.axis, binding_left_y.value.axis,
1376 left_offset_x, left_offset_y));
1349 } else { 1377 } else {
1378 joystick->PreSetAxis(binding_left_x.value.axis);
1379 joystick->PreSetAxis(binding_left_y.value.axis);
1380 const auto left_offset_x = -joystick->GetAxis(binding_left_x.value.axis, 1.0f, 0);
1381 const auto left_offset_y = -joystick->GetAxis(binding_left_y.value.axis, 1.0f, 0);
1350 mapping.insert_or_assign( 1382 mapping.insert_or_assign(
1351 Settings::NativeAnalog::LStick, 1383 Settings::NativeAnalog::LStick,
1352 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), 1384 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
1353 binding_left_x.value.axis, binding_left_y.value.axis)); 1385 binding_left_x.value.axis, binding_left_y.value.axis,
1386 left_offset_x, left_offset_y));
1354 } 1387 }
1355 const auto& binding_right_x = 1388 const auto& binding_right_x =
1356 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX); 1389 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
1357 const auto& binding_right_y = 1390 const auto& binding_right_y =
1358 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY); 1391 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY);
1392 joystick->PreSetAxis(binding_right_x.value.axis);
1393 joystick->PreSetAxis(binding_right_y.value.axis);
1394 const auto right_offset_x = -joystick->GetAxis(binding_right_x.value.axis, 1.0f, 0);
1395 const auto right_offset_y = -joystick->GetAxis(binding_right_y.value.axis, 1.0f, 0);
1359 mapping.insert_or_assign(Settings::NativeAnalog::RStick, 1396 mapping.insert_or_assign(Settings::NativeAnalog::RStick,
1360 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), 1397 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
1361 binding_right_x.value.axis, 1398 binding_right_x.value.axis,
1362 binding_right_y.value.axis)); 1399 binding_right_y.value.axis, right_offset_x,
1400 right_offset_y));
1363 return mapping; 1401 return mapping;
1364} 1402}
1365 1403
@@ -1563,8 +1601,9 @@ public:
1563 } 1601 }
1564 1602
1565 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) { 1603 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
1604 // Set offset to zero since the joystick is not on center
1566 auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), 1605 auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
1567 first_axis, axis); 1606 first_axis, axis, 0, 0);
1568 first_axis = -1; 1607 first_axis = -1;
1569 return params; 1608 return params;
1570 } 1609 }
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 1eb67c051..2f6cdd216 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -97,6 +97,7 @@ add_library(video_core STATIC
97 renderer_opengl/gl_stream_buffer.h 97 renderer_opengl/gl_stream_buffer.h
98 renderer_opengl/gl_texture_cache.cpp 98 renderer_opengl/gl_texture_cache.cpp
99 renderer_opengl/gl_texture_cache.h 99 renderer_opengl/gl_texture_cache.h
100 renderer_opengl/gl_texture_cache_base.cpp
100 renderer_opengl/gl_query_cache.cpp 101 renderer_opengl/gl_query_cache.cpp
101 renderer_opengl/gl_query_cache.h 102 renderer_opengl/gl_query_cache.h
102 renderer_opengl/maxwell_to_gl.h 103 renderer_opengl/maxwell_to_gl.h
@@ -155,6 +156,7 @@ add_library(video_core STATIC
155 renderer_vulkan/vk_swapchain.h 156 renderer_vulkan/vk_swapchain.h
156 renderer_vulkan/vk_texture_cache.cpp 157 renderer_vulkan/vk_texture_cache.cpp
157 renderer_vulkan/vk_texture_cache.h 158 renderer_vulkan/vk_texture_cache.h
159 renderer_vulkan/vk_texture_cache_base.cpp
158 renderer_vulkan/vk_update_descriptor.cpp 160 renderer_vulkan/vk_update_descriptor.cpp
159 renderer_vulkan/vk_update_descriptor.h 161 renderer_vulkan/vk_update_descriptor.h
160 shader_cache.cpp 162 shader_cache.cpp
@@ -186,6 +188,7 @@ add_library(video_core STATIC
186 texture_cache/samples_helper.h 188 texture_cache/samples_helper.h
187 texture_cache/slot_vector.h 189 texture_cache/slot_vector.h
188 texture_cache/texture_cache.h 190 texture_cache/texture_cache.h
191 texture_cache/texture_cache_base.h
189 texture_cache/types.h 192 texture_cache/types.h
190 texture_cache/util.cpp 193 texture_cache/util.cpp
191 texture_cache/util.h 194 texture_cache/util.h
diff --git a/src/video_core/command_classes/codecs/vp9.cpp b/src/video_core/command_classes/codecs/vp9.cpp
index 7eecb3991..70030066a 100644
--- a/src/video_core/command_classes/codecs/vp9.cpp
+++ b/src/video_core/command_classes/codecs/vp9.cpp
@@ -397,14 +397,14 @@ Vp9FrameContainer VP9::GetCurrentFrame(const NvdecCommon::NvdecRegisters& state)
397 next_frame = std::move(temp); 397 next_frame = std::move(temp);
398 } else { 398 } else {
399 next_frame.info = current_frame.info; 399 next_frame.info = current_frame.info;
400 next_frame.bit_stream = std::move(current_frame.bit_stream); 400 next_frame.bit_stream = current_frame.bit_stream;
401 } 401 }
402 return current_frame; 402 return current_frame;
403} 403}
404 404
405std::vector<u8> VP9::ComposeCompressedHeader() { 405std::vector<u8> VP9::ComposeCompressedHeader() {
406 VpxRangeEncoder writer{}; 406 VpxRangeEncoder writer{};
407 const bool update_probs = current_frame_info.show_frame && !current_frame_info.is_key_frame; 407 const bool update_probs = !current_frame_info.is_key_frame && current_frame_info.show_frame;
408 if (!current_frame_info.lossless) { 408 if (!current_frame_info.lossless) {
409 if (static_cast<u32>(current_frame_info.transform_mode) >= 3) { 409 if (static_cast<u32>(current_frame_info.transform_mode) >= 3) {
410 writer.Write(3, 2); 410 writer.Write(3, 2);
diff --git a/src/video_core/command_classes/codecs/vp9_types.h b/src/video_core/command_classes/codecs/vp9_types.h
index 6820afa26..87eafdb03 100644
--- a/src/video_core/command_classes/codecs/vp9_types.h
+++ b/src/video_core/command_classes/codecs/vp9_types.h
@@ -176,7 +176,7 @@ struct PictureInfo {
176 .frame_size_changed = (vp9_flags & FrameFlags::FrameSizeChanged) != 0, 176 .frame_size_changed = (vp9_flags & FrameFlags::FrameSizeChanged) != 0,
177 .error_resilient_mode = (vp9_flags & FrameFlags::ErrorResilientMode) != 0, 177 .error_resilient_mode = (vp9_flags & FrameFlags::ErrorResilientMode) != 0,
178 .last_frame_shown = (vp9_flags & FrameFlags::LastShowFrame) != 0, 178 .last_frame_shown = (vp9_flags & FrameFlags::LastShowFrame) != 0,
179 .show_frame = false, 179 .show_frame = true,
180 .ref_frame_sign_bias = ref_frame_sign_bias, 180 .ref_frame_sign_bias = ref_frame_sign_bias,
181 .base_q_index = base_q_index, 181 .base_q_index = base_q_index,
182 .y_dc_delta_q = y_dc_delta_q, 182 .y_dc_delta_q = y_dc_delta_q,
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
index fac0034fb..bccb37a58 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
@@ -15,7 +15,7 @@
15#include "video_core/renderer_opengl/gl_shader_util.h" 15#include "video_core/renderer_opengl/gl_shader_util.h"
16#include "video_core/renderer_opengl/gl_state_tracker.h" 16#include "video_core/renderer_opengl/gl_state_tracker.h"
17#include "video_core/shader_notify.h" 17#include "video_core/shader_notify.h"
18#include "video_core/texture_cache/texture_cache.h" 18#include "video_core/texture_cache/texture_cache_base.h"
19 19
20#if defined(_MSC_VER) && defined(NDEBUG) 20#if defined(_MSC_VER) && defined(NDEBUG)
21#define LAMBDA_FORCEINLINE [[msvc::forceinline]] 21#define LAMBDA_FORCEINLINE [[msvc::forceinline]]
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 41d2b73f4..b909c387e 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -32,7 +32,7 @@
32#include "video_core/renderer_opengl/maxwell_to_gl.h" 32#include "video_core/renderer_opengl/maxwell_to_gl.h"
33#include "video_core/renderer_opengl/renderer_opengl.h" 33#include "video_core/renderer_opengl/renderer_opengl.h"
34#include "video_core/shader_cache.h" 34#include "video_core/shader_cache.h"
35#include "video_core/texture_cache/texture_cache.h" 35#include "video_core/texture_cache/texture_cache_base.h"
36 36
37namespace OpenGL { 37namespace OpenGL {
38 38
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index c373c9cb4..b0aee6cc1 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -18,10 +18,8 @@
18#include "video_core/renderer_opengl/maxwell_to_gl.h" 18#include "video_core/renderer_opengl/maxwell_to_gl.h"
19#include "video_core/renderer_opengl/util_shaders.h" 19#include "video_core/renderer_opengl/util_shaders.h"
20#include "video_core/surface.h" 20#include "video_core/surface.h"
21#include "video_core/texture_cache/format_lookup_table.h" 21#include "video_core/texture_cache/formatter.h"
22#include "video_core/texture_cache/samples_helper.h" 22#include "video_core/texture_cache/samples_helper.h"
23#include "video_core/texture_cache/texture_cache.h"
24#include "video_core/textures/decoders.h"
25 23
26namespace OpenGL { 24namespace OpenGL {
27namespace { 25namespace {
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index 921072ebe..4a4f6301c 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -12,7 +12,7 @@
12#include "shader_recompiler/shader_info.h" 12#include "shader_recompiler/shader_info.h"
13#include "video_core/renderer_opengl/gl_resource_manager.h" 13#include "video_core/renderer_opengl/gl_resource_manager.h"
14#include "video_core/renderer_opengl/util_shaders.h" 14#include "video_core/renderer_opengl/util_shaders.h"
15#include "video_core/texture_cache/texture_cache.h" 15#include "video_core/texture_cache/texture_cache_base.h"
16 16
17namespace OpenGL { 17namespace OpenGL {
18 18
diff --git a/src/video_core/renderer_opengl/gl_texture_cache_base.cpp b/src/video_core/renderer_opengl/gl_texture_cache_base.cpp
new file mode 100644
index 000000000..385358fea
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_texture_cache_base.cpp
@@ -0,0 +1,10 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "video_core/renderer_opengl/gl_texture_cache.h"
6#include "video_core/texture_cache/texture_cache.h"
7
8namespace VideoCommon {
9template class VideoCommon::TextureCache<OpenGL::TextureCacheParams>;
10}
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 23cef2996..3ac18ea54 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -32,7 +32,7 @@
32#include "video_core/renderer_vulkan/vk_texture_cache.h" 32#include "video_core/renderer_vulkan/vk_texture_cache.h"
33#include "video_core/renderer_vulkan/vk_update_descriptor.h" 33#include "video_core/renderer_vulkan/vk_update_descriptor.h"
34#include "video_core/shader_cache.h" 34#include "video_core/shader_cache.h"
35#include "video_core/texture_cache/texture_cache.h" 35#include "video_core/texture_cache/texture_cache_base.h"
36#include "video_core/vulkan_common/vulkan_device.h" 36#include "video_core/vulkan_common/vulkan_device.h"
37#include "video_core/vulkan_common/vulkan_wrapper.h" 37#include "video_core/vulkan_common/vulkan_wrapper.h"
38 38
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 8e029bcb3..8f4df7122 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -19,6 +19,8 @@
19#include "video_core/renderer_vulkan/vk_scheduler.h" 19#include "video_core/renderer_vulkan/vk_scheduler.h"
20#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" 20#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
21#include "video_core/renderer_vulkan/vk_texture_cache.h" 21#include "video_core/renderer_vulkan/vk_texture_cache.h"
22#include "video_core/texture_cache/formatter.h"
23#include "video_core/texture_cache/samples_helper.h"
22#include "video_core/vulkan_common/vulkan_device.h" 24#include "video_core/vulkan_common/vulkan_device.h"
23#include "video_core/vulkan_common/vulkan_memory_allocator.h" 25#include "video_core/vulkan_common/vulkan_memory_allocator.h"
24#include "video_core/vulkan_common/vulkan_wrapper.h" 26#include "video_core/vulkan_common/vulkan_wrapper.h"
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index 0b73d55f8..5fe6b7ba3 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -9,7 +9,7 @@
9 9
10#include "shader_recompiler/shader_info.h" 10#include "shader_recompiler/shader_info.h"
11#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" 11#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
12#include "video_core/texture_cache/texture_cache.h" 12#include "video_core/texture_cache/texture_cache_base.h"
13#include "video_core/vulkan_common/vulkan_memory_allocator.h" 13#include "video_core/vulkan_common/vulkan_memory_allocator.h"
14#include "video_core/vulkan_common/vulkan_wrapper.h" 14#include "video_core/vulkan_common/vulkan_wrapper.h"
15 15
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache_base.cpp b/src/video_core/renderer_vulkan/vk_texture_cache_base.cpp
new file mode 100644
index 000000000..44e688342
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_texture_cache_base.cpp
@@ -0,0 +1,10 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "video_core/renderer_vulkan/vk_texture_cache.h"
6#include "video_core/texture_cache/texture_cache.h"
7
8namespace VideoCommon {
9template class VideoCommon::TextureCache<Vulkan::TextureCacheParams>;
10}
diff --git a/src/video_core/texture_cache/image_view_info.cpp b/src/video_core/texture_cache/image_view_info.cpp
index faf5b151f..6527e14c8 100644
--- a/src/video_core/texture_cache/image_view_info.cpp
+++ b/src/video_core/texture_cache/image_view_info.cpp
@@ -6,7 +6,7 @@
6 6
7#include "common/assert.h" 7#include "common/assert.h"
8#include "video_core/texture_cache/image_view_info.h" 8#include "video_core/texture_cache/image_view_info.h"
9#include "video_core/texture_cache/texture_cache.h" 9#include "video_core/texture_cache/texture_cache_base.h"
10#include "video_core/texture_cache/types.h" 10#include "video_core/texture_cache/types.h"
11#include "video_core/textures/texture.h" 11#include "video_core/textures/texture.h"
12 12
@@ -14,6 +14,8 @@ namespace VideoCommon {
14 14
15namespace { 15namespace {
16 16
17using Tegra::Texture::TextureType;
18
17constexpr u8 RENDER_TARGET_SWIZZLE = std::numeric_limits<u8>::max(); 19constexpr u8 RENDER_TARGET_SWIZZLE = std::numeric_limits<u8>::max();
18 20
19[[nodiscard]] u8 CastSwizzle(SwizzleSource source) { 21[[nodiscard]] u8 CastSwizzle(SwizzleSource source) {
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index f34c9d9ca..a087498ff 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -4,48 +4,11 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <algorithm>
8#include <array>
9#include <bit>
10#include <memory>
11#include <mutex>
12#include <optional>
13#include <span>
14#include <type_traits>
15#include <unordered_map>
16#include <unordered_set>
17#include <utility>
18#include <vector>
19
20#include <boost/container/small_vector.hpp>
21
22#include "common/alignment.h" 7#include "common/alignment.h"
23#include "common/common_types.h"
24#include "common/literals.h"
25#include "common/logging/log.h"
26#include "common/settings.h" 8#include "common/settings.h"
27#include "video_core/compatible_formats.h"
28#include "video_core/delayed_destruction_ring.h"
29#include "video_core/dirty_flags.h" 9#include "video_core/dirty_flags.h"
30#include "video_core/engines/fermi_2d.h"
31#include "video_core/engines/kepler_compute.h"
32#include "video_core/engines/maxwell_3d.h"
33#include "video_core/memory_manager.h"
34#include "video_core/rasterizer_interface.h"
35#include "video_core/surface.h"
36#include "video_core/texture_cache/descriptor_table.h"
37#include "video_core/texture_cache/format_lookup_table.h"
38#include "video_core/texture_cache/formatter.h"
39#include "video_core/texture_cache/image_base.h"
40#include "video_core/texture_cache/image_info.h"
41#include "video_core/texture_cache/image_view_base.h"
42#include "video_core/texture_cache/image_view_info.h"
43#include "video_core/texture_cache/render_targets.h"
44#include "video_core/texture_cache/samples_helper.h" 10#include "video_core/texture_cache/samples_helper.h"
45#include "video_core/texture_cache/slot_vector.h" 11#include "video_core/texture_cache/texture_cache_base.h"
46#include "video_core/texture_cache/types.h"
47#include "video_core/texture_cache/util.h"
48#include "video_core/textures/texture.h"
49 12
50namespace VideoCommon { 13namespace VideoCommon {
51 14
@@ -62,352 +25,6 @@ using VideoCore::Surface::SurfaceType;
62using namespace Common::Literals; 25using namespace Common::Literals;
63 26
64template <class P> 27template <class P>
65class TextureCache {
66 /// Address shift for caching images into a hash table
67 static constexpr u64 PAGE_BITS = 20;
68
69 /// Enables debugging features to the texture cache
70 static constexpr bool ENABLE_VALIDATION = P::ENABLE_VALIDATION;
71 /// Implement blits as copies between framebuffers
72 static constexpr bool FRAMEBUFFER_BLITS = P::FRAMEBUFFER_BLITS;
73 /// True when some copies have to be emulated
74 static constexpr bool HAS_EMULATED_COPIES = P::HAS_EMULATED_COPIES;
75 /// True when the API can provide info about the memory of the device.
76 static constexpr bool HAS_DEVICE_MEMORY_INFO = P::HAS_DEVICE_MEMORY_INFO;
77
78 /// Image view ID for null descriptors
79 static constexpr ImageViewId NULL_IMAGE_VIEW_ID{0};
80 /// Sampler ID for bugged sampler ids
81 static constexpr SamplerId NULL_SAMPLER_ID{0};
82
83 static constexpr u64 DEFAULT_EXPECTED_MEMORY = 1_GiB;
84 static constexpr u64 DEFAULT_CRITICAL_MEMORY = 2_GiB;
85
86 using Runtime = typename P::Runtime;
87 using Image = typename P::Image;
88 using ImageAlloc = typename P::ImageAlloc;
89 using ImageView = typename P::ImageView;
90 using Sampler = typename P::Sampler;
91 using Framebuffer = typename P::Framebuffer;
92
93 struct BlitImages {
94 ImageId dst_id;
95 ImageId src_id;
96 PixelFormat dst_format;
97 PixelFormat src_format;
98 };
99
100 template <typename T>
101 struct IdentityHash {
102 [[nodiscard]] size_t operator()(T value) const noexcept {
103 return static_cast<size_t>(value);
104 }
105 };
106
107public:
108 explicit TextureCache(Runtime&, VideoCore::RasterizerInterface&, Tegra::Engines::Maxwell3D&,
109 Tegra::Engines::KeplerCompute&, Tegra::MemoryManager&);
110
111 /// Notify the cache that a new frame has been queued
112 void TickFrame();
113
114 /// Return a constant reference to the given image view id
115 [[nodiscard]] const ImageView& GetImageView(ImageViewId id) const noexcept;
116
117 /// Return a reference to the given image view id
118 [[nodiscard]] ImageView& GetImageView(ImageViewId id) noexcept;
119
120 /// Mark an image as modified from the GPU
121 void MarkModification(ImageId id) noexcept;
122
123 /// Fill image_view_ids with the graphics images in indices
124 void FillGraphicsImageViews(std::span<const u32> indices,
125 std::span<ImageViewId> image_view_ids);
126
127 /// Fill image_view_ids with the compute images in indices
128 void FillComputeImageViews(std::span<const u32> indices, std::span<ImageViewId> image_view_ids);
129
130 /// Get the sampler from the graphics descriptor table in the specified index
131 Sampler* GetGraphicsSampler(u32 index);
132
133 /// Get the sampler from the compute descriptor table in the specified index
134 Sampler* GetComputeSampler(u32 index);
135
136 /// Refresh the state for graphics image view and sampler descriptors
137 void SynchronizeGraphicsDescriptors();
138
139 /// Refresh the state for compute image view and sampler descriptors
140 void SynchronizeComputeDescriptors();
141
142 /// Update bound render targets and upload memory if necessary
143 /// @param is_clear True when the render targets are being used for clears
144 void UpdateRenderTargets(bool is_clear);
145
146 /// Find a framebuffer with the currently bound render targets
147 /// UpdateRenderTargets should be called before this
148 Framebuffer* GetFramebuffer();
149
150 /// Mark images in a range as modified from the CPU
151 void WriteMemory(VAddr cpu_addr, size_t size);
152
153 /// Download contents of host images to guest memory in a region
154 void DownloadMemory(VAddr cpu_addr, size_t size);
155
156 /// Remove images in a region
157 void UnmapMemory(VAddr cpu_addr, size_t size);
158
159 /// Remove images in a region
160 void UnmapGPUMemory(GPUVAddr gpu_addr, size_t size);
161
162 /// Blit an image with the given parameters
163 void BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
164 const Tegra::Engines::Fermi2D::Surface& src,
165 const Tegra::Engines::Fermi2D::Config& copy);
166
167 /// Invalidate the contents of the color buffer index
168 /// These contents become unspecified, the cache can assume aggressive optimizations.
169 void InvalidateColorBuffer(size_t index);
170
171 /// Invalidate the contents of the depth buffer
172 /// These contents become unspecified, the cache can assume aggressive optimizations.
173 void InvalidateDepthBuffer();
174
175 /// Try to find a cached image view in the given CPU address
176 [[nodiscard]] ImageView* TryFindFramebufferImageView(VAddr cpu_addr);
177
178 /// Return true when there are uncommitted images to be downloaded
179 [[nodiscard]] bool HasUncommittedFlushes() const noexcept;
180
181 /// Return true when the caller should wait for async downloads
182 [[nodiscard]] bool ShouldWaitAsyncFlushes() const noexcept;
183
184 /// Commit asynchronous downloads
185 void CommitAsyncFlushes();
186
187 /// Pop asynchronous downloads
188 void PopAsyncFlushes();
189
190 /// Return true when a CPU region is modified from the GPU
191 [[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size);
192
193 std::mutex mutex;
194
195private:
196 /// Iterate over all page indices in a range
197 template <typename Func>
198 static void ForEachCPUPage(VAddr addr, size_t size, Func&& func) {
199 static constexpr bool RETURNS_BOOL = std::is_same_v<std::invoke_result<Func, u64>, bool>;
200 const u64 page_end = (addr + size - 1) >> PAGE_BITS;
201 for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) {
202 if constexpr (RETURNS_BOOL) {
203 if (func(page)) {
204 break;
205 }
206 } else {
207 func(page);
208 }
209 }
210 }
211
212 template <typename Func>
213 static void ForEachGPUPage(GPUVAddr addr, size_t size, Func&& func) {
214 static constexpr bool RETURNS_BOOL = std::is_same_v<std::invoke_result<Func, u64>, bool>;
215 const u64 page_end = (addr + size - 1) >> PAGE_BITS;
216 for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) {
217 if constexpr (RETURNS_BOOL) {
218 if (func(page)) {
219 break;
220 }
221 } else {
222 func(page);
223 }
224 }
225 }
226
227 /// Runs the Garbage Collector.
228 void RunGarbageCollector();
229
230 /// Fills image_view_ids in the image views in indices
231 void FillImageViews(DescriptorTable<TICEntry>& table,
232 std::span<ImageViewId> cached_image_view_ids, std::span<const u32> indices,
233 std::span<ImageViewId> image_view_ids);
234
235 /// Find or create an image view in the guest descriptor table
236 ImageViewId VisitImageView(DescriptorTable<TICEntry>& table,
237 std::span<ImageViewId> cached_image_view_ids, u32 index);
238
239 /// Find or create a framebuffer with the given render target parameters
240 FramebufferId GetFramebufferId(const RenderTargets& key);
241
242 /// Refresh the contents (pixel data) of an image
243 void RefreshContents(Image& image, ImageId image_id);
244
245 /// Upload data from guest to an image
246 template <typename StagingBuffer>
247 void UploadImageContents(Image& image, StagingBuffer& staging_buffer);
248
249 /// Find or create an image view from a guest descriptor
250 [[nodiscard]] ImageViewId FindImageView(const TICEntry& config);
251
252 /// Create a new image view from a guest descriptor
253 [[nodiscard]] ImageViewId CreateImageView(const TICEntry& config);
254
255 /// Find or create an image from the given parameters
256 [[nodiscard]] ImageId FindOrInsertImage(const ImageInfo& info, GPUVAddr gpu_addr,
257 RelaxedOptions options = RelaxedOptions{});
258
259 /// Find an image from the given parameters
260 [[nodiscard]] ImageId FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
261 RelaxedOptions options);
262
263 /// Create an image from the given parameters
264 [[nodiscard]] ImageId InsertImage(const ImageInfo& info, GPUVAddr gpu_addr,
265 RelaxedOptions options);
266
267 /// Create a new image and join perfectly matching existing images
268 /// Remove joined images from the cache
269 [[nodiscard]] ImageId JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VAddr cpu_addr);
270
271 /// Return a blit image pair from the given guest blit parameters
272 [[nodiscard]] BlitImages GetBlitImages(const Tegra::Engines::Fermi2D::Surface& dst,
273 const Tegra::Engines::Fermi2D::Surface& src);
274
275 /// Find or create a sampler from a guest descriptor sampler
276 [[nodiscard]] SamplerId FindSampler(const TSCEntry& config);
277
278 /// Find or create an image view for the given color buffer index
279 [[nodiscard]] ImageViewId FindColorBuffer(size_t index, bool is_clear);
280
281 /// Find or create an image view for the depth buffer
282 [[nodiscard]] ImageViewId FindDepthBuffer(bool is_clear);
283
284 /// Find or create a view for a render target with the given image parameters
285 [[nodiscard]] ImageViewId FindRenderTargetView(const ImageInfo& info, GPUVAddr gpu_addr,
286 bool is_clear);
287
288 /// Iterates over all the images in a region calling func
289 template <typename Func>
290 void ForEachImageInRegion(VAddr cpu_addr, size_t size, Func&& func);
291
292 template <typename Func>
293 void ForEachImageInRegionGPU(GPUVAddr gpu_addr, size_t size, Func&& func);
294
295 template <typename Func>
296 void ForEachSparseImageInRegion(GPUVAddr gpu_addr, size_t size, Func&& func);
297
298 /// Iterates over all the images in a region calling func
299 template <typename Func>
300 void ForEachSparseSegment(ImageBase& image, Func&& func);
301
302 /// Find or create an image view in the given image with the passed parameters
303 [[nodiscard]] ImageViewId FindOrEmplaceImageView(ImageId image_id, const ImageViewInfo& info);
304
305 /// Register image in the page table
306 void RegisterImage(ImageId image);
307
308 /// Unregister image from the page table
309 void UnregisterImage(ImageId image);
310
311 /// Track CPU reads and writes for image
312 void TrackImage(ImageBase& image, ImageId image_id);
313
314 /// Stop tracking CPU reads and writes for image
315 void UntrackImage(ImageBase& image, ImageId image_id);
316
317 /// Delete image from the cache
318 void DeleteImage(ImageId image);
319
320 /// Remove image views references from the cache
321 void RemoveImageViewReferences(std::span<const ImageViewId> removed_views);
322
323 /// Remove framebuffers using the given image views from the cache
324 void RemoveFramebuffers(std::span<const ImageViewId> removed_views);
325
326 /// Mark an image as modified from the GPU
327 void MarkModification(ImageBase& image) noexcept;
328
329 /// Synchronize image aliases, copying data if needed
330 void SynchronizeAliases(ImageId image_id);
331
332 /// Prepare an image to be used
333 void PrepareImage(ImageId image_id, bool is_modification, bool invalidate);
334
335 /// Prepare an image view to be used
336 void PrepareImageView(ImageViewId image_view_id, bool is_modification, bool invalidate);
337
338 /// Execute copies from one image to the other, even if they are incompatible
339 void CopyImage(ImageId dst_id, ImageId src_id, std::span<const ImageCopy> copies);
340
341 /// Bind an image view as render target, downloading resources preemtively if needed
342 void BindRenderTarget(ImageViewId* old_id, ImageViewId new_id);
343
344 /// Create a render target from a given image and image view parameters
345 [[nodiscard]] std::pair<FramebufferId, ImageViewId> RenderTargetFromImage(
346 ImageId, const ImageViewInfo& view_info);
347
348 /// Returns true if the current clear parameters clear the whole image of a given image view
349 [[nodiscard]] bool IsFullClear(ImageViewId id);
350
351 Runtime& runtime;
352 VideoCore::RasterizerInterface& rasterizer;
353 Tegra::Engines::Maxwell3D& maxwell3d;
354 Tegra::Engines::KeplerCompute& kepler_compute;
355 Tegra::MemoryManager& gpu_memory;
356
357 DescriptorTable<TICEntry> graphics_image_table{gpu_memory};
358 DescriptorTable<TSCEntry> graphics_sampler_table{gpu_memory};
359 std::vector<SamplerId> graphics_sampler_ids;
360 std::vector<ImageViewId> graphics_image_view_ids;
361
362 DescriptorTable<TICEntry> compute_image_table{gpu_memory};
363 DescriptorTable<TSCEntry> compute_sampler_table{gpu_memory};
364 std::vector<SamplerId> compute_sampler_ids;
365 std::vector<ImageViewId> compute_image_view_ids;
366
367 RenderTargets render_targets;
368
369 std::unordered_map<TICEntry, ImageViewId> image_views;
370 std::unordered_map<TSCEntry, SamplerId> samplers;
371 std::unordered_map<RenderTargets, FramebufferId> framebuffers;
372
373 std::unordered_map<u64, std::vector<ImageMapId>, IdentityHash<u64>> page_table;
374 std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>> gpu_page_table;
375 std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>> sparse_page_table;
376
377 std::unordered_map<ImageId, std::vector<ImageViewId>> sparse_views;
378
379 VAddr virtual_invalid_space{};
380
381 bool has_deleted_images = false;
382 u64 total_used_memory = 0;
383 u64 minimum_memory;
384 u64 expected_memory;
385 u64 critical_memory;
386
387 SlotVector<Image> slot_images;
388 SlotVector<ImageMapView> slot_map_views;
389 SlotVector<ImageView> slot_image_views;
390 SlotVector<ImageAlloc> slot_image_allocs;
391 SlotVector<Sampler> slot_samplers;
392 SlotVector<Framebuffer> slot_framebuffers;
393
394 // TODO: This data structure is not optimal and it should be reworked
395 std::vector<ImageId> uncommitted_downloads;
396 std::queue<std::vector<ImageId>> committed_downloads;
397
398 static constexpr size_t TICKS_TO_DESTROY = 6;
399 DelayedDestructionRing<Image, TICKS_TO_DESTROY> sentenced_images;
400 DelayedDestructionRing<ImageView, TICKS_TO_DESTROY> sentenced_image_view;
401 DelayedDestructionRing<Framebuffer, TICKS_TO_DESTROY> sentenced_framebuffers;
402
403 std::unordered_map<GPUVAddr, ImageAllocId> image_allocs_table;
404
405 u64 modification_tick = 0;
406 u64 frame_tick = 0;
407 typename SlotVector<Image>::Iterator deletion_iterator;
408};
409
410template <class P>
411TextureCache<P>::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface& rasterizer_, 28TextureCache<P>::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface& rasterizer_,
412 Tegra::Engines::Maxwell3D& maxwell3d_, 29 Tegra::Engines::Maxwell3D& maxwell3d_,
413 Tegra::Engines::KeplerCompute& kepler_compute_, 30 Tegra::Engines::KeplerCompute& kepler_compute_,
@@ -821,40 +438,6 @@ void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
821} 438}
822 439
823template <class P> 440template <class P>
824void TextureCache<P>::InvalidateColorBuffer(size_t index) {
825 ImageViewId& color_buffer_id = render_targets.color_buffer_ids[index];
826 color_buffer_id = FindColorBuffer(index, false);
827 if (!color_buffer_id) {
828 LOG_ERROR(HW_GPU, "Invalidating invalid color buffer in index={}", index);
829 return;
830 }
831 // When invalidating a color buffer, the old contents are no longer relevant
832 ImageView& color_buffer = slot_image_views[color_buffer_id];
833 Image& image = slot_images[color_buffer.image_id];
834 image.flags &= ~ImageFlagBits::CpuModified;
835 image.flags &= ~ImageFlagBits::GpuModified;
836
837 runtime.InvalidateColorBuffer(color_buffer, index);
838}
839
840template <class P>
841void TextureCache<P>::InvalidateDepthBuffer() {
842 ImageViewId& depth_buffer_id = render_targets.depth_buffer_id;
843 depth_buffer_id = FindDepthBuffer(false);
844 if (!depth_buffer_id) {
845 LOG_ERROR(HW_GPU, "Invalidating invalid depth buffer");
846 return;
847 }
848 // When invalidating the depth buffer, the old contents are no longer relevant
849 ImageBase& image = slot_images[slot_image_views[depth_buffer_id].image_id];
850 image.flags &= ~ImageFlagBits::CpuModified;
851 image.flags &= ~ImageFlagBits::GpuModified;
852
853 ImageView& depth_buffer = slot_image_views[depth_buffer_id];
854 runtime.InvalidateDepthBuffer(depth_buffer);
855}
856
857template <class P>
858typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView(VAddr cpu_addr) { 441typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView(VAddr cpu_addr) {
859 // TODO: Properly implement this 442 // TODO: Properly implement this
860 const auto it = page_table.find(cpu_addr >> PAGE_BITS); 443 const auto it = page_table.find(cpu_addr >> PAGE_BITS);
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
new file mode 100644
index 000000000..e4ae351cb
--- /dev/null
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -0,0 +1,385 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <mutex>
9#include <span>
10#include <type_traits>
11#include <unordered_map>
12#include <unordered_set>
13#include <vector>
14
15#include "common/common_types.h"
16#include "common/literals.h"
17#include "video_core/compatible_formats.h"
18#include "video_core/delayed_destruction_ring.h"
19#include "video_core/engines/fermi_2d.h"
20#include "video_core/engines/kepler_compute.h"
21#include "video_core/engines/maxwell_3d.h"
22#include "video_core/memory_manager.h"
23#include "video_core/rasterizer_interface.h"
24#include "video_core/surface.h"
25#include "video_core/texture_cache/descriptor_table.h"
26#include "video_core/texture_cache/image_base.h"
27#include "video_core/texture_cache/image_info.h"
28#include "video_core/texture_cache/image_view_info.h"
29#include "video_core/texture_cache/render_targets.h"
30#include "video_core/texture_cache/slot_vector.h"
31#include "video_core/texture_cache/types.h"
32#include "video_core/texture_cache/util.h"
33#include "video_core/textures/texture.h"
34
35namespace VideoCommon {
36
37using Tegra::Texture::SwizzleSource;
38using Tegra::Texture::TICEntry;
39using Tegra::Texture::TSCEntry;
40using VideoCore::Surface::GetFormatType;
41using VideoCore::Surface::IsCopyCompatible;
42using VideoCore::Surface::PixelFormat;
43using VideoCore::Surface::PixelFormatFromDepthFormat;
44using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
45using namespace Common::Literals;
46
47template <class P>
48class TextureCache {
49 /// Address shift for caching images into a hash table
50 static constexpr u64 PAGE_BITS = 20;
51
52 /// Enables debugging features to the texture cache
53 static constexpr bool ENABLE_VALIDATION = P::ENABLE_VALIDATION;
54 /// Implement blits as copies between framebuffers
55 static constexpr bool FRAMEBUFFER_BLITS = P::FRAMEBUFFER_BLITS;
56 /// True when some copies have to be emulated
57 static constexpr bool HAS_EMULATED_COPIES = P::HAS_EMULATED_COPIES;
58 /// True when the API can provide info about the memory of the device.
59 static constexpr bool HAS_DEVICE_MEMORY_INFO = P::HAS_DEVICE_MEMORY_INFO;
60
61 /// Image view ID for null descriptors
62 static constexpr ImageViewId NULL_IMAGE_VIEW_ID{0};
63 /// Sampler ID for bugged sampler ids
64 static constexpr SamplerId NULL_SAMPLER_ID{0};
65
66 static constexpr u64 DEFAULT_EXPECTED_MEMORY = 1_GiB;
67 static constexpr u64 DEFAULT_CRITICAL_MEMORY = 2_GiB;
68
69 using Runtime = typename P::Runtime;
70 using Image = typename P::Image;
71 using ImageAlloc = typename P::ImageAlloc;
72 using ImageView = typename P::ImageView;
73 using Sampler = typename P::Sampler;
74 using Framebuffer = typename P::Framebuffer;
75
76 struct BlitImages {
77 ImageId dst_id;
78 ImageId src_id;
79 PixelFormat dst_format;
80 PixelFormat src_format;
81 };
82
83 template <typename T>
84 struct IdentityHash {
85 [[nodiscard]] size_t operator()(T value) const noexcept {
86 return static_cast<size_t>(value);
87 }
88 };
89
90public:
91 explicit TextureCache(Runtime&, VideoCore::RasterizerInterface&, Tegra::Engines::Maxwell3D&,
92 Tegra::Engines::KeplerCompute&, Tegra::MemoryManager&);
93
94 /// Notify the cache that a new frame has been queued
95 void TickFrame();
96
97 /// Return a constant reference to the given image view id
98 [[nodiscard]] const ImageView& GetImageView(ImageViewId id) const noexcept;
99
100 /// Return a reference to the given image view id
101 [[nodiscard]] ImageView& GetImageView(ImageViewId id) noexcept;
102
103 /// Mark an image as modified from the GPU
104 void MarkModification(ImageId id) noexcept;
105
106 /// Fill image_view_ids with the graphics images in indices
107 void FillGraphicsImageViews(std::span<const u32> indices,
108 std::span<ImageViewId> image_view_ids);
109
110 /// Fill image_view_ids with the compute images in indices
111 void FillComputeImageViews(std::span<const u32> indices, std::span<ImageViewId> image_view_ids);
112
113 /// Get the sampler from the graphics descriptor table in the specified index
114 Sampler* GetGraphicsSampler(u32 index);
115
116 /// Get the sampler from the compute descriptor table in the specified index
117 Sampler* GetComputeSampler(u32 index);
118
119 /// Refresh the state for graphics image view and sampler descriptors
120 void SynchronizeGraphicsDescriptors();
121
122 /// Refresh the state for compute image view and sampler descriptors
123 void SynchronizeComputeDescriptors();
124
125 /// Update bound render targets and upload memory if necessary
126 /// @param is_clear True when the render targets are being used for clears
127 void UpdateRenderTargets(bool is_clear);
128
129 /// Find a framebuffer with the currently bound render targets
130 /// UpdateRenderTargets should be called before this
131 Framebuffer* GetFramebuffer();
132
133 /// Mark images in a range as modified from the CPU
134 void WriteMemory(VAddr cpu_addr, size_t size);
135
136 /// Download contents of host images to guest memory in a region
137 void DownloadMemory(VAddr cpu_addr, size_t size);
138
139 /// Remove images in a region
140 void UnmapMemory(VAddr cpu_addr, size_t size);
141
142 /// Remove images in a region
143 void UnmapGPUMemory(GPUVAddr gpu_addr, size_t size);
144
145 /// Blit an image with the given parameters
146 void BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
147 const Tegra::Engines::Fermi2D::Surface& src,
148 const Tegra::Engines::Fermi2D::Config& copy);
149
150 /// Try to find a cached image view in the given CPU address
151 [[nodiscard]] ImageView* TryFindFramebufferImageView(VAddr cpu_addr);
152
153 /// Return true when there are uncommitted images to be downloaded
154 [[nodiscard]] bool HasUncommittedFlushes() const noexcept;
155
156 /// Return true when the caller should wait for async downloads
157 [[nodiscard]] bool ShouldWaitAsyncFlushes() const noexcept;
158
159 /// Commit asynchronous downloads
160 void CommitAsyncFlushes();
161
162 /// Pop asynchronous downloads
163 void PopAsyncFlushes();
164
165 /// Return true when a CPU region is modified from the GPU
166 [[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size);
167
168 std::mutex mutex;
169
170private:
171 /// Iterate over all page indices in a range
172 template <typename Func>
173 static void ForEachCPUPage(VAddr addr, size_t size, Func&& func) {
174 static constexpr bool RETURNS_BOOL = std::is_same_v<std::invoke_result<Func, u64>, bool>;
175 const u64 page_end = (addr + size - 1) >> PAGE_BITS;
176 for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) {
177 if constexpr (RETURNS_BOOL) {
178 if (func(page)) {
179 break;
180 }
181 } else {
182 func(page);
183 }
184 }
185 }
186
187 template <typename Func>
188 static void ForEachGPUPage(GPUVAddr addr, size_t size, Func&& func) {
189 static constexpr bool RETURNS_BOOL = std::is_same_v<std::invoke_result<Func, u64>, bool>;
190 const u64 page_end = (addr + size - 1) >> PAGE_BITS;
191 for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) {
192 if constexpr (RETURNS_BOOL) {
193 if (func(page)) {
194 break;
195 }
196 } else {
197 func(page);
198 }
199 }
200 }
201
202 /// Runs the Garbage Collector.
203 void RunGarbageCollector();
204
205 /// Fills image_view_ids in the image views in indices
206 void FillImageViews(DescriptorTable<TICEntry>& table,
207 std::span<ImageViewId> cached_image_view_ids, std::span<const u32> indices,
208 std::span<ImageViewId> image_view_ids);
209
210 /// Find or create an image view in the guest descriptor table
211 ImageViewId VisitImageView(DescriptorTable<TICEntry>& table,
212 std::span<ImageViewId> cached_image_view_ids, u32 index);
213
214 /// Find or create a framebuffer with the given render target parameters
215 FramebufferId GetFramebufferId(const RenderTargets& key);
216
217 /// Refresh the contents (pixel data) of an image
218 void RefreshContents(Image& image, ImageId image_id);
219
220 /// Upload data from guest to an image
221 template <typename StagingBuffer>
222 void UploadImageContents(Image& image, StagingBuffer& staging_buffer);
223
224 /// Find or create an image view from a guest descriptor
225 [[nodiscard]] ImageViewId FindImageView(const TICEntry& config);
226
227 /// Create a new image view from a guest descriptor
228 [[nodiscard]] ImageViewId CreateImageView(const TICEntry& config);
229
230 /// Find or create an image from the given parameters
231 [[nodiscard]] ImageId FindOrInsertImage(const ImageInfo& info, GPUVAddr gpu_addr,
232 RelaxedOptions options = RelaxedOptions{});
233
234 /// Find an image from the given parameters
235 [[nodiscard]] ImageId FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
236 RelaxedOptions options);
237
238 /// Create an image from the given parameters
239 [[nodiscard]] ImageId InsertImage(const ImageInfo& info, GPUVAddr gpu_addr,
240 RelaxedOptions options);
241
242 /// Create a new image and join perfectly matching existing images
243 /// Remove joined images from the cache
244 [[nodiscard]] ImageId JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VAddr cpu_addr);
245
246 /// Return a blit image pair from the given guest blit parameters
247 [[nodiscard]] BlitImages GetBlitImages(const Tegra::Engines::Fermi2D::Surface& dst,
248 const Tegra::Engines::Fermi2D::Surface& src);
249
250 /// Find or create a sampler from a guest descriptor sampler
251 [[nodiscard]] SamplerId FindSampler(const TSCEntry& config);
252
253 /// Find or create an image view for the given color buffer index
254 [[nodiscard]] ImageViewId FindColorBuffer(size_t index, bool is_clear);
255
256 /// Find or create an image view for the depth buffer
257 [[nodiscard]] ImageViewId FindDepthBuffer(bool is_clear);
258
259 /// Find or create a view for a render target with the given image parameters
260 [[nodiscard]] ImageViewId FindRenderTargetView(const ImageInfo& info, GPUVAddr gpu_addr,
261 bool is_clear);
262
263 /// Iterates over all the images in a region calling func
264 template <typename Func>
265 void ForEachImageInRegion(VAddr cpu_addr, size_t size, Func&& func);
266
267 template <typename Func>
268 void ForEachImageInRegionGPU(GPUVAddr gpu_addr, size_t size, Func&& func);
269
270 template <typename Func>
271 void ForEachSparseImageInRegion(GPUVAddr gpu_addr, size_t size, Func&& func);
272
273 /// Iterates over all the images in a region calling func
274 template <typename Func>
275 void ForEachSparseSegment(ImageBase& image, Func&& func);
276
277 /// Find or create an image view in the given image with the passed parameters
278 [[nodiscard]] ImageViewId FindOrEmplaceImageView(ImageId image_id, const ImageViewInfo& info);
279
280 /// Register image in the page table
281 void RegisterImage(ImageId image);
282
283 /// Unregister image from the page table
284 void UnregisterImage(ImageId image);
285
286 /// Track CPU reads and writes for image
287 void TrackImage(ImageBase& image, ImageId image_id);
288
289 /// Stop tracking CPU reads and writes for image
290 void UntrackImage(ImageBase& image, ImageId image_id);
291
292 /// Delete image from the cache
293 void DeleteImage(ImageId image);
294
295 /// Remove image views references from the cache
296 void RemoveImageViewReferences(std::span<const ImageViewId> removed_views);
297
298 /// Remove framebuffers using the given image views from the cache
299 void RemoveFramebuffers(std::span<const ImageViewId> removed_views);
300
301 /// Mark an image as modified from the GPU
302 void MarkModification(ImageBase& image) noexcept;
303
304 /// Synchronize image aliases, copying data if needed
305 void SynchronizeAliases(ImageId image_id);
306
307 /// Prepare an image to be used
308 void PrepareImage(ImageId image_id, bool is_modification, bool invalidate);
309
310 /// Prepare an image view to be used
311 void PrepareImageView(ImageViewId image_view_id, bool is_modification, bool invalidate);
312
313 /// Execute copies from one image to the other, even if they are incompatible
314 void CopyImage(ImageId dst_id, ImageId src_id, std::span<const ImageCopy> copies);
315
316 /// Bind an image view as render target, downloading resources preemtively if needed
317 void BindRenderTarget(ImageViewId* old_id, ImageViewId new_id);
318
319 /// Create a render target from a given image and image view parameters
320 [[nodiscard]] std::pair<FramebufferId, ImageViewId> RenderTargetFromImage(
321 ImageId, const ImageViewInfo& view_info);
322
323 /// Returns true if the current clear parameters clear the whole image of a given image view
324 [[nodiscard]] bool IsFullClear(ImageViewId id);
325
326 Runtime& runtime;
327 VideoCore::RasterizerInterface& rasterizer;
328 Tegra::Engines::Maxwell3D& maxwell3d;
329 Tegra::Engines::KeplerCompute& kepler_compute;
330 Tegra::MemoryManager& gpu_memory;
331
332 DescriptorTable<TICEntry> graphics_image_table{gpu_memory};
333 DescriptorTable<TSCEntry> graphics_sampler_table{gpu_memory};
334 std::vector<SamplerId> graphics_sampler_ids;
335 std::vector<ImageViewId> graphics_image_view_ids;
336
337 DescriptorTable<TICEntry> compute_image_table{gpu_memory};
338 DescriptorTable<TSCEntry> compute_sampler_table{gpu_memory};
339 std::vector<SamplerId> compute_sampler_ids;
340 std::vector<ImageViewId> compute_image_view_ids;
341
342 RenderTargets render_targets;
343
344 std::unordered_map<TICEntry, ImageViewId> image_views;
345 std::unordered_map<TSCEntry, SamplerId> samplers;
346 std::unordered_map<RenderTargets, FramebufferId> framebuffers;
347
348 std::unordered_map<u64, std::vector<ImageMapId>, IdentityHash<u64>> page_table;
349 std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>> gpu_page_table;
350 std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>> sparse_page_table;
351
352 std::unordered_map<ImageId, std::vector<ImageViewId>> sparse_views;
353
354 VAddr virtual_invalid_space{};
355
356 bool has_deleted_images = false;
357 u64 total_used_memory = 0;
358 u64 minimum_memory;
359 u64 expected_memory;
360 u64 critical_memory;
361
362 SlotVector<Image> slot_images;
363 SlotVector<ImageMapView> slot_map_views;
364 SlotVector<ImageView> slot_image_views;
365 SlotVector<ImageAlloc> slot_image_allocs;
366 SlotVector<Sampler> slot_samplers;
367 SlotVector<Framebuffer> slot_framebuffers;
368
369 // TODO: This data structure is not optimal and it should be reworked
370 std::vector<ImageId> uncommitted_downloads;
371 std::queue<std::vector<ImageId>> committed_downloads;
372
373 static constexpr size_t TICKS_TO_DESTROY = 6;
374 DelayedDestructionRing<Image, TICKS_TO_DESTROY> sentenced_images;
375 DelayedDestructionRing<ImageView, TICKS_TO_DESTROY> sentenced_image_view;
376 DelayedDestructionRing<Framebuffer, TICKS_TO_DESTROY> sentenced_framebuffers;
377
378 std::unordered_map<GPUVAddr, ImageAllocId> image_allocs_table;
379
380 u64 modification_tick = 0;
381 u64 frame_tick = 0;
382 typename SlotVector<Image>::Iterator deletion_iterator;
383};
384
385} // namespace VideoCommon
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
index aa173d19e..300a61205 100644
--- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
+++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
@@ -228,7 +228,9 @@ void MemoryCommit::Release() {
228 228
229MemoryAllocator::MemoryAllocator(const Device& device_, bool export_allocations_) 229MemoryAllocator::MemoryAllocator(const Device& device_, bool export_allocations_)
230 : device{device_}, properties{device_.GetPhysical().GetMemoryProperties()}, 230 : device{device_}, properties{device_.GetPhysical().GetMemoryProperties()},
231 export_allocations{export_allocations_} {} 231 export_allocations{export_allocations_},
232 buffer_image_granularity{
233 device_.GetPhysical().GetProperties().limits.bufferImageGranularity} {}
232 234
233MemoryAllocator::~MemoryAllocator() = default; 235MemoryAllocator::~MemoryAllocator() = default;
234 236
@@ -258,7 +260,9 @@ MemoryCommit MemoryAllocator::Commit(const vk::Buffer& buffer, MemoryUsage usage
258} 260}
259 261
260MemoryCommit MemoryAllocator::Commit(const vk::Image& image, MemoryUsage usage) { 262MemoryCommit MemoryAllocator::Commit(const vk::Image& image, MemoryUsage usage) {
261 auto commit = Commit(device.GetLogical().GetImageMemoryRequirements(*image), usage); 263 VkMemoryRequirements requirements = device.GetLogical().GetImageMemoryRequirements(*image);
264 requirements.size = Common::AlignUp(requirements.size, buffer_image_granularity);
265 auto commit = Commit(requirements, usage);
262 image.BindMemory(commit.Memory(), commit.Offset()); 266 image.BindMemory(commit.Memory(), commit.Offset());
263 return commit; 267 return commit;
264} 268}
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.h b/src/video_core/vulkan_common/vulkan_memory_allocator.h
index b61e931e0..86e8ed119 100644
--- a/src/video_core/vulkan_common/vulkan_memory_allocator.h
+++ b/src/video_core/vulkan_common/vulkan_memory_allocator.h
@@ -123,6 +123,8 @@ private:
123 const VkPhysicalDeviceMemoryProperties properties; ///< Physical device properties. 123 const VkPhysicalDeviceMemoryProperties properties; ///< Physical device properties.
124 const bool export_allocations; ///< True when memory allocations have to be exported. 124 const bool export_allocations; ///< True when memory allocations have to be exported.
125 std::vector<std::unique_ptr<MemoryAllocation>> allocations; ///< Current allocations. 125 std::vector<std::unique_ptr<MemoryAllocation>> allocations; ///< Current allocations.
126 VkDeviceSize buffer_image_granularity; // The granularity for adjacent offsets between buffers
127 // and optimal images
126}; 128};
127 129
128/// Returns true when a memory usage is guaranteed to be host visible. 130/// Returns true when a memory usage is guaranteed to be host visible.
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui
index 8ce97edec..69b6c2d66 100644
--- a/src/yuzu/configuration/configure_general.ui
+++ b/src/yuzu/configuration/configure_general.ui
@@ -25,6 +25,36 @@
25 <item> 25 <item>
26 <layout class="QVBoxLayout" name="GeneralVerticalLayout"> 26 <layout class="QVBoxLayout" name="GeneralVerticalLayout">
27 <item> 27 <item>
28 <layout class="QHBoxLayout" name="horizontalLayout_2">
29 <item>
30 <widget class="QLabel" name="fps_cap_label">
31 <property name="text">
32 <string>Framerate Cap</string>
33 </property>
34 <property name="toolTip">
35 <string>Requires the use of the FPS Limiter Toggle hotkey to take effect.</string>
36 </property>
37 </widget>
38 </item>
39 <item>
40 <widget class="QSpinBox" name="fps_cap">
41 <property name="suffix">
42 <string>x</string>
43 </property>
44 <property name="minimum">
45 <number>1</number>
46 </property>
47 <property name="maximum">
48 <number>1000</number>
49 </property>
50 <property name="value">
51 <number>500</number>
52 </property>
53 </widget>
54 </item>
55 </layout>
56 </item>
57 <item>
28 <layout class="QHBoxLayout" name="horizontalLayout_2"> 58 <layout class="QHBoxLayout" name="horizontalLayout_2">
29 <item> 59 <item>
30 <widget class="QCheckBox" name="toggle_speed_limit"> 60 <widget class="QCheckBox" name="toggle_speed_limit">
@@ -52,36 +82,6 @@
52 </layout> 82 </layout>
53 </item> 83 </item>
54 <item> 84 <item>
55 <layout class="QHBoxLayout" name="horizontalLayout_2">
56 <item>
57 <widget class="QLabel" name="fps_cap_label">
58 <property name="text">
59 <string>Framerate Cap</string>
60 </property>
61 <property name="toolTip">
62 <string>Requires the use of the FPS Limiter Toggle hotkey to take effect.</string>
63 </property>
64 </widget>
65 </item>
66 <item>
67 <widget class="QSpinBox" name="fps_cap">
68 <property name="suffix">
69 <string>x</string>
70 </property>
71 <property name="minimum">
72 <number>1</number>
73 </property>
74 <property name="maximum">
75 <number>1000</number>
76 </property>
77 <property name="value">
78 <number>500</number>
79 </property>
80 </widget>
81 </item>
82 </layout>
83 </item>
84 <item>
85 <widget class="QCheckBox" name="use_multi_core"> 85 <widget class="QCheckBox" name="use_multi_core">
86 <property name="text"> 86 <property name="text">
87 <string>Multicore CPU Emulation</string> 87 <string>Multicore CPU Emulation</string>
diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui
index 379dc5d2e..4fe6b86ae 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.ui
+++ b/src/yuzu/configuration/configure_graphics_advanced.ui
@@ -82,14 +82,17 @@
82 <string>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</string> 82 <string>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</string>
83 </property> 83 </property>
84 <property name="text"> 84 <property name="text">
85 <string>Use asynchronous shader building</string> 85 <string>Use asynchronous shader building (hack)</string>
86 </property> 86 </property>
87 </widget> 87 </widget>
88 </item> 88 </item>
89 <item> 89 <item>
90 <widget class="QCheckBox" name="use_fast_gpu_time"> 90 <widget class="QCheckBox" name="use_fast_gpu_time">
91 <property name="toolTip">
92 <string>Enables Fast GPU Time. This option will force most games to run at their highest native resolution.</string>
93 </property>
91 <property name="text"> 94 <property name="text">
92 <string>Use Fast GPU Time</string> 95 <string>Use Fast GPU Time (hack)</string>
93 </property> 96 </property>
94 </widget> 97 </widget>
95 </item> 98 </item>
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 6b9bd05f1..7527c068b 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -309,11 +309,14 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
309 buttons_param[button_id].Clear(); 309 buttons_param[button_id].Clear();
310 button_map[button_id]->setText(tr("[not set]")); 310 button_map[button_id]->setText(tr("[not set]"));
311 }); 311 });
312 context_menu.addAction(tr("Toggle button"), [&] { 312 if (buttons_param[button_id].Has("toggle")) {
313 const bool toggle_value = !buttons_param[button_id].Get("toggle", false); 313 context_menu.addAction(tr("Toggle button"), [&] {
314 buttons_param[button_id].Set("toggle", toggle_value); 314 const bool toggle_value =
315 button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); 315 !buttons_param[button_id].Get("toggle", false);
316 }); 316 buttons_param[button_id].Set("toggle", toggle_value);
317 button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
318 });
319 }
317 if (buttons_param[button_id].Has("threshold")) { 320 if (buttons_param[button_id].Has("threshold")) {
318 context_menu.addAction(tr("Set threshold"), [&] { 321 context_menu.addAction(tr("Set threshold"), [&] {
319 const int button_threshold = static_cast<int>( 322 const int button_threshold = static_cast<int>(
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 9544f0fb0..5940e0cfd 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -2814,8 +2814,6 @@ void GMainWindow::OnToggleFilterBar() {
2814} 2814}
2815 2815
2816void GMainWindow::OnCaptureScreenshot() { 2816void GMainWindow::OnCaptureScreenshot() {
2817 OnPauseGame();
2818
2819 const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); 2817 const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
2820 const auto screenshot_path = 2818 const auto screenshot_path =
2821 QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::ScreenshotsDir)); 2819 QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::ScreenshotsDir));
@@ -2827,23 +2825,22 @@ void GMainWindow::OnCaptureScreenshot() {
2827 .arg(date); 2825 .arg(date);
2828 2826
2829 if (!Common::FS::CreateDir(screenshot_path.toStdString())) { 2827 if (!Common::FS::CreateDir(screenshot_path.toStdString())) {
2830 OnStartGame();
2831 return; 2828 return;
2832 } 2829 }
2833 2830
2834#ifdef _WIN32 2831#ifdef _WIN32
2835 if (UISettings::values.enable_screenshot_save_as) { 2832 if (UISettings::values.enable_screenshot_save_as) {
2833 OnPauseGame();
2836 filename = QFileDialog::getSaveFileName(this, tr("Capture Screenshot"), filename, 2834 filename = QFileDialog::getSaveFileName(this, tr("Capture Screenshot"), filename,
2837 tr("PNG Image (*.png)")); 2835 tr("PNG Image (*.png)"));
2836 OnStartGame();
2838 if (filename.isEmpty()) { 2837 if (filename.isEmpty()) {
2839 OnStartGame();
2840 return; 2838 return;
2841 } 2839 }
2842 } 2840 }
2843#endif 2841#endif
2844 render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor.GetValue(), 2842 render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor.GetValue(),
2845 filename); 2843 filename);
2846 OnStartGame();
2847} 2844}
2848 2845
2849// TODO: Written 2020-10-01: Remove per-game config migration code when it is irrelevant 2846// TODO: Written 2020-10-01: Remove per-game config migration code when it is irrelevant
diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt
index e55a19649..74fc24972 100644
--- a/src/yuzu_cmd/CMakeLists.txt
+++ b/src/yuzu_cmd/CMakeLists.txt
@@ -1,5 +1,6 @@
1set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules) 1set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules)
2 2
3# Credits to Samantas5855 and others for this function.
3function(create_resource file output filename) 4function(create_resource file output filename)
4 # Read hex data from file 5 # Read hex data from file
5 file(READ ${file} filedata HEX) 6 file(READ ${file} filedata HEX)
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index c80f7791c..87fce0c23 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -232,6 +232,7 @@ void EmuWindow_SDL2::WaitEvent() {
232 } 232 }
233} 233}
234 234
235// Credits to Samantas5855 and others for this function.
235void EmuWindow_SDL2::SetWindowIcon() { 236void EmuWindow_SDL2::SetWindowIcon() {
236 SDL_RWops* const yuzu_icon_stream = SDL_RWFromConstMem((void*)yuzu_icon, yuzu_icon_size); 237 SDL_RWops* const yuzu_icon_stream = SDL_RWFromConstMem((void*)yuzu_icon, yuzu_icon_size);
237 if (yuzu_icon_stream == nullptr) { 238 if (yuzu_icon_stream == nullptr) {