summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/CMakeLists.txt3
-rw-r--r--src/common/common_funcs.h13
-rw-r--r--src/common/error.cpp (renamed from src/common/misc.cpp)6
-rw-r--r--src/common/error.h21
-rw-r--r--src/common/logging/backend.cpp1
-rw-r--r--src/common/logging/filter.cpp1
-rw-r--r--src/common/logging/types.h1
-rw-r--r--src/common/lru_cache.h140
-rw-r--r--src/common/settings.cpp6
-rw-r--r--src/common/settings.h9
-rw-r--r--src/common/thread.cpp6
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/cpu_manager.cpp25
-rw-r--r--src/core/cpu_manager.h6
-rw-r--r--src/core/file_sys/kernel_executable.h1
-rw-r--r--src/core/hle/api_version.h17
-rw-r--r--src/core/hle/kernel/kernel.h1
-rw-r--r--src/core/hle/service/acc/acc.cpp66
-rw-r--r--src/core/hle/service/acc/async_context.cpp68
-rw-r--r--src/core/hle/service/acc/async_context.h37
-rw-r--r--src/core/hle/service/am/am.cpp16
-rw-r--r--src/core/hle/service/am/am.h2
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp26
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h14
-rw-r--r--src/core/hle/service/hid/hid.cpp14
-rw-r--r--src/core/hle/service/hid/hid.h1
-rw-r--r--src/core/hle/service/ngct/ngct.cpp46
-rw-r--r--src/core/hle/service/ngct/ngct.h20
-rw-r--r--src/core/hle/service/nifm/nifm.cpp113
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp9
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp25
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h15
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp17
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h3
-rw-r--r--src/core/hle/service/service.cpp2
-rw-r--r--src/core/hle/service/vi/display/vi_display.cpp17
-rw-r--r--src/core/hle/service/vi/display/vi_display.h13
-rw-r--r--src/core/network/network.cpp5
-rw-r--r--src/core/network/network_interface.cpp171
-rw-r--r--src/core/telemetry_session.cpp16
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_warp.cpp122
-rw-r--r--src/shader_recompiler/backend/spirv/emit_context.cpp56
-rw-r--r--src/shader_recompiler/backend/spirv/emit_context.h4
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp52
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp41
-rw-r--r--src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp127
-rw-r--r--src/shader_recompiler/frontend/maxwell/structured_control_flow.h9
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate_program.cpp2
-rw-r--r--src/shader_recompiler/host_translate_info.h5
-rw-r--r--src/video_core/CMakeLists.txt1
-rw-r--r--src/video_core/buffer_cache/buffer_base.h20
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h61
-rw-r--r--src/video_core/command_classes/codecs/codec.cpp224
-rw-r--r--src/video_core/command_classes/codecs/codec.h10
-rw-r--r--src/video_core/command_classes/codecs/h264.cpp3
-rw-r--r--src/video_core/command_classes/codecs/vp9.cpp1
-rw-r--r--src/video_core/command_classes/codecs/vp9_types.h85
-rw-r--r--src/video_core/engines/maxwell_3d.h8
-rw-r--r--src/video_core/memory_manager.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_device.h4
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp1
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp3
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_descriptor_pool.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp25
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp40
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.cpp30
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.h10
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp5
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h14
-rw-r--r--src/video_core/shader_environment.cpp1
-rw-r--r--src/video_core/texture_cache/image_base.h2
-rw-r--r--src/video_core/texture_cache/slot_vector.h1
-rw-r--r--src/video_core/texture_cache/texture_cache.h92
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h8
-rw-r--r--src/video_core/textures/decoders.cpp8
-rw-r--r--src/video_core/video_core.cpp3
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp10
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h6
-rw-r--r--src/yuzu/configuration/config.cpp9
-rw-r--r--src/yuzu/configuration/config.h1
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp42
-rw-r--r--src/yuzu/configuration/configure_graphics.h1
-rw-r--r--src/yuzu/configuration/configure_graphics.ui53
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.cpp6
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.h1
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.ui14
-rw-r--r--src/yuzu/game_list.cpp8
-rw-r--r--src/yuzu/main.cpp20
-rw-r--r--src/yuzu_cmd/config.cpp3
-rw-r--r--src/yuzu_cmd/default_ini.h6
94 files changed, 1557 insertions, 699 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 57922b51c..b18a2a2f5 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -53,6 +53,8 @@ add_library(common STATIC
53 div_ceil.h 53 div_ceil.h
54 dynamic_library.cpp 54 dynamic_library.cpp
55 dynamic_library.h 55 dynamic_library.h
56 error.cpp
57 error.h
56 fiber.cpp 58 fiber.cpp
57 fiber.h 59 fiber.h
58 fs/file.cpp 60 fs/file.cpp
@@ -88,7 +90,6 @@ add_library(common STATIC
88 microprofile.cpp 90 microprofile.cpp
89 microprofile.h 91 microprofile.h
90 microprofileui.h 92 microprofileui.h
91 misc.cpp
92 nvidia_flags.cpp 93 nvidia_flags.cpp
93 nvidia_flags.h 94 nvidia_flags.h
94 page_table.cpp 95 page_table.cpp
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index 53bd7da60..1e74d6930 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -4,9 +4,8 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <algorithm>
8#include <array> 7#include <array>
9#include <string> 8#include <iterator>
10 9
11#if !defined(ARCHITECTURE_x86_64) 10#if !defined(ARCHITECTURE_x86_64)
12#include <cstdlib> // for exit 11#include <cstdlib> // for exit
@@ -49,16 +48,6 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
49 48
50#endif // _MSC_VER ndef 49#endif // _MSC_VER ndef
51 50
52// Generic function to get last error message.
53// Call directly after the command or use the error num.
54// This function might change the error code.
55// Defined in misc.cpp.
56[[nodiscard]] std::string GetLastErrorMsg();
57
58// Like GetLastErrorMsg(), but passing an explicit error code.
59// Defined in misc.cpp.
60[[nodiscard]] std::string NativeErrorToString(int e);
61
62#define DECLARE_ENUM_FLAG_OPERATORS(type) \ 51#define DECLARE_ENUM_FLAG_OPERATORS(type) \
63 [[nodiscard]] constexpr type operator|(type a, type b) noexcept { \ 52 [[nodiscard]] constexpr type operator|(type a, type b) noexcept { \
64 using T = std::underlying_type_t<type>; \ 53 using T = std::underlying_type_t<type>; \
diff --git a/src/common/misc.cpp b/src/common/error.cpp
index 495385b9e..d4455e310 100644
--- a/src/common/misc.cpp
+++ b/src/common/error.cpp
@@ -10,7 +10,9 @@
10#include <cstring> 10#include <cstring>
11#endif 11#endif
12 12
13#include "common/common_funcs.h" 13#include "common/error.h"
14
15namespace Common {
14 16
15std::string NativeErrorToString(int e) { 17std::string NativeErrorToString(int e) {
16#ifdef _WIN32 18#ifdef _WIN32
@@ -50,3 +52,5 @@ std::string GetLastErrorMsg() {
50 return NativeErrorToString(errno); 52 return NativeErrorToString(errno);
51#endif 53#endif
52} 54}
55
56} // namespace Common
diff --git a/src/common/error.h b/src/common/error.h
new file mode 100644
index 000000000..e084d4b0f
--- /dev/null
+++ b/src/common/error.h
@@ -0,0 +1,21 @@
1// Copyright 2013 Dolphin Emulator Project / 2014 Citra 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 <string>
8
9namespace Common {
10
11// Generic function to get last error message.
12// Call directly after the command or use the error num.
13// This function might change the error code.
14// Defined in error.cpp.
15[[nodiscard]] std::string GetLastErrorMsg();
16
17// Like GetLastErrorMsg(), but passing an explicit error code.
18// Defined in error.cpp.
19[[nodiscard]] std::string NativeErrorToString(int e);
20
21} // namespace Common
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 949384fd3..e40d117d6 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -18,6 +18,7 @@
18#include "common/fs/fs_paths.h" 18#include "common/fs/fs_paths.h"
19#include "common/fs/path_util.h" 19#include "common/fs/path_util.h"
20#include "common/literals.h" 20#include "common/literals.h"
21#include "common/thread.h"
21 22
22#include "common/logging/backend.h" 23#include "common/logging/backend.h"
23#include "common/logging/log.h" 24#include "common/logging/log.h"
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp
index f055f0e11..42744c994 100644
--- a/src/common/logging/filter.cpp
+++ b/src/common/logging/filter.cpp
@@ -111,6 +111,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
111 SUB(Service, NCM) \ 111 SUB(Service, NCM) \
112 SUB(Service, NFC) \ 112 SUB(Service, NFC) \
113 SUB(Service, NFP) \ 113 SUB(Service, NFP) \
114 SUB(Service, NGCT) \
114 SUB(Service, NIFM) \ 115 SUB(Service, NIFM) \
115 SUB(Service, NIM) \ 116 SUB(Service, NIM) \
116 SUB(Service, NPNS) \ 117 SUB(Service, NPNS) \
diff --git a/src/common/logging/types.h b/src/common/logging/types.h
index 7ad0334fc..ddf9d27ca 100644
--- a/src/common/logging/types.h
+++ b/src/common/logging/types.h
@@ -81,6 +81,7 @@ enum class Class : u8 {
81 Service_NCM, ///< The NCM service 81 Service_NCM, ///< The NCM service
82 Service_NFC, ///< The NFC (Near-field communication) service 82 Service_NFC, ///< The NFC (Near-field communication) service
83 Service_NFP, ///< The NFP service 83 Service_NFP, ///< The NFP service
84 Service_NGCT, ///< The NGCT (No Good Content for Terra) service
84 Service_NIFM, ///< The NIFM (Network interface) service 85 Service_NIFM, ///< The NIFM (Network interface) service
85 Service_NIM, ///< The NIM service 86 Service_NIM, ///< The NIM service
86 Service_NPNS, ///< The NPNS service 87 Service_NPNS, ///< The NPNS service
diff --git a/src/common/lru_cache.h b/src/common/lru_cache.h
new file mode 100644
index 000000000..365488ba5
--- /dev/null
+++ b/src/common/lru_cache.h
@@ -0,0 +1,140 @@
1// Copyright 2021 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 <deque>
8#include <memory>
9#include <type_traits>
10
11#include "common/common_types.h"
12
13namespace Common {
14
15template <class Traits>
16class LeastRecentlyUsedCache {
17 using ObjectType = typename Traits::ObjectType;
18 using TickType = typename Traits::TickType;
19
20 struct Item {
21 ObjectType obj;
22 TickType tick;
23 Item* next{};
24 Item* prev{};
25 };
26
27public:
28 LeastRecentlyUsedCache() : first_item{}, last_item{} {}
29 ~LeastRecentlyUsedCache() = default;
30
31 size_t Insert(ObjectType obj, TickType tick) {
32 const auto new_id = Build();
33 auto& item = item_pool[new_id];
34 item.obj = obj;
35 item.tick = tick;
36 Attach(item);
37 return new_id;
38 }
39
40 void Touch(size_t id, TickType tick) {
41 auto& item = item_pool[id];
42 if (item.tick >= tick) {
43 return;
44 }
45 item.tick = tick;
46 if (&item == last_item) {
47 return;
48 }
49 Detach(item);
50 Attach(item);
51 }
52
53 void Free(size_t id) {
54 auto& item = item_pool[id];
55 Detach(item);
56 item.prev = nullptr;
57 item.next = nullptr;
58 free_items.push_back(id);
59 }
60
61 template <typename Func>
62 void ForEachItemBelow(TickType tick, Func&& func) {
63 static constexpr bool RETURNS_BOOL =
64 std::is_same_v<std::invoke_result<Func, ObjectType>, bool>;
65 Item* iterator = first_item;
66 while (iterator) {
67 if (static_cast<s64>(tick) - static_cast<s64>(iterator->tick) < 0) {
68 return;
69 }
70 Item* next = iterator->next;
71 if constexpr (RETURNS_BOOL) {
72 if (func(iterator->obj)) {
73 return;
74 }
75 } else {
76 func(iterator->obj);
77 }
78 iterator = next;
79 }
80 }
81
82private:
83 size_t Build() {
84 if (free_items.empty()) {
85 const size_t item_id = item_pool.size();
86 auto& item = item_pool.emplace_back();
87 item.next = nullptr;
88 item.prev = nullptr;
89 return item_id;
90 }
91 const size_t item_id = free_items.front();
92 free_items.pop_front();
93 auto& item = item_pool[item_id];
94 item.next = nullptr;
95 item.prev = nullptr;
96 return item_id;
97 }
98
99 void Attach(Item& item) {
100 if (!first_item) {
101 first_item = &item;
102 }
103 if (!last_item) {
104 last_item = &item;
105 } else {
106 item.prev = last_item;
107 last_item->next = &item;
108 item.next = nullptr;
109 last_item = &item;
110 }
111 }
112
113 void Detach(Item& item) {
114 if (item.prev) {
115 item.prev->next = item.next;
116 }
117 if (item.next) {
118 item.next->prev = item.prev;
119 }
120 if (&item == first_item) {
121 first_item = item.next;
122 if (first_item) {
123 first_item->prev = nullptr;
124 }
125 }
126 if (&item == last_item) {
127 last_item = item.prev;
128 if (last_item) {
129 last_item->next = nullptr;
130 }
131 }
132 }
133
134 std::deque<Item> item_pool;
135 std::deque<size_t> free_items;
136 Item* first_item{};
137 Item* last_item{};
138};
139
140} // namespace Common
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 996315999..0d2df80a8 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -54,12 +54,11 @@ void LogSettings() {
54 log_setting("Renderer_GPUAccuracyLevel", values.gpu_accuracy.GetValue()); 54 log_setting("Renderer_GPUAccuracyLevel", values.gpu_accuracy.GetValue());
55 log_setting("Renderer_UseAsynchronousGpuEmulation", 55 log_setting("Renderer_UseAsynchronousGpuEmulation",
56 values.use_asynchronous_gpu_emulation.GetValue()); 56 values.use_asynchronous_gpu_emulation.GetValue());
57 log_setting("Renderer_UseNvdecEmulation", values.use_nvdec_emulation.GetValue()); 57 log_setting("Renderer_NvdecEmulation", values.nvdec_emulation.GetValue());
58 log_setting("Renderer_AccelerateASTC", values.accelerate_astc.GetValue()); 58 log_setting("Renderer_AccelerateASTC", values.accelerate_astc.GetValue());
59 log_setting("Renderer_UseVsync", values.use_vsync.GetValue()); 59 log_setting("Renderer_UseVsync", values.use_vsync.GetValue());
60 log_setting("Renderer_ShaderBackend", values.shader_backend.GetValue()); 60 log_setting("Renderer_ShaderBackend", values.shader_backend.GetValue());
61 log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue()); 61 log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
62 log_setting("Renderer_UseGarbageCollection", values.use_caches_gc.GetValue());
63 log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue()); 62 log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue());
64 log_setting("Audio_OutputEngine", values.sink_id.GetValue()); 63 log_setting("Audio_OutputEngine", values.sink_id.GetValue());
65 log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue()); 64 log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue());
@@ -137,13 +136,12 @@ void RestoreGlobalState(bool is_powered_on) {
137 values.use_disk_shader_cache.SetGlobal(true); 136 values.use_disk_shader_cache.SetGlobal(true);
138 values.gpu_accuracy.SetGlobal(true); 137 values.gpu_accuracy.SetGlobal(true);
139 values.use_asynchronous_gpu_emulation.SetGlobal(true); 138 values.use_asynchronous_gpu_emulation.SetGlobal(true);
140 values.use_nvdec_emulation.SetGlobal(true); 139 values.nvdec_emulation.SetGlobal(true);
141 values.accelerate_astc.SetGlobal(true); 140 values.accelerate_astc.SetGlobal(true);
142 values.use_vsync.SetGlobal(true); 141 values.use_vsync.SetGlobal(true);
143 values.shader_backend.SetGlobal(true); 142 values.shader_backend.SetGlobal(true);
144 values.use_asynchronous_shaders.SetGlobal(true); 143 values.use_asynchronous_shaders.SetGlobal(true);
145 values.use_fast_gpu_time.SetGlobal(true); 144 values.use_fast_gpu_time.SetGlobal(true);
146 values.use_caches_gc.SetGlobal(true);
147 values.bg_red.SetGlobal(true); 145 values.bg_red.SetGlobal(true);
148 values.bg_green.SetGlobal(true); 146 values.bg_green.SetGlobal(true);
149 values.bg_blue.SetGlobal(true); 147 values.bg_blue.SetGlobal(true);
diff --git a/src/common/settings.h b/src/common/settings.h
index 20769d310..b7195670b 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -48,6 +48,12 @@ enum class FullscreenMode : u32 {
48 Exclusive = 1, 48 Exclusive = 1,
49}; 49};
50 50
51enum class NvdecEmulation : u32 {
52 Off = 0,
53 CPU = 1,
54 GPU = 2,
55};
56
51/** The BasicSetting class is a simple resource manager. It defines a label and default value 57/** The BasicSetting class is a simple resource manager. It defines a label and default value
52 * alongside the actual value of the setting for simpler and less-error prone use with frontend 58 * alongside the actual value of the setting for simpler and less-error prone use with frontend
53 * configurations. Setting a default value and label is required, though subclasses may deviate from 59 * configurations. Setting a default value and label is required, though subclasses may deviate from
@@ -466,7 +472,7 @@ struct Values {
466 RangedSetting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, GPUAccuracy::Normal, 472 RangedSetting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, GPUAccuracy::Normal,
467 GPUAccuracy::Extreme, "gpu_accuracy"}; 473 GPUAccuracy::Extreme, "gpu_accuracy"};
468 Setting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"}; 474 Setting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"};
469 Setting<bool> use_nvdec_emulation{true, "use_nvdec_emulation"}; 475 Setting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"};
470 Setting<bool> accelerate_astc{true, "accelerate_astc"}; 476 Setting<bool> accelerate_astc{true, "accelerate_astc"};
471 Setting<bool> use_vsync{true, "use_vsync"}; 477 Setting<bool> use_vsync{true, "use_vsync"};
472 BasicRangedSetting<u16> fps_cap{1000, 1, 1000, "fps_cap"}; 478 BasicRangedSetting<u16> fps_cap{1000, 1, 1000, "fps_cap"};
@@ -475,7 +481,6 @@ struct Values {
475 ShaderBackend::SPIRV, "shader_backend"}; 481 ShaderBackend::SPIRV, "shader_backend"};
476 Setting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; 482 Setting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
477 Setting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; 483 Setting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
478 Setting<bool> use_caches_gc{false, "use_caches_gc"};
479 484
480 Setting<u8> bg_red{0, "bg_red"}; 485 Setting<u8> bg_red{0, "bg_red"};
481 Setting<u8> bg_green{0, "bg_green"}; 486 Setting<u8> bg_green{0, "bg_green"};
diff --git a/src/common/thread.cpp b/src/common/thread.cpp
index d2c1ac60d..946a1114d 100644
--- a/src/common/thread.cpp
+++ b/src/common/thread.cpp
@@ -2,7 +2,9 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/common_funcs.h" 5#include <string>
6
7#include "common/error.h"
6#include "common/logging/log.h" 8#include "common/logging/log.h"
7#include "common/thread.h" 9#include "common/thread.h"
8#ifdef __APPLE__ 10#ifdef __APPLE__
@@ -21,8 +23,6 @@
21#include <unistd.h> 23#include <unistd.h>
22#endif 24#endif
23 25
24#include <string>
25
26#ifdef __FreeBSD__ 26#ifdef __FreeBSD__
27#define cpu_set_t cpuset_t 27#define cpu_set_t cpuset_t
28#endif 28#endif
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index f5cf5c16a..7140d0db8 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -263,6 +263,8 @@ add_library(core STATIC
263 hle/service/acc/acc_u0.h 263 hle/service/acc/acc_u0.h
264 hle/service/acc/acc_u1.cpp 264 hle/service/acc/acc_u1.cpp
265 hle/service/acc/acc_u1.h 265 hle/service/acc/acc_u1.h
266 hle/service/acc/async_context.cpp
267 hle/service/acc/async_context.h
266 hle/service/acc/errors.h 268 hle/service/acc/errors.h
267 hle/service/acc/profile_manager.cpp 269 hle/service/acc/profile_manager.cpp
268 hle/service/acc/profile_manager.h 270 hle/service/acc/profile_manager.h
@@ -452,6 +454,8 @@ add_library(core STATIC
452 hle/service/nfp/nfp.h 454 hle/service/nfp/nfp.h
453 hle/service/nfp/nfp_user.cpp 455 hle/service/nfp/nfp_user.cpp
454 hle/service/nfp/nfp_user.h 456 hle/service/nfp/nfp_user.h
457 hle/service/ngct/ngct.cpp
458 hle/service/ngct/ngct.h
455 hle/service/nifm/nifm.cpp 459 hle/service/nifm/nifm.cpp
456 hle/service/nifm/nifm.h 460 hle/service/nifm/nifm.h
457 hle/service/nim/nim.cpp 461 hle/service/nim/nim.cpp
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index 7e195346b..77efcabf0 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -21,34 +21,25 @@ namespace Core {
21CpuManager::CpuManager(System& system_) : system{system_} {} 21CpuManager::CpuManager(System& system_) : system{system_} {}
22CpuManager::~CpuManager() = default; 22CpuManager::~CpuManager() = default;
23 23
24void CpuManager::ThreadStart(CpuManager& cpu_manager, std::size_t core) { 24void CpuManager::ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager,
25 cpu_manager.RunThread(core); 25 std::size_t core) {
26 cpu_manager.RunThread(stop_token, core);
26} 27}
27 28
28void CpuManager::Initialize() { 29void CpuManager::Initialize() {
29 running_mode = true; 30 running_mode = true;
30 if (is_multicore) { 31 if (is_multicore) {
31 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 32 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
32 core_data[core].host_thread = 33 core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core);
33 std::make_unique<std::thread>(ThreadStart, std::ref(*this), core);
34 } 34 }
35 } else { 35 } else {
36 core_data[0].host_thread = std::make_unique<std::thread>(ThreadStart, std::ref(*this), 0); 36 core_data[0].host_thread = std::jthread(ThreadStart, std::ref(*this), 0);
37 } 37 }
38} 38}
39 39
40void CpuManager::Shutdown() { 40void CpuManager::Shutdown() {
41 running_mode = false; 41 running_mode = false;
42 Pause(false); 42 Pause(false);
43 if (is_multicore) {
44 for (auto& data : core_data) {
45 data.host_thread->join();
46 data.host_thread.reset();
47 }
48 } else {
49 core_data[0].host_thread->join();
50 core_data[0].host_thread.reset();
51 }
52} 43}
53 44
54std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() { 45std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() {
@@ -317,7 +308,7 @@ void CpuManager::Pause(bool paused) {
317 } 308 }
318} 309}
319 310
320void CpuManager::RunThread(std::size_t core) { 311void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
321 /// Initialization 312 /// Initialization
322 system.RegisterCoreThread(core); 313 system.RegisterCoreThread(core);
323 std::string name; 314 std::string name;
@@ -361,6 +352,10 @@ void CpuManager::RunThread(std::size_t core) {
361 return; 352 return;
362 } 353 }
363 354
355 if (stop_token.stop_requested()) {
356 break;
357 }
358
364 auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread(); 359 auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
365 data.is_running = true; 360 data.is_running = true;
366 Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext()); 361 Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext());
diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h
index 140263b09..9d92d4af0 100644
--- a/src/core/cpu_manager.h
+++ b/src/core/cpu_manager.h
@@ -78,9 +78,9 @@ private:
78 void SingleCoreRunSuspendThread(); 78 void SingleCoreRunSuspendThread();
79 void SingleCorePause(bool paused); 79 void SingleCorePause(bool paused);
80 80
81 static void ThreadStart(CpuManager& cpu_manager, std::size_t core); 81 static void ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, std::size_t core);
82 82
83 void RunThread(std::size_t core); 83 void RunThread(std::stop_token stop_token, std::size_t core);
84 84
85 struct CoreData { 85 struct CoreData {
86 std::shared_ptr<Common::Fiber> host_context; 86 std::shared_ptr<Common::Fiber> host_context;
@@ -89,7 +89,7 @@ private:
89 std::atomic<bool> is_running; 89 std::atomic<bool> is_running;
90 std::atomic<bool> is_paused; 90 std::atomic<bool> is_paused;
91 std::atomic<bool> initialized; 91 std::atomic<bool> initialized;
92 std::unique_ptr<std::thread> host_thread; 92 std::jthread host_thread;
93 }; 93 };
94 94
95 std::atomic<bool> running_mode{}; 95 std::atomic<bool> running_mode{};
diff --git a/src/core/file_sys/kernel_executable.h b/src/core/file_sys/kernel_executable.h
index 044c554d3..79ca82f8b 100644
--- a/src/core/file_sys/kernel_executable.h
+++ b/src/core/file_sys/kernel_executable.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <string>
8#include <vector> 9#include <vector>
9 10
10#include "common/common_funcs.h" 11#include "common/common_funcs.h"
diff --git a/src/core/hle/api_version.h b/src/core/hle/api_version.h
index 43d5670a9..626e30753 100644
--- a/src/core/hle/api_version.h
+++ b/src/core/hle/api_version.h
@@ -28,13 +28,20 @@ constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 12.1.0-1.0";
28 28
29// Atmosphere version constants. 29// Atmosphere version constants.
30 30
31constexpr u8 ATMOSPHERE_RELEASE_VERSION_MAJOR = 0; 31constexpr u8 ATMOSPHERE_RELEASE_VERSION_MAJOR = 1;
32constexpr u8 ATMOSPHERE_RELEASE_VERSION_MINOR = 19; 32constexpr u8 ATMOSPHERE_RELEASE_VERSION_MINOR = 0;
33constexpr u8 ATMOSPHERE_RELEASE_VERSION_MICRO = 5; 33constexpr u8 ATMOSPHERE_RELEASE_VERSION_MICRO = 0;
34
35constexpr u32 AtmosphereTargetFirmwareWithRevision(u8 major, u8 minor, u8 micro, u8 rev) {
36 return u32{major} << 24 | u32{minor} << 16 | u32{micro} << 8 | u32{rev};
37}
38
39constexpr u32 AtmosphereTargetFirmware(u8 major, u8 minor, u8 micro) {
40 return AtmosphereTargetFirmwareWithRevision(major, minor, micro, 0);
41}
34 42
35constexpr u32 GetTargetFirmware() { 43constexpr u32 GetTargetFirmware() {
36 return u32{HOS_VERSION_MAJOR} << 24 | u32{HOS_VERSION_MINOR} << 16 | 44 return AtmosphereTargetFirmware(HOS_VERSION_MAJOR, HOS_VERSION_MINOR, HOS_VERSION_MICRO);
37 u32{HOS_VERSION_MICRO} << 8 | 0U;
38} 45}
39 46
40} // namespace HLE::ApiVersion 47} // namespace HLE::ApiVersion
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 3a6db0b1c..901d43da9 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <functional>
8#include <memory> 9#include <memory>
9#include <string> 10#include <string>
10#include <unordered_map> 11#include <unordered_map>
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 882fc1492..6d9ec0a8a 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -23,6 +23,7 @@
23#include "core/hle/service/acc/acc_su.h" 23#include "core/hle/service/acc/acc_su.h"
24#include "core/hle/service/acc/acc_u0.h" 24#include "core/hle/service/acc/acc_u0.h"
25#include "core/hle/service/acc/acc_u1.h" 25#include "core/hle/service/acc/acc_u1.h"
26#include "core/hle/service/acc/async_context.h"
26#include "core/hle/service/acc/errors.h" 27#include "core/hle/service/acc/errors.h"
27#include "core/hle/service/acc/profile_manager.h" 28#include "core/hle/service/acc/profile_manager.h"
28#include "core/hle/service/glue/arp.h" 29#include "core/hle/service/glue/arp.h"
@@ -454,22 +455,6 @@ public:
454 : IProfileCommon{system_, "IProfileEditor", true, user_id_, profile_manager_} {} 455 : IProfileCommon{system_, "IProfileEditor", true, user_id_, profile_manager_} {}
455}; 456};
456 457
457class IAsyncContext final : public ServiceFramework<IAsyncContext> {
458public:
459 explicit IAsyncContext(Core::System& system_) : ServiceFramework{system_, "IAsyncContext"} {
460 // clang-format off
461 static const FunctionInfo functions[] = {
462 {0, nullptr, "GetSystemEvent"},
463 {1, nullptr, "Cancel"},
464 {2, nullptr, "HasDone"},
465 {3, nullptr, "GetResult"},
466 };
467 // clang-format on
468
469 RegisterHandlers(functions);
470 }
471};
472
473class ISessionObject final : public ServiceFramework<ISessionObject> { 458class ISessionObject final : public ServiceFramework<ISessionObject> {
474public: 459public:
475 explicit ISessionObject(Core::System& system_, Common::UUID) 460 explicit ISessionObject(Core::System& system_, Common::UUID)
@@ -504,16 +489,44 @@ public:
504 } 489 }
505}; 490};
506 491
492class EnsureTokenIdCacheAsyncInterface final : public IAsyncContext {
493public:
494 explicit EnsureTokenIdCacheAsyncInterface(Core::System& system_) : IAsyncContext{system_} {
495 MarkComplete();
496 }
497 ~EnsureTokenIdCacheAsyncInterface() = default;
498
499 void LoadIdTokenCache(Kernel::HLERequestContext& ctx) {
500 LOG_WARNING(Service_ACC, "(STUBBED) called");
501
502 IPC::ResponseBuilder rb{ctx, 2};
503 rb.Push(ResultSuccess);
504 }
505
506protected:
507 bool IsComplete() const override {
508 return true;
509 }
510
511 void Cancel() override {}
512
513 ResultCode GetResult() const override {
514 return ResultSuccess;
515 }
516};
517
507class IManagerForApplication final : public ServiceFramework<IManagerForApplication> { 518class IManagerForApplication final : public ServiceFramework<IManagerForApplication> {
508public: 519public:
509 explicit IManagerForApplication(Core::System& system_, Common::UUID user_id_) 520 explicit IManagerForApplication(Core::System& system_, Common::UUID user_id_)
510 : ServiceFramework{system_, "IManagerForApplication"}, user_id{user_id_} { 521 : ServiceFramework{system_, "IManagerForApplication"},
522 ensure_token_id{std::make_shared<EnsureTokenIdCacheAsyncInterface>(system)},
523 user_id{user_id_} {
511 // clang-format off 524 // clang-format off
512 static const FunctionInfo functions[] = { 525 static const FunctionInfo functions[] = {
513 {0, &IManagerForApplication::CheckAvailability, "CheckAvailability"}, 526 {0, &IManagerForApplication::CheckAvailability, "CheckAvailability"},
514 {1, &IManagerForApplication::GetAccountId, "GetAccountId"}, 527 {1, &IManagerForApplication::GetAccountId, "GetAccountId"},
515 {2, nullptr, "EnsureIdTokenCacheAsync"}, 528 {2, &IManagerForApplication::EnsureIdTokenCacheAsync, "EnsureIdTokenCacheAsync"},
516 {3, nullptr, "LoadIdTokenCache"}, 529 {3, &IManagerForApplication::LoadIdTokenCache, "LoadIdTokenCache"},
517 {130, &IManagerForApplication::GetNintendoAccountUserResourceCacheForApplication, "GetNintendoAccountUserResourceCacheForApplication"}, 530 {130, &IManagerForApplication::GetNintendoAccountUserResourceCacheForApplication, "GetNintendoAccountUserResourceCacheForApplication"},
518 {150, nullptr, "CreateAuthorizationRequest"}, 531 {150, nullptr, "CreateAuthorizationRequest"},
519 {160, &IManagerForApplication::StoreOpenContext, "StoreOpenContext"}, 532 {160, &IManagerForApplication::StoreOpenContext, "StoreOpenContext"},
@@ -540,6 +553,20 @@ private:
540 rb.PushRaw<u64>(user_id.GetNintendoID()); 553 rb.PushRaw<u64>(user_id.GetNintendoID());
541 } 554 }
542 555
556 void EnsureIdTokenCacheAsync(Kernel::HLERequestContext& ctx) {
557 LOG_WARNING(Service_ACC, "(STUBBED) called");
558
559 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
560 rb.Push(ResultSuccess);
561 rb.PushIpcInterface(ensure_token_id);
562 }
563
564 void LoadIdTokenCache(Kernel::HLERequestContext& ctx) {
565 LOG_WARNING(Service_ACC, "(STUBBED) called");
566
567 ensure_token_id->LoadIdTokenCache(ctx);
568 }
569
543 void GetNintendoAccountUserResourceCacheForApplication(Kernel::HLERequestContext& ctx) { 570 void GetNintendoAccountUserResourceCacheForApplication(Kernel::HLERequestContext& ctx) {
544 LOG_WARNING(Service_ACC, "(STUBBED) called"); 571 LOG_WARNING(Service_ACC, "(STUBBED) called");
545 572
@@ -562,6 +589,7 @@ private:
562 rb.Push(ResultSuccess); 589 rb.Push(ResultSuccess);
563 } 590 }
564 591
592 std::shared_ptr<EnsureTokenIdCacheAsyncInterface> ensure_token_id{};
565 Common::UUID user_id{Common::INVALID_UUID}; 593 Common::UUID user_id{Common::INVALID_UUID};
566}; 594};
567 595
diff --git a/src/core/hle/service/acc/async_context.cpp b/src/core/hle/service/acc/async_context.cpp
new file mode 100644
index 000000000..459323132
--- /dev/null
+++ b/src/core/hle/service/acc/async_context.cpp
@@ -0,0 +1,68 @@
1// Copyright 2021 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/core.h"
6#include "core/hle/ipc_helpers.h"
7#include "core/hle/service/acc/async_context.h"
8
9namespace Service::Account {
10IAsyncContext::IAsyncContext(Core::System& system_)
11 : ServiceFramework{system_, "IAsyncContext"}, compeletion_event{system_.Kernel()} {
12
13 Kernel::KAutoObject::Create(std::addressof(compeletion_event));
14 compeletion_event.Initialize("IAsyncContext:CompletionEvent");
15
16 // clang-format off
17 static const FunctionInfo functions[] = {
18 {0, &IAsyncContext::GetSystemEvent, "GetSystemEvent"},
19 {1, &IAsyncContext::Cancel, "Cancel"},
20 {2, &IAsyncContext::HasDone, "HasDone"},
21 {3, &IAsyncContext::GetResult, "GetResult"},
22 };
23 // clang-format on
24
25 RegisterHandlers(functions);
26}
27
28void IAsyncContext::GetSystemEvent(Kernel::HLERequestContext& ctx) {
29 LOG_DEBUG(Service_ACC, "called");
30
31 IPC::ResponseBuilder rb{ctx, 2, 1};
32 rb.Push(ResultSuccess);
33 rb.PushCopyObjects(compeletion_event.GetReadableEvent());
34}
35
36void IAsyncContext::Cancel(Kernel::HLERequestContext& ctx) {
37 LOG_DEBUG(Service_ACC, "called");
38
39 Cancel();
40 MarkComplete();
41
42 IPC::ResponseBuilder rb{ctx, 2};
43 rb.Push(ResultSuccess);
44}
45
46void IAsyncContext::HasDone(Kernel::HLERequestContext& ctx) {
47 LOG_DEBUG(Service_ACC, "called");
48
49 is_complete.store(IsComplete());
50
51 IPC::ResponseBuilder rb{ctx, 3};
52 rb.Push(ResultSuccess);
53 rb.Push(is_complete.load());
54}
55
56void IAsyncContext::GetResult(Kernel::HLERequestContext& ctx) {
57 LOG_DEBUG(Service_ACC, "called");
58
59 IPC::ResponseBuilder rb{ctx, 3};
60 rb.Push(GetResult());
61}
62
63void IAsyncContext::MarkComplete() {
64 is_complete.store(true);
65 compeletion_event.GetWritableEvent().Signal();
66}
67
68} // namespace Service::Account
diff --git a/src/core/hle/service/acc/async_context.h b/src/core/hle/service/acc/async_context.h
new file mode 100644
index 000000000..c694b4946
--- /dev/null
+++ b/src/core/hle/service/acc/async_context.h
@@ -0,0 +1,37 @@
1// Copyright 2021 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <atomic>
8#include "core/hle/kernel/k_event.h"
9#include "core/hle/service/service.h"
10
11namespace Core {
12class System;
13}
14
15namespace Service::Account {
16
17class IAsyncContext : public ServiceFramework<IAsyncContext> {
18public:
19 explicit IAsyncContext(Core::System& system_);
20
21 void GetSystemEvent(Kernel::HLERequestContext& ctx);
22 void Cancel(Kernel::HLERequestContext& ctx);
23 void HasDone(Kernel::HLERequestContext& ctx);
24 void GetResult(Kernel::HLERequestContext& ctx);
25
26protected:
27 virtual bool IsComplete() const = 0;
28 virtual void Cancel() = 0;
29 virtual ResultCode GetResult() const = 0;
30
31 void MarkComplete();
32
33 std::atomic<bool> is_complete{false};
34 Kernel::KEvent compeletion_event;
35};
36
37} // namespace Service::Account
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index a538f82e3..c3ac73131 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -1270,7 +1270,8 @@ void ILibraryAppletCreator::CreateHandleStorage(Kernel::HLERequestContext& ctx)
1270IApplicationFunctions::IApplicationFunctions(Core::System& system_) 1270IApplicationFunctions::IApplicationFunctions(Core::System& system_)
1271 : ServiceFramework{system_, "IApplicationFunctions"}, gpu_error_detected_event{system.Kernel()}, 1271 : ServiceFramework{system_, "IApplicationFunctions"}, gpu_error_detected_event{system.Kernel()},
1272 friend_invitation_storage_channel_event{system.Kernel()}, 1272 friend_invitation_storage_channel_event{system.Kernel()},
1273 health_warning_disappeared_system_event{system.Kernel()} { 1273 notification_storage_channel_event{system.Kernel()}, health_warning_disappeared_system_event{
1274 system.Kernel()} {
1274 // clang-format off 1275 // clang-format off
1275 static const FunctionInfo functions[] = { 1276 static const FunctionInfo functions[] = {
1276 {1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"}, 1277 {1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"},
@@ -1322,7 +1323,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
1322 {131, nullptr, "SetDelayTimeToAbortOnGpuError"}, 1323 {131, nullptr, "SetDelayTimeToAbortOnGpuError"},
1323 {140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"}, 1324 {140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"},
1324 {141, &IApplicationFunctions::TryPopFromFriendInvitationStorageChannel, "TryPopFromFriendInvitationStorageChannel"}, 1325 {141, &IApplicationFunctions::TryPopFromFriendInvitationStorageChannel, "TryPopFromFriendInvitationStorageChannel"},
1325 {150, nullptr, "GetNotificationStorageChannelEvent"}, 1326 {150, &IApplicationFunctions::GetNotificationStorageChannelEvent, "GetNotificationStorageChannelEvent"},
1326 {151, nullptr, "TryPopFromNotificationStorageChannel"}, 1327 {151, nullptr, "TryPopFromNotificationStorageChannel"},
1327 {160, &IApplicationFunctions::GetHealthWarningDisappearedSystemEvent, "GetHealthWarningDisappearedSystemEvent"}, 1328 {160, &IApplicationFunctions::GetHealthWarningDisappearedSystemEvent, "GetHealthWarningDisappearedSystemEvent"},
1328 {170, nullptr, "SetHdcpAuthenticationActivated"}, 1329 {170, nullptr, "SetHdcpAuthenticationActivated"},
@@ -1340,11 +1341,14 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
1340 1341
1341 Kernel::KAutoObject::Create(std::addressof(gpu_error_detected_event)); 1342 Kernel::KAutoObject::Create(std::addressof(gpu_error_detected_event));
1342 Kernel::KAutoObject::Create(std::addressof(friend_invitation_storage_channel_event)); 1343 Kernel::KAutoObject::Create(std::addressof(friend_invitation_storage_channel_event));
1344 Kernel::KAutoObject::Create(std::addressof(notification_storage_channel_event));
1343 Kernel::KAutoObject::Create(std::addressof(health_warning_disappeared_system_event)); 1345 Kernel::KAutoObject::Create(std::addressof(health_warning_disappeared_system_event));
1344 1346
1345 gpu_error_detected_event.Initialize("IApplicationFunctions:GpuErrorDetectedSystemEvent"); 1347 gpu_error_detected_event.Initialize("IApplicationFunctions:GpuErrorDetectedSystemEvent");
1346 friend_invitation_storage_channel_event.Initialize( 1348 friend_invitation_storage_channel_event.Initialize(
1347 "IApplicationFunctions:FriendInvitationStorageChannelEvent"); 1349 "IApplicationFunctions:FriendInvitationStorageChannelEvent");
1350 notification_storage_channel_event.Initialize(
1351 "IApplicationFunctions:NotificationStorageChannelEvent");
1348 health_warning_disappeared_system_event.Initialize( 1352 health_warning_disappeared_system_event.Initialize(
1349 "IApplicationFunctions:HealthWarningDisappearedSystemEvent"); 1353 "IApplicationFunctions:HealthWarningDisappearedSystemEvent");
1350} 1354}
@@ -1762,6 +1766,14 @@ void IApplicationFunctions::TryPopFromFriendInvitationStorageChannel(
1762 rb.Push(ERR_NO_DATA_IN_CHANNEL); 1766 rb.Push(ERR_NO_DATA_IN_CHANNEL);
1763} 1767}
1764 1768
1769void IApplicationFunctions::GetNotificationStorageChannelEvent(Kernel::HLERequestContext& ctx) {
1770 LOG_DEBUG(Service_AM, "called");
1771
1772 IPC::ResponseBuilder rb{ctx, 2, 1};
1773 rb.Push(ResultSuccess);
1774 rb.PushCopyObjects(notification_storage_channel_event.GetReadableEvent());
1775}
1776
1765void IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(Kernel::HLERequestContext& ctx) { 1777void IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(Kernel::HLERequestContext& ctx) {
1766 LOG_DEBUG(Service_AM, "called"); 1778 LOG_DEBUG(Service_AM, "called");
1767 1779
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 184030a8e..c13aa5787 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -295,6 +295,7 @@ private:
295 void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx); 295 void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx);
296 void GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx); 296 void GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx);
297 void TryPopFromFriendInvitationStorageChannel(Kernel::HLERequestContext& ctx); 297 void TryPopFromFriendInvitationStorageChannel(Kernel::HLERequestContext& ctx);
298 void GetNotificationStorageChannelEvent(Kernel::HLERequestContext& ctx);
298 void GetHealthWarningDisappearedSystemEvent(Kernel::HLERequestContext& ctx); 299 void GetHealthWarningDisappearedSystemEvent(Kernel::HLERequestContext& ctx);
299 300
300 bool launch_popped_application_specific = false; 301 bool launch_popped_application_specific = false;
@@ -302,6 +303,7 @@ private:
302 s32 previous_program_index{-1}; 303 s32 previous_program_index{-1};
303 Kernel::KEvent gpu_error_detected_event; 304 Kernel::KEvent gpu_error_detected_event;
304 Kernel::KEvent friend_invitation_storage_channel_event; 305 Kernel::KEvent friend_invitation_storage_channel_event;
306 Kernel::KEvent notification_storage_channel_event;
305 Kernel::KEvent health_warning_disappeared_system_event; 307 Kernel::KEvent health_warning_disappeared_system_event;
306}; 308};
307 309
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 4a9b13e45..c8d65f328 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -97,14 +97,24 @@ ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) cons
97 97
98ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const { 98ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const {
99 std::string path(Common::FS::SanitizePath(path_)); 99 std::string path(Common::FS::SanitizePath(path_));
100 auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path)); 100
101 if (dir == nullptr || Common::FS::GetFilename(Common::FS::GetParentPath(path)).empty()) { 101 // NOTE: This is inaccurate behavior. CreateDirectory is not recursive.
102 dir = backing; 102 // CreateDirectory should return PathNotFound if the parent directory does not exist.
103 } 103 // This is here temporarily in order to have UMM "work" in the meantime.
104 auto new_dir = dir->CreateSubdirectory(Common::FS::GetFilename(path)); 104 // TODO (Morph): Remove this when a hardware test verifies the correct behavior.
105 if (new_dir == nullptr) { 105 const auto components = Common::FS::SplitPathComponents(path);
106 // TODO(DarkLordZach): Find a better error code for this 106 std::string relative_path;
107 return ResultUnknown; 107 for (const auto& component : components) {
108 // Skip empty path components
109 if (component.empty()) {
110 continue;
111 }
112 relative_path = Common::FS::SanitizePath(relative_path + '/' + component);
113 auto new_dir = backing->CreateSubdirectory(relative_path);
114 if (new_dir == nullptr) {
115 // TODO(DarkLordZach): Find a better error code for this
116 return ResultUnknown;
117 }
108 } 118 }
109 return ResultSuccess; 119 return ResultSuccess;
110} 120}
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index ef2becefd..8e9b40c0a 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -15,6 +15,20 @@
15namespace Service::HID { 15namespace Service::HID {
16class Controller_Touchscreen final : public ControllerBase { 16class Controller_Touchscreen final : public ControllerBase {
17public: 17public:
18 enum class TouchScreenModeForNx : u8 {
19 UseSystemSetting,
20 Finger,
21 Heat2,
22 };
23
24 struct TouchScreenConfigurationForNx {
25 TouchScreenModeForNx mode;
26 INSERT_PADDING_BYTES_NOINIT(0x7);
27 INSERT_PADDING_BYTES_NOINIT(0xF); // Reserved
28 };
29 static_assert(sizeof(TouchScreenConfigurationForNx) == 0x17,
30 "TouchScreenConfigurationForNx is an invalid size");
31
18 explicit Controller_Touchscreen(Core::System& system_); 32 explicit Controller_Touchscreen(Core::System& system_);
19 ~Controller_Touchscreen() override; 33 ~Controller_Touchscreen() override;
20 34
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index b8b80570d..a1707a72a 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -331,7 +331,7 @@ Hid::Hid(Core::System& system_)
331 {529, nullptr, "SetDisallowedPalmaConnection"}, 331 {529, nullptr, "SetDisallowedPalmaConnection"},
332 {1000, &Hid::SetNpadCommunicationMode, "SetNpadCommunicationMode"}, 332 {1000, &Hid::SetNpadCommunicationMode, "SetNpadCommunicationMode"},
333 {1001, &Hid::GetNpadCommunicationMode, "GetNpadCommunicationMode"}, 333 {1001, &Hid::GetNpadCommunicationMode, "GetNpadCommunicationMode"},
334 {1002, nullptr, "SetTouchScreenConfiguration"}, 334 {1002, &Hid::SetTouchScreenConfiguration, "SetTouchScreenConfiguration"},
335 {1003, nullptr, "IsFirmwareUpdateNeededForNotification"}, 335 {1003, nullptr, "IsFirmwareUpdateNeededForNotification"},
336 {2000, nullptr, "ActivateDigitizer"}, 336 {2000, nullptr, "ActivateDigitizer"},
337 }; 337 };
@@ -1631,6 +1631,18 @@ void Hid::GetNpadCommunicationMode(Kernel::HLERequestContext& ctx) {
1631 .GetNpadCommunicationMode()); 1631 .GetNpadCommunicationMode());
1632} 1632}
1633 1633
1634void Hid::SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx) {
1635 IPC::RequestParser rp{ctx};
1636 const auto touchscreen_mode{rp.PopRaw<Controller_Touchscreen::TouchScreenConfigurationForNx>()};
1637 const auto applet_resource_user_id{rp.Pop<u64>()};
1638
1639 LOG_WARNING(Service_HID, "(STUBBED) called, touchscreen_mode={}, applet_resource_user_id={}",
1640 touchscreen_mode.mode, applet_resource_user_id);
1641
1642 IPC::ResponseBuilder rb{ctx, 2};
1643 rb.Push(ResultSuccess);
1644}
1645
1634class HidDbg final : public ServiceFramework<HidDbg> { 1646class HidDbg final : public ServiceFramework<HidDbg> {
1635public: 1647public:
1636 explicit HidDbg(Core::System& system_) : ServiceFramework{system_, "hid:dbg"} { 1648 explicit HidDbg(Core::System& system_) : ServiceFramework{system_, "hid:dbg"} {
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 9c5c7f252..b1fe75e94 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -159,6 +159,7 @@ private:
159 void SetPalmaBoostMode(Kernel::HLERequestContext& ctx); 159 void SetPalmaBoostMode(Kernel::HLERequestContext& ctx);
160 void SetNpadCommunicationMode(Kernel::HLERequestContext& ctx); 160 void SetNpadCommunicationMode(Kernel::HLERequestContext& ctx);
161 void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx); 161 void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx);
162 void SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx);
162 163
163 enum class VibrationDeviceType : u32 { 164 enum class VibrationDeviceType : u32 {
164 Unknown = 0, 165 Unknown = 0,
diff --git a/src/core/hle/service/ngct/ngct.cpp b/src/core/hle/service/ngct/ngct.cpp
new file mode 100644
index 000000000..deb3abb28
--- /dev/null
+++ b/src/core/hle/service/ngct/ngct.cpp
@@ -0,0 +1,46 @@
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 "common/string_util.h"
6#include "core/core.h"
7#include "core/hle/ipc_helpers.h"
8#include "core/hle/service/ngct/ngct.h"
9#include "core/hle/service/service.h"
10
11namespace Service::NGCT {
12
13class IService final : public ServiceFramework<IService> {
14public:
15 explicit IService(Core::System& system_) : ServiceFramework{system_, "ngct:u"} {
16 // clang-format off
17 static const FunctionInfo functions[] = {
18 {0, nullptr, "Match"},
19 {1, &IService::Filter, "Filter"},
20 };
21 // clang-format on
22
23 RegisterHandlers(functions);
24 }
25
26private:
27 void Filter(Kernel::HLERequestContext& ctx) {
28 const auto buffer = ctx.ReadBuffer();
29 const auto text = Common::StringFromFixedZeroTerminatedBuffer(
30 reinterpret_cast<const char*>(buffer.data()), buffer.size());
31
32 LOG_WARNING(Service_NGCT, "(STUBBED) called, text={}", text);
33
34 // Return the same string since we don't censor anything
35 ctx.WriteBuffer(buffer);
36
37 IPC::ResponseBuilder rb{ctx, 2};
38 rb.Push(ResultSuccess);
39 }
40};
41
42void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
43 std::make_shared<IService>(system)->InstallAsService(system.ServiceManager());
44}
45
46} // namespace Service::NGCT
diff --git a/src/core/hle/service/ngct/ngct.h b/src/core/hle/service/ngct/ngct.h
new file mode 100644
index 000000000..1f2a47b78
--- /dev/null
+++ b/src/core/hle/service/ngct/ngct.h
@@ -0,0 +1,20 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#pragma once
6
7namespace Core {
8class System;
9}
10
11namespace Service::SM {
12class ServiceManager;
13}
14
15namespace Service::NGCT {
16
17/// Registers all NGCT services with the specified service manager.
18void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
19
20} // namespace Service::NGCT
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 0a53c0c81..9decb9290 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -277,37 +277,45 @@ private:
277 void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) { 277 void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) {
278 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 278 LOG_WARNING(Service_NIFM, "(STUBBED) called");
279 279
280 const SfNetworkProfileData network_profile_data{ 280 const auto net_iface = Network::GetSelectedNetworkInterface();
281 .ip_setting_data{ 281
282 .ip_address_setting{ 282 const SfNetworkProfileData network_profile_data = [&net_iface] {
283 .is_automatic{true}, 283 if (!net_iface) {
284 .current_address{192, 168, 1, 100}, 284 return SfNetworkProfileData{};
285 .subnet_mask{255, 255, 255, 0}, 285 }
286 .gateway{192, 168, 1, 1}, 286
287 }, 287 return SfNetworkProfileData{
288 .dns_setting{ 288 .ip_setting_data{
289 .is_automatic{true}, 289 .ip_address_setting{
290 .primary_dns{1, 1, 1, 1}, 290 .is_automatic{true},
291 .secondary_dns{1, 0, 0, 1}, 291 .current_address{Network::TranslateIPv4(net_iface->ip_address)},
292 .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
293 .gateway{Network::TranslateIPv4(net_iface->gateway)},
294 },
295 .dns_setting{
296 .is_automatic{true},
297 .primary_dns{1, 1, 1, 1},
298 .secondary_dns{1, 0, 0, 1},
299 },
300 .proxy_setting{
301 .enabled{false},
302 .port{},
303 .proxy_server{},
304 .automatic_auth_enabled{},
305 .user{},
306 .password{},
307 },
308 .mtu{1500},
292 }, 309 },
293 .proxy_setting{ 310 .uuid{0xdeadbeef, 0xdeadbeef},
294 .enabled{false}, 311 .network_name{"yuzu Network"},
295 .port{}, 312 .wireless_setting_data{
296 .proxy_server{}, 313 .ssid_length{12},
297 .automatic_auth_enabled{}, 314 .ssid{"yuzu Network"},
298 .user{}, 315 .passphrase{"yuzupassword"},
299 .password{},
300 }, 316 },
301 .mtu{1500}, 317 };
302 }, 318 }();
303 .uuid{0xdeadbeef, 0xdeadbeef},
304 .network_name{"yuzu Network"},
305 .wireless_setting_data{
306 .ssid_length{12},
307 .ssid{"yuzu Network"},
308 .passphrase{"yuzupassword"},
309 },
310 };
311 319
312 ctx.WriteBuffer(network_profile_data); 320 ctx.WriteBuffer(network_profile_data);
313 321
@@ -352,38 +360,33 @@ private:
352 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 360 LOG_WARNING(Service_NIFM, "(STUBBED) called");
353 361
354 struct IpConfigInfo { 362 struct IpConfigInfo {
355 IpAddressSetting ip_address_setting; 363 IpAddressSetting ip_address_setting{};
356 DnsSetting dns_setting; 364 DnsSetting dns_setting{};
357 }; 365 };
358 static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting), 366 static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting),
359 "IpConfigInfo has incorrect size."); 367 "IpConfigInfo has incorrect size.");
360 368
361 IpConfigInfo ip_config_info{ 369 const auto net_iface = Network::GetSelectedNetworkInterface();
362 .ip_address_setting{
363 .is_automatic{true},
364 .current_address{0, 0, 0, 0},
365 .subnet_mask{255, 255, 255, 0},
366 .gateway{192, 168, 1, 1},
367 },
368 .dns_setting{
369 .is_automatic{true},
370 .primary_dns{1, 1, 1, 1},
371 .secondary_dns{1, 0, 0, 1},
372 },
373 };
374 370
375 const auto iface = Network::GetSelectedNetworkInterface(); 371 const IpConfigInfo ip_config_info = [&net_iface] {
376 if (iface) { 372 if (!net_iface) {
377 ip_config_info.ip_address_setting = 373 return IpConfigInfo{};
378 IpAddressSetting{.is_automatic{true}, 374 }
379 .current_address{Network::TranslateIPv4(iface->ip_address)},
380 .subnet_mask{Network::TranslateIPv4(iface->subnet_mask)},
381 .gateway{Network::TranslateIPv4(iface->gateway)}};
382 375
383 } else { 376 return IpConfigInfo{
384 LOG_ERROR(Service_NIFM, 377 .ip_address_setting{
385 "Couldn't get host network configuration info, using default values"); 378 .is_automatic{true},
386 } 379 .current_address{Network::TranslateIPv4(net_iface->ip_address)},
380 .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
381 .gateway{Network::TranslateIPv4(net_iface->gateway)},
382 },
383 .dns_setting{
384 .is_automatic{true},
385 .primary_dns{1, 1, 1, 1},
386 .secondary_dns{1, 0, 0, 1},
387 },
388 };
389 }();
387 390
388 IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)}; 391 IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)};
389 rb.Push(ResultSuccess); 392 rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index ce6065db2..a33e47d0b 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -42,15 +42,14 @@ void nvdisp_disp0::OnClose(DeviceFD fd) {}
42void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, 42void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height,
43 u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform, 43 u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform,
44 const Common::Rectangle<int>& crop_rect) { 44 const Common::Rectangle<int>& crop_rect) {
45 VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle); 45 const VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle);
46 LOG_TRACE(Service, 46 LOG_TRACE(Service,
47 "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}", 47 "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}",
48 addr, offset, width, height, stride, format); 48 addr, offset, width, height, stride, format);
49 49
50 using PixelFormat = Tegra::FramebufferConfig::PixelFormat; 50 const auto pixel_format = static_cast<Tegra::FramebufferConfig::PixelFormat>(format);
51 const Tegra::FramebufferConfig framebuffer{ 51 const Tegra::FramebufferConfig framebuffer{addr, offset, width, height,
52 addr, offset, width, height, stride, static_cast<PixelFormat>(format), 52 stride, pixel_format, transform, crop_rect};
53 transform, crop_rect};
54 53
55 system.GetPerfStats().EndSystemFrame(); 54 system.GetPerfStats().EndSystemFrame();
56 system.GPU().SwapBuffers(&framebuffer); 55 system.GPU().SwapBuffers(&framebuffer);
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index 59ddf6298..b4c3a6099 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -9,17 +9,20 @@
9#include "core/core.h" 9#include "core/core.h"
10#include "core/hle/kernel/k_writable_event.h" 10#include "core/hle/kernel/k_writable_event.h"
11#include "core/hle/kernel/kernel.h" 11#include "core/hle/kernel/kernel.h"
12#include "core/hle/service/kernel_helpers.h"
12#include "core/hle/service/nvflinger/buffer_queue.h" 13#include "core/hle/service/nvflinger/buffer_queue.h"
13 14
14namespace Service::NVFlinger { 15namespace Service::NVFlinger {
15 16
16BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_) 17BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
17 : id(id_), layer_id(layer_id_), buffer_wait_event{kernel} { 18 KernelHelpers::ServiceContext& service_context_)
18 Kernel::KAutoObject::Create(std::addressof(buffer_wait_event)); 19 : id(id_), layer_id(layer_id_), service_context{service_context_} {
19 buffer_wait_event.Initialize("BufferQueue:WaitEvent"); 20 buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent");
20} 21}
21 22
22BufferQueue::~BufferQueue() = default; 23BufferQueue::~BufferQueue() {
24 service_context.CloseEvent(buffer_wait_event);
25}
23 26
24void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) { 27void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) {
25 ASSERT(slot < buffer_slots); 28 ASSERT(slot < buffer_slots);
@@ -41,7 +44,7 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)
41 .multi_fence = {}, 44 .multi_fence = {},
42 }; 45 };
43 46
44 buffer_wait_event.GetWritableEvent().Signal(); 47 buffer_wait_event->GetWritableEvent().Signal();
45} 48}
46 49
47std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width, 50std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width,
@@ -119,7 +122,7 @@ void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& mult
119 } 122 }
120 free_buffers_condition.notify_one(); 123 free_buffers_condition.notify_one();
121 124
122 buffer_wait_event.GetWritableEvent().Signal(); 125 buffer_wait_event->GetWritableEvent().Signal();
123} 126}
124 127
125std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() { 128std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
@@ -154,7 +157,7 @@ void BufferQueue::ReleaseBuffer(u32 slot) {
154 } 157 }
155 free_buffers_condition.notify_one(); 158 free_buffers_condition.notify_one();
156 159
157 buffer_wait_event.GetWritableEvent().Signal(); 160 buffer_wait_event->GetWritableEvent().Signal();
158} 161}
159 162
160void BufferQueue::Connect() { 163void BufferQueue::Connect() {
@@ -169,7 +172,7 @@ void BufferQueue::Disconnect() {
169 std::unique_lock lock{queue_sequence_mutex}; 172 std::unique_lock lock{queue_sequence_mutex};
170 queue_sequence.clear(); 173 queue_sequence.clear();
171 } 174 }
172 buffer_wait_event.GetWritableEvent().Signal(); 175 buffer_wait_event->GetWritableEvent().Signal();
173 is_connect = false; 176 is_connect = false;
174 free_buffers_condition.notify_one(); 177 free_buffers_condition.notify_one();
175} 178}
@@ -189,11 +192,11 @@ u32 BufferQueue::Query(QueryType type) {
189} 192}
190 193
191Kernel::KWritableEvent& BufferQueue::GetWritableBufferWaitEvent() { 194Kernel::KWritableEvent& BufferQueue::GetWritableBufferWaitEvent() {
192 return buffer_wait_event.GetWritableEvent(); 195 return buffer_wait_event->GetWritableEvent();
193} 196}
194 197
195Kernel::KReadableEvent& BufferQueue::GetBufferWaitEvent() { 198Kernel::KReadableEvent& BufferQueue::GetBufferWaitEvent() {
196 return buffer_wait_event.GetReadableEvent(); 199 return buffer_wait_event->GetReadableEvent();
197} 200}
198 201
199} // namespace Service::NVFlinger 202} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index 61e337ac5..78de3f354 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -24,6 +24,10 @@ class KReadableEvent;
24class KWritableEvent; 24class KWritableEvent;
25} // namespace Kernel 25} // namespace Kernel
26 26
27namespace Service::KernelHelpers {
28class ServiceContext;
29} // namespace Service::KernelHelpers
30
27namespace Service::NVFlinger { 31namespace Service::NVFlinger {
28 32
29constexpr u32 buffer_slots = 0x40; 33constexpr u32 buffer_slots = 0x40;
@@ -38,7 +42,9 @@ struct IGBPBuffer {
38 u32_le index; 42 u32_le index;
39 INSERT_PADDING_WORDS(3); 43 INSERT_PADDING_WORDS(3);
40 u32_le gpu_buffer_id; 44 u32_le gpu_buffer_id;
41 INSERT_PADDING_WORDS(17); 45 INSERT_PADDING_WORDS(6);
46 u32_le external_format;
47 INSERT_PADDING_WORDS(10);
42 u32_le nvmap_handle; 48 u32_le nvmap_handle;
43 u32_le offset; 49 u32_le offset;
44 INSERT_PADDING_WORDS(60); 50 INSERT_PADDING_WORDS(60);
@@ -54,7 +60,8 @@ public:
54 NativeWindowFormat = 2, 60 NativeWindowFormat = 2,
55 }; 61 };
56 62
57 explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_); 63 explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
64 KernelHelpers::ServiceContext& service_context_);
58 ~BufferQueue(); 65 ~BufferQueue();
59 66
60 enum class BufferTransformFlags : u32 { 67 enum class BufferTransformFlags : u32 {
@@ -130,12 +137,14 @@ private:
130 std::list<u32> free_buffers; 137 std::list<u32> free_buffers;
131 std::array<Buffer, buffer_slots> buffers; 138 std::array<Buffer, buffer_slots> buffers;
132 std::list<u32> queue_sequence; 139 std::list<u32> queue_sequence;
133 Kernel::KEvent buffer_wait_event; 140 Kernel::KEvent* buffer_wait_event{};
134 141
135 std::mutex free_buffers_mutex; 142 std::mutex free_buffers_mutex;
136 std::condition_variable free_buffers_condition; 143 std::condition_variable free_buffers_condition;
137 144
138 std::mutex queue_sequence_mutex; 145 std::mutex queue_sequence_mutex;
146
147 KernelHelpers::ServiceContext& service_context;
139}; 148};
140 149
141} // namespace Service::NVFlinger 150} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 941748970..3ead813b0 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -61,12 +61,13 @@ void NVFlinger::SplitVSync() {
61 } 61 }
62} 62}
63 63
64NVFlinger::NVFlinger(Core::System& system_) : system(system_) { 64NVFlinger::NVFlinger(Core::System& system_)
65 displays.emplace_back(0, "Default", system); 65 : system(system_), service_context(system_, "nvflinger") {
66 displays.emplace_back(1, "External", system); 66 displays.emplace_back(0, "Default", service_context, system);
67 displays.emplace_back(2, "Edid", system); 67 displays.emplace_back(1, "External", service_context, system);
68 displays.emplace_back(3, "Internal", system); 68 displays.emplace_back(2, "Edid", service_context, system);
69 displays.emplace_back(4, "Null", system); 69 displays.emplace_back(3, "Internal", service_context, system);
70 displays.emplace_back(4, "Null", service_context, system);
70 guard = std::make_shared<std::mutex>(); 71 guard = std::make_shared<std::mutex>();
71 72
72 // Schedule the screen composition events 73 // Schedule the screen composition events
@@ -146,7 +147,7 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
146void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) { 147void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) {
147 const u32 buffer_queue_id = next_buffer_queue_id++; 148 const u32 buffer_queue_id = next_buffer_queue_id++;
148 buffer_queues.emplace_back( 149 buffer_queues.emplace_back(
149 std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id)); 150 std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id, service_context));
150 display.CreateLayer(layer_id, *buffer_queues.back()); 151 display.CreateLayer(layer_id, *buffer_queues.back());
151} 152}
152 153
@@ -297,7 +298,7 @@ void NVFlinger::Compose() {
297 auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0"); 298 auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0");
298 ASSERT(nvdisp); 299 ASSERT(nvdisp);
299 300
300 nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.format, 301 nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.external_format,
301 igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, 302 igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride,
302 buffer->get().transform, buffer->get().crop_rect); 303 buffer->get().transform, buffer->get().crop_rect);
303 304
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index d80fd07ef..6d84cafb4 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -15,6 +15,7 @@
15#include <vector> 15#include <vector>
16 16
17#include "common/common_types.h" 17#include "common/common_types.h"
18#include "core/hle/service/kernel_helpers.h"
18 19
19namespace Common { 20namespace Common {
20class Event; 21class Event;
@@ -135,6 +136,8 @@ private:
135 std::unique_ptr<std::thread> vsync_thread; 136 std::unique_ptr<std::thread> vsync_thread;
136 std::unique_ptr<Common::Event> wait_event; 137 std::unique_ptr<Common::Event> wait_event;
137 std::atomic<bool> is_running{}; 138 std::atomic<bool> is_running{};
139
140 KernelHelpers::ServiceContext service_context;
138}; 141};
139 142
140} // namespace Service::NVFlinger 143} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index b3e50433b..065133166 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -46,6 +46,7 @@
46#include "core/hle/service/ncm/ncm.h" 46#include "core/hle/service/ncm/ncm.h"
47#include "core/hle/service/nfc/nfc.h" 47#include "core/hle/service/nfc/nfc.h"
48#include "core/hle/service/nfp/nfp.h" 48#include "core/hle/service/nfp/nfp.h"
49#include "core/hle/service/ngct/ngct.h"
49#include "core/hle/service/nifm/nifm.h" 50#include "core/hle/service/nifm/nifm.h"
50#include "core/hle/service/nim/nim.h" 51#include "core/hle/service/nim/nim.h"
51#include "core/hle/service/npns/npns.h" 52#include "core/hle/service/npns/npns.h"
@@ -271,6 +272,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
271 NCM::InstallInterfaces(*sm, system); 272 NCM::InstallInterfaces(*sm, system);
272 NFC::InstallInterfaces(*sm, system); 273 NFC::InstallInterfaces(*sm, system);
273 NFP::InstallInterfaces(*sm, system); 274 NFP::InstallInterfaces(*sm, system);
275 NGCT::InstallInterfaces(*sm, system);
274 NIFM::InstallInterfaces(*sm, system); 276 NIFM::InstallInterfaces(*sm, system);
275 NIM::InstallInterfaces(*sm, system); 277 NIM::InstallInterfaces(*sm, system);
276 NPNS::InstallInterfaces(*sm, system); 278 NPNS::InstallInterfaces(*sm, system);
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
index 0dd342dbf..b7705c02a 100644
--- a/src/core/hle/service/vi/display/vi_display.cpp
+++ b/src/core/hle/service/vi/display/vi_display.cpp
@@ -12,18 +12,21 @@
12#include "core/hle/kernel/k_event.h" 12#include "core/hle/kernel/k_event.h"
13#include "core/hle/kernel/k_readable_event.h" 13#include "core/hle/kernel/k_readable_event.h"
14#include "core/hle/kernel/k_writable_event.h" 14#include "core/hle/kernel/k_writable_event.h"
15#include "core/hle/service/kernel_helpers.h"
15#include "core/hle/service/vi/display/vi_display.h" 16#include "core/hle/service/vi/display/vi_display.h"
16#include "core/hle/service/vi/layer/vi_layer.h" 17#include "core/hle/service/vi/layer/vi_layer.h"
17 18
18namespace Service::VI { 19namespace Service::VI {
19 20
20Display::Display(u64 id, std::string name_, Core::System& system) 21Display::Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_,
21 : display_id{id}, name{std::move(name_)}, vsync_event{system.Kernel()} { 22 Core::System& system_)
22 Kernel::KAutoObject::Create(std::addressof(vsync_event)); 23 : display_id{id}, name{std::move(name_)}, service_context{service_context_} {
23 vsync_event.Initialize(fmt::format("Display VSync Event {}", id)); 24 vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id));
24} 25}
25 26
26Display::~Display() = default; 27Display::~Display() {
28 service_context.CloseEvent(vsync_event);
29}
27 30
28Layer& Display::GetLayer(std::size_t index) { 31Layer& Display::GetLayer(std::size_t index) {
29 return *layers.at(index); 32 return *layers.at(index);
@@ -34,11 +37,11 @@ const Layer& Display::GetLayer(std::size_t index) const {
34} 37}
35 38
36Kernel::KReadableEvent& Display::GetVSyncEvent() { 39Kernel::KReadableEvent& Display::GetVSyncEvent() {
37 return vsync_event.GetReadableEvent(); 40 return vsync_event->GetReadableEvent();
38} 41}
39 42
40void Display::SignalVSyncEvent() { 43void Display::SignalVSyncEvent() {
41 vsync_event.GetWritableEvent().Signal(); 44 vsync_event->GetWritableEvent().Signal();
42} 45}
43 46
44void Display::CreateLayer(u64 layer_id, NVFlinger::BufferQueue& buffer_queue) { 47void Display::CreateLayer(u64 layer_id, NVFlinger::BufferQueue& buffer_queue) {
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h
index 166f2a4cc..0979fc421 100644
--- a/src/core/hle/service/vi/display/vi_display.h
+++ b/src/core/hle/service/vi/display/vi_display.h
@@ -18,6 +18,9 @@ class KEvent;
18namespace Service::NVFlinger { 18namespace Service::NVFlinger {
19class BufferQueue; 19class BufferQueue;
20} 20}
21namespace Service::KernelHelpers {
22class ServiceContext;
23} // namespace Service::KernelHelpers
21 24
22namespace Service::VI { 25namespace Service::VI {
23 26
@@ -31,10 +34,13 @@ class Display {
31public: 34public:
32 /// Constructs a display with a given unique ID and name. 35 /// Constructs a display with a given unique ID and name.
33 /// 36 ///
34 /// @param id The unique ID for this display. 37 /// @param id The unique ID for this display.
38 /// @param service_context_ The ServiceContext for the owning service.
35 /// @param name_ The name for this display. 39 /// @param name_ The name for this display.
40 /// @param system_ The global system instance.
36 /// 41 ///
37 Display(u64 id, std::string name_, Core::System& system); 42 Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_,
43 Core::System& system_);
38 ~Display(); 44 ~Display();
39 45
40 /// Gets the unique ID assigned to this display. 46 /// Gets the unique ID assigned to this display.
@@ -98,9 +104,10 @@ public:
98private: 104private:
99 u64 display_id; 105 u64 display_id;
100 std::string name; 106 std::string name;
107 KernelHelpers::ServiceContext& service_context;
101 108
102 std::vector<std::shared_ptr<Layer>> layers; 109 std::vector<std::shared_ptr<Layer>> layers;
103 Kernel::KEvent vsync_event; 110 Kernel::KEvent* vsync_event{};
104}; 111};
105 112
106} // namespace Service::VI 113} // namespace Service::VI
diff --git a/src/core/network/network.cpp b/src/core/network/network.cpp
index 4732d4485..72eea52f0 100644
--- a/src/core/network/network.cpp
+++ b/src/core/network/network.cpp
@@ -7,7 +7,8 @@
7#include <limits> 7#include <limits>
8#include <utility> 8#include <utility>
9#include <vector> 9#include <vector>
10#include "common/common_funcs.h" 10
11#include "common/error.h"
11 12
12#ifdef _WIN32 13#ifdef _WIN32
13#include <winsock2.h> 14#include <winsock2.h>
@@ -223,7 +224,7 @@ Errno GetAndLogLastError() {
223 if (err == Errno::AGAIN) { 224 if (err == Errno::AGAIN) {
224 return err; 225 return err;
225 } 226 }
226 LOG_ERROR(Network, "Socket operation error: {}", NativeErrorToString(e)); 227 LOG_ERROR(Network, "Socket operation error: {}", Common::NativeErrorToString(e));
227 return err; 228 return err;
228} 229}
229 230
diff --git a/src/core/network/network_interface.cpp b/src/core/network/network_interface.cpp
index cecc9aa11..6811f21b1 100644
--- a/src/core/network/network_interface.cpp
+++ b/src/core/network/network_interface.cpp
@@ -37,73 +37,73 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
37 AF_INET, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_GATEWAYS, 37 AF_INET, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_GATEWAYS,
38 nullptr, adapter_addresses.data(), &buf_size); 38 nullptr, adapter_addresses.data(), &buf_size);
39 39
40 if (ret == ERROR_BUFFER_OVERFLOW) { 40 if (ret != ERROR_BUFFER_OVERFLOW) {
41 adapter_addresses.resize((buf_size / sizeof(IP_ADAPTER_ADDRESSES)) + 1);
42 } else {
43 break; 41 break;
44 } 42 }
43
44 adapter_addresses.resize((buf_size / sizeof(IP_ADAPTER_ADDRESSES)) + 1);
45 } 45 }
46 46
47 if (ret == NO_ERROR) { 47 if (ret != NO_ERROR) {
48 std::vector<NetworkInterface> result; 48 LOG_ERROR(Network, "Failed to get network interfaces with GetAdaptersAddresses");
49 return {};
50 }
49 51
50 for (auto current_address = adapter_addresses.data(); current_address != nullptr; 52 std::vector<NetworkInterface> result;
51 current_address = current_address->Next) {
52 if (current_address->FirstUnicastAddress == nullptr ||
53 current_address->FirstUnicastAddress->Address.lpSockaddr == nullptr) {
54 continue;
55 }
56 53
57 if (current_address->OperStatus != IfOperStatusUp) { 54 for (auto current_address = adapter_addresses.data(); current_address != nullptr;
58 continue; 55 current_address = current_address->Next) {
59 } 56 if (current_address->FirstUnicastAddress == nullptr ||
57 current_address->FirstUnicastAddress->Address.lpSockaddr == nullptr) {
58 continue;
59 }
60 60
61 const auto ip_addr = Common::BitCast<struct sockaddr_in>( 61 if (current_address->OperStatus != IfOperStatusUp) {
62 *current_address->FirstUnicastAddress->Address.lpSockaddr) 62 continue;
63 .sin_addr; 63 }
64 64
65 ULONG mask = 0; 65 const auto ip_addr = Common::BitCast<struct sockaddr_in>(
66 if (ConvertLengthToIpv4Mask(current_address->FirstUnicastAddress->OnLinkPrefixLength, 66 *current_address->FirstUnicastAddress->Address.lpSockaddr)
67 &mask) != NO_ERROR) { 67 .sin_addr;
68 LOG_ERROR(Network, "Failed to convert IPv4 prefix length to subnet mask");
69 continue;
70 }
71 68
72 struct in_addr gateway = {.S_un{.S_addr{0}}}; 69 ULONG mask = 0;
73 if (current_address->FirstGatewayAddress != nullptr && 70 if (ConvertLengthToIpv4Mask(current_address->FirstUnicastAddress->OnLinkPrefixLength,
74 current_address->FirstGatewayAddress->Address.lpSockaddr != nullptr) { 71 &mask) != NO_ERROR) {
75 gateway = Common::BitCast<struct sockaddr_in>( 72 LOG_ERROR(Network, "Failed to convert IPv4 prefix length to subnet mask");
76 *current_address->FirstGatewayAddress->Address.lpSockaddr) 73 continue;
77 .sin_addr; 74 }
78 }
79 75
80 result.push_back(NetworkInterface{ 76 struct in_addr gateway = {.S_un{.S_addr{0}}};
81 .name{Common::UTF16ToUTF8(std::wstring{current_address->FriendlyName})}, 77 if (current_address->FirstGatewayAddress != nullptr &&
82 .ip_address{ip_addr}, 78 current_address->FirstGatewayAddress->Address.lpSockaddr != nullptr) {
83 .subnet_mask = in_addr{.S_un{.S_addr{mask}}}, 79 gateway = Common::BitCast<struct sockaddr_in>(
84 .gateway = gateway}); 80 *current_address->FirstGatewayAddress->Address.lpSockaddr)
81 .sin_addr;
85 } 82 }
86 83
87 return result; 84 result.emplace_back(NetworkInterface{
88 } else { 85 .name{Common::UTF16ToUTF8(std::wstring{current_address->FriendlyName})},
89 LOG_ERROR(Network, "Failed to get network interfaces with GetAdaptersAddresses"); 86 .ip_address{ip_addr},
90 return {}; 87 .subnet_mask = in_addr{.S_un{.S_addr{mask}}},
88 .gateway = gateway});
91 } 89 }
90
91 return result;
92} 92}
93 93
94#else 94#else
95 95
96std::vector<NetworkInterface> GetAvailableNetworkInterfaces() { 96std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
97 std::vector<NetworkInterface> result;
98
99 struct ifaddrs* ifaddr = nullptr; 97 struct ifaddrs* ifaddr = nullptr;
100 98
101 if (getifaddrs(&ifaddr) != 0) { 99 if (getifaddrs(&ifaddr) != 0) {
102 LOG_ERROR(Network, "Failed to get network interfaces with getifaddrs: {}", 100 LOG_ERROR(Network, "Failed to get network interfaces with getifaddrs: {}",
103 std::strerror(errno)); 101 std::strerror(errno));
104 return result; 102 return {};
105 } 103 }
106 104
105 std::vector<NetworkInterface> result;
106
107 for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { 107 for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
108 if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr) { 108 if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr) {
109 continue; 109 continue;
@@ -117,55 +117,62 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
117 continue; 117 continue;
118 } 118 }
119 119
120 std::uint32_t gateway{0}; 120 u32 gateway{};
121
121 std::ifstream file{"/proc/net/route"}; 122 std::ifstream file{"/proc/net/route"};
122 if (file.is_open()) { 123 if (!file.is_open()) {
124 LOG_ERROR(Network, "Failed to open \"/proc/net/route\"");
123 125
124 // ignore header 126 result.emplace_back(NetworkInterface{
125 file.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); 127 .name{ifa->ifa_name},
128 .ip_address{Common::BitCast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr},
129 .subnet_mask{Common::BitCast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr},
130 .gateway{in_addr{.s_addr = gateway}}});
131 continue;
132 }
126 133
127 bool gateway_found = false; 134 // ignore header
135 file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
128 136
129 for (std::string line; std::getline(file, line);) { 137 bool gateway_found = false;
130 std::istringstream iss{line};
131 138
132 std::string iface_name{}; 139 for (std::string line; std::getline(file, line);) {
133 iss >> iface_name; 140 std::istringstream iss{line};
134 if (iface_name != ifa->ifa_name) {
135 continue;
136 }
137 141
138 iss >> std::hex; 142 std::string iface_name;
143 iss >> iface_name;
144 if (iface_name != ifa->ifa_name) {
145 continue;
146 }
139 147
140 std::uint32_t dest{0}; 148 iss >> std::hex;
141 iss >> dest;
142 if (dest != 0) {
143 // not the default route
144 continue;
145 }
146 149
147 iss >> gateway; 150 u32 dest{};
151 iss >> dest;
152 if (dest != 0) {
153 // not the default route
154 continue;
155 }
148 156
149 std::uint16_t flags{0}; 157 iss >> gateway;
150 iss >> flags;
151 158
152 // flag RTF_GATEWAY (defined in <linux/route.h>) 159 u16 flags{};
153 if ((flags & 0x2) == 0) { 160 iss >> flags;
154 continue;
155 }
156 161
157 gateway_found = true; 162 // flag RTF_GATEWAY (defined in <linux/route.h>)
158 break; 163 if ((flags & 0x2) == 0) {
164 continue;
159 } 165 }
160 166
161 if (!gateway_found) { 167 gateway_found = true;
162 gateway = 0; 168 break;
163 }
164 } else {
165 LOG_ERROR(Network, "Failed to open \"/proc/net/route\"");
166 } 169 }
167 170
168 result.push_back(NetworkInterface{ 171 if (!gateway_found) {
172 gateway = 0;
173 }
174
175 result.emplace_back(NetworkInterface{
169 .name{ifa->ifa_name}, 176 .name{ifa->ifa_name},
170 .ip_address{Common::BitCast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr}, 177 .ip_address{Common::BitCast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr},
171 .subnet_mask{Common::BitCast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr}, 178 .subnet_mask{Common::BitCast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr},
@@ -180,11 +187,11 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
180#endif 187#endif
181 188
182std::optional<NetworkInterface> GetSelectedNetworkInterface() { 189std::optional<NetworkInterface> GetSelectedNetworkInterface() {
183 const std::string& selected_network_interface = Settings::values.network_interface.GetValue(); 190 const auto& selected_network_interface = Settings::values.network_interface.GetValue();
184 const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); 191 const auto network_interfaces = Network::GetAvailableNetworkInterfaces();
185 if (network_interfaces.size() == 0) { 192 if (network_interfaces.size() == 0) {
186 LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces"); 193 LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces");
187 return {}; 194 return std::nullopt;
188 } 195 }
189 196
190 const auto res = 197 const auto res =
@@ -192,12 +199,12 @@ std::optional<NetworkInterface> GetSelectedNetworkInterface() {
192 return iface.name == selected_network_interface; 199 return iface.name == selected_network_interface;
193 }); 200 });
194 201
195 if (res != network_interfaces.end()) { 202 if (res == network_interfaces.end()) {
196 return *res;
197 } else {
198 LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface); 203 LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface);
199 return {}; 204 return std::nullopt;
200 } 205 }
206
207 return *res;
201} 208}
202 209
203} // namespace Network 210} // namespace Network
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 5a8cfd301..1f1607998 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -72,6 +72,18 @@ static const char* TranslateGPUAccuracyLevel(Settings::GPUAccuracy backend) {
72 return "Unknown"; 72 return "Unknown";
73} 73}
74 74
75static const char* TranslateNvdecEmulation(Settings::NvdecEmulation backend) {
76 switch (backend) {
77 case Settings::NvdecEmulation::Off:
78 return "Off";
79 case Settings::NvdecEmulation::CPU:
80 return "CPU";
81 case Settings::NvdecEmulation::GPU:
82 return "GPU";
83 }
84 return "Unknown";
85}
86
75u64 GetTelemetryId() { 87u64 GetTelemetryId() {
76 u64 telemetry_id{}; 88 u64 telemetry_id{};
77 const auto filename = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "telemetry_id"; 89 const auto filename = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "telemetry_id";
@@ -229,8 +241,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
229 TranslateGPUAccuracyLevel(Settings::values.gpu_accuracy.GetValue())); 241 TranslateGPUAccuracyLevel(Settings::values.gpu_accuracy.GetValue()));
230 AddField(field_type, "Renderer_UseAsynchronousGpuEmulation", 242 AddField(field_type, "Renderer_UseAsynchronousGpuEmulation",
231 Settings::values.use_asynchronous_gpu_emulation.GetValue()); 243 Settings::values.use_asynchronous_gpu_emulation.GetValue());
232 AddField(field_type, "Renderer_UseNvdecEmulation", 244 AddField(field_type, "Renderer_NvdecEmulation",
233 Settings::values.use_nvdec_emulation.GetValue()); 245 TranslateNvdecEmulation(Settings::values.nvdec_emulation.GetValue()));
234 AddField(field_type, "Renderer_AccelerateASTC", Settings::values.accelerate_astc.GetValue()); 246 AddField(field_type, "Renderer_AccelerateASTC", Settings::values.accelerate_astc.GetValue());
235 AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue()); 247 AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue());
236 AddField(field_type, "Renderer_ShaderBackend", 248 AddField(field_type, "Renderer_ShaderBackend",
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_warp.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_warp.cpp
index a982dd8a2..cd285e2c8 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_warp.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_warp.cpp
@@ -11,6 +11,8 @@
11 11
12namespace Shader::Backend::GLSL { 12namespace Shader::Backend::GLSL {
13namespace { 13namespace {
14constexpr char THREAD_ID[]{"gl_SubGroupInvocationARB"};
15
14void SetInBoundsFlag(EmitContext& ctx, IR::Inst& inst) { 16void SetInBoundsFlag(EmitContext& ctx, IR::Inst& inst) {
15 IR::Inst* const in_bounds{inst.GetAssociatedPseudoOperation(IR::Opcode::GetInBoundsFromOp)}; 17 IR::Inst* const in_bounds{inst.GetAssociatedPseudoOperation(IR::Opcode::GetInBoundsFromOp)};
16 if (!in_bounds) { 18 if (!in_bounds) {
@@ -43,84 +45,100 @@ void UseShuffleNv(EmitContext& ctx, IR::Inst& inst, std::string_view shfl_op,
43 ctx.AddU32("{}={}({},{},{},shfl_in_bounds);", inst, shfl_op, value, index, width); 45 ctx.AddU32("{}={}({},{},{},shfl_in_bounds);", inst, shfl_op, value, index, width);
44 SetInBoundsFlag(ctx, inst); 46 SetInBoundsFlag(ctx, inst);
45} 47}
48
49std::string_view BallotIndex(EmitContext& ctx) {
50 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
51 return ".x";
52 }
53 return "[gl_SubGroupInvocationARB>>5]";
54}
55
56std::string GetMask(EmitContext& ctx, std::string_view mask) {
57 const auto ballot_index{BallotIndex(ctx)};
58 return fmt::format("uint(uvec2({}){})", mask, ballot_index);
59}
46} // Anonymous namespace 60} // Anonymous namespace
47 61
48void EmitLaneId(EmitContext& ctx, IR::Inst& inst) { 62void EmitLaneId(EmitContext& ctx, IR::Inst& inst) {
49 ctx.AddU32("{}=gl_SubGroupInvocationARB&31u;", inst); 63 ctx.AddU32("{}={}&31u;", inst, THREAD_ID);
50} 64}
51 65
52void EmitVoteAll(EmitContext& ctx, IR::Inst& inst, std::string_view pred) { 66void EmitVoteAll(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
53 if (!ctx.profile.warp_size_potentially_larger_than_guest) { 67 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
54 ctx.AddU1("{}=allInvocationsEqualARB({});", inst, pred); 68 ctx.AddU1("{}=allInvocationsEqualARB({});", inst, pred);
55 } else { 69 return;
56 const auto active_mask{fmt::format("uvec2(ballotARB(true))[gl_SubGroupInvocationARB]")};
57 const auto ballot{fmt::format("uvec2(ballotARB({}))[gl_SubGroupInvocationARB]", pred)};
58 ctx.AddU1("{}=({}&{})=={};", inst, ballot, active_mask, active_mask);
59 } 70 }
71 const auto ballot_index{BallotIndex(ctx)};
72 const auto active_mask{fmt::format("uvec2(ballotARB(true)){}", ballot_index)};
73 const auto ballot{fmt::format("uvec2(ballotARB({})){}", pred, ballot_index)};
74 ctx.AddU1("{}=({}&{})=={};", inst, ballot, active_mask, active_mask);
60} 75}
61 76
62void EmitVoteAny(EmitContext& ctx, IR::Inst& inst, std::string_view pred) { 77void EmitVoteAny(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
63 if (!ctx.profile.warp_size_potentially_larger_than_guest) { 78 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
64 ctx.AddU1("{}=anyInvocationARB({});", inst, pred); 79 ctx.AddU1("{}=anyInvocationARB({});", inst, pred);
65 } else { 80 return;
66 const auto active_mask{fmt::format("uvec2(ballotARB(true))[gl_SubGroupInvocationARB]")};
67 const auto ballot{fmt::format("uvec2(ballotARB({}))[gl_SubGroupInvocationARB]", pred)};
68 ctx.AddU1("{}=({}&{})!=0u;", inst, ballot, active_mask, active_mask);
69 } 81 }
82 const auto ballot_index{BallotIndex(ctx)};
83 const auto active_mask{fmt::format("uvec2(ballotARB(true)){}", ballot_index)};
84 const auto ballot{fmt::format("uvec2(ballotARB({})){}", pred, ballot_index)};
85 ctx.AddU1("{}=({}&{})!=0u;", inst, ballot, active_mask, active_mask);
70} 86}
71 87
72void EmitVoteEqual(EmitContext& ctx, IR::Inst& inst, std::string_view pred) { 88void EmitVoteEqual(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
73 if (!ctx.profile.warp_size_potentially_larger_than_guest) { 89 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
74 ctx.AddU1("{}=allInvocationsEqualARB({});", inst, pred); 90 ctx.AddU1("{}=allInvocationsEqualARB({});", inst, pred);
75 } else { 91 return;
76 const auto active_mask{fmt::format("uvec2(ballotARB(true))[gl_SubGroupInvocationARB]")};
77 const auto ballot{fmt::format("uvec2(ballotARB({}))[gl_SubGroupInvocationARB]", pred)};
78 const auto value{fmt::format("({}^{})", ballot, active_mask)};
79 ctx.AddU1("{}=({}==0)||({}=={});", inst, value, value, active_mask);
80 } 92 }
93 const auto ballot_index{BallotIndex(ctx)};
94 const auto active_mask{fmt::format("uvec2(ballotARB(true)){}", ballot_index)};
95 const auto ballot{fmt::format("uvec2(ballotARB({})){}", pred, ballot_index)};
96 const auto value{fmt::format("({}^{})", ballot, active_mask)};
97 ctx.AddU1("{}=({}==0)||({}=={});", inst, value, value, active_mask);
81} 98}
82 99
83void EmitSubgroupBallot(EmitContext& ctx, IR::Inst& inst, std::string_view pred) { 100void EmitSubgroupBallot(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
84 if (!ctx.profile.warp_size_potentially_larger_than_guest) { 101 const auto ballot_index{BallotIndex(ctx)};
85 ctx.AddU32("{}=uvec2(ballotARB({})).x;", inst, pred); 102 ctx.AddU32("{}=uvec2(ballotARB({})){};", inst, pred, ballot_index);
86 } else {
87 ctx.AddU32("{}=uvec2(ballotARB({}))[gl_SubGroupInvocationARB];", inst, pred);
88 }
89} 103}
90 104
91void EmitSubgroupEqMask(EmitContext& ctx, IR::Inst& inst) { 105void EmitSubgroupEqMask(EmitContext& ctx, IR::Inst& inst) {
92 ctx.AddU32("{}=uint(gl_SubGroupEqMaskARB.x);", inst); 106 ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupEqMaskARB"));
93} 107}
94 108
95void EmitSubgroupLtMask(EmitContext& ctx, IR::Inst& inst) { 109void EmitSubgroupLtMask(EmitContext& ctx, IR::Inst& inst) {
96 ctx.AddU32("{}=uint(gl_SubGroupLtMaskARB.x);", inst); 110 ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupLtMaskARB"));
97} 111}
98 112
99void EmitSubgroupLeMask(EmitContext& ctx, IR::Inst& inst) { 113void EmitSubgroupLeMask(EmitContext& ctx, IR::Inst& inst) {
100 ctx.AddU32("{}=uint(gl_SubGroupLeMaskARB.x);", inst); 114 ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupLeMaskARB"));
101} 115}
102 116
103void EmitSubgroupGtMask(EmitContext& ctx, IR::Inst& inst) { 117void EmitSubgroupGtMask(EmitContext& ctx, IR::Inst& inst) {
104 ctx.AddU32("{}=uint(gl_SubGroupGtMaskARB.x);", inst); 118 ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupGtMaskARB"));
105} 119}
106 120
107void EmitSubgroupGeMask(EmitContext& ctx, IR::Inst& inst) { 121void EmitSubgroupGeMask(EmitContext& ctx, IR::Inst& inst) {
108 ctx.AddU32("{}=uint(gl_SubGroupGeMaskARB.x);", inst); 122 ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupGeMaskARB"));
109} 123}
110 124
111void EmitShuffleIndex(EmitContext& ctx, IR::Inst& inst, std::string_view value, 125void EmitShuffleIndex(EmitContext& ctx, IR::Inst& inst, std::string_view value,
112 std::string_view index, std::string_view clamp, 126 std::string_view index, std::string_view clamp, std::string_view seg_mask) {
113 std::string_view segmentation_mask) {
114 if (ctx.profile.support_gl_warp_intrinsics) { 127 if (ctx.profile.support_gl_warp_intrinsics) {
115 UseShuffleNv(ctx, inst, "shuffleNV", value, index, clamp, segmentation_mask); 128 UseShuffleNv(ctx, inst, "shuffleNV", value, index, clamp, seg_mask);
116 return; 129 return;
117 } 130 }
118 const auto not_seg_mask{fmt::format("(~{})", segmentation_mask)}; 131 const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest};
119 const auto thread_id{"gl_SubGroupInvocationARB"}; 132 const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"};
120 const auto min_thread_id{ComputeMinThreadId(thread_id, segmentation_mask)}; 133 const auto upper_index{fmt::format("{}?{}+32:{}", is_upper_partition, index, index)};
121 const auto max_thread_id{ComputeMaxThreadId(min_thread_id, clamp, not_seg_mask)}; 134 const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)};
135
136 const auto not_seg_mask{fmt::format("(~{})", seg_mask)};
137 const auto min_thread_id{ComputeMinThreadId(THREAD_ID, seg_mask)};
138 const auto max_thread_id{
139 ComputeMaxThreadId(min_thread_id, big_warp ? upper_clamp : clamp, not_seg_mask)};
122 140
123 const auto lhs{fmt::format("({}&{})", index, not_seg_mask)}; 141 const auto lhs{fmt::format("({}&{})", big_warp ? upper_index : index, not_seg_mask)};
124 const auto src_thread_id{fmt::format("({})|({})", lhs, min_thread_id)}; 142 const auto src_thread_id{fmt::format("({})|({})", lhs, min_thread_id)};
125 ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id); 143 ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id);
126 SetInBoundsFlag(ctx, inst); 144 SetInBoundsFlag(ctx, inst);
@@ -128,29 +146,34 @@ void EmitShuffleIndex(EmitContext& ctx, IR::Inst& inst, std::string_view value,
128} 146}
129 147
130void EmitShuffleUp(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view index, 148void EmitShuffleUp(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view index,
131 std::string_view clamp, std::string_view segmentation_mask) { 149 std::string_view clamp, std::string_view seg_mask) {
132 if (ctx.profile.support_gl_warp_intrinsics) { 150 if (ctx.profile.support_gl_warp_intrinsics) {
133 UseShuffleNv(ctx, inst, "shuffleUpNV", value, index, clamp, segmentation_mask); 151 UseShuffleNv(ctx, inst, "shuffleUpNV", value, index, clamp, seg_mask);
134 return; 152 return;
135 } 153 }
136 const auto thread_id{"gl_SubGroupInvocationARB"}; 154 const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest};
137 const auto max_thread_id{GetMaxThreadId(thread_id, clamp, segmentation_mask)}; 155 const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"};
138 const auto src_thread_id{fmt::format("({}-{})", thread_id, index)}; 156 const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)};
157
158 const auto max_thread_id{GetMaxThreadId(THREAD_ID, big_warp ? upper_clamp : clamp, seg_mask)};
159 const auto src_thread_id{fmt::format("({}-{})", THREAD_ID, index)};
139 ctx.Add("shfl_in_bounds=int({})>=int({});", src_thread_id, max_thread_id); 160 ctx.Add("shfl_in_bounds=int({})>=int({});", src_thread_id, max_thread_id);
140 SetInBoundsFlag(ctx, inst); 161 SetInBoundsFlag(ctx, inst);
141 ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value); 162 ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
142} 163}
143 164
144void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, std::string_view value, 165void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, std::string_view value,
145 std::string_view index, std::string_view clamp, 166 std::string_view index, std::string_view clamp, std::string_view seg_mask) {
146 std::string_view segmentation_mask) {
147 if (ctx.profile.support_gl_warp_intrinsics) { 167 if (ctx.profile.support_gl_warp_intrinsics) {
148 UseShuffleNv(ctx, inst, "shuffleDownNV", value, index, clamp, segmentation_mask); 168 UseShuffleNv(ctx, inst, "shuffleDownNV", value, index, clamp, seg_mask);
149 return; 169 return;
150 } 170 }
151 const auto thread_id{"gl_SubGroupInvocationARB"}; 171 const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest};
152 const auto max_thread_id{GetMaxThreadId(thread_id, clamp, segmentation_mask)}; 172 const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"};
153 const auto src_thread_id{fmt::format("({}+{})", thread_id, index)}; 173 const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)};
174
175 const auto max_thread_id{GetMaxThreadId(THREAD_ID, big_warp ? upper_clamp : clamp, seg_mask)};
176 const auto src_thread_id{fmt::format("({}+{})", THREAD_ID, index)};
154 ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id); 177 ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id);
155 SetInBoundsFlag(ctx, inst); 178 SetInBoundsFlag(ctx, inst);
156 ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value); 179 ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
@@ -158,14 +181,17 @@ void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, std::string_view value,
158 181
159void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, std::string_view value, 182void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, std::string_view value,
160 std::string_view index, std::string_view clamp, 183 std::string_view index, std::string_view clamp,
161 std::string_view segmentation_mask) { 184 std::string_view seg_mask) {
162 if (ctx.profile.support_gl_warp_intrinsics) { 185 if (ctx.profile.support_gl_warp_intrinsics) {
163 UseShuffleNv(ctx, inst, "shuffleXorNV", value, index, clamp, segmentation_mask); 186 UseShuffleNv(ctx, inst, "shuffleXorNV", value, index, clamp, seg_mask);
164 return; 187 return;
165 } 188 }
166 const auto thread_id{"gl_SubGroupInvocationARB"}; 189 const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest};
167 const auto max_thread_id{GetMaxThreadId(thread_id, clamp, segmentation_mask)}; 190 const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"};
168 const auto src_thread_id{fmt::format("({}^{})", thread_id, index)}; 191 const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)};
192
193 const auto max_thread_id{GetMaxThreadId(THREAD_ID, big_warp ? upper_clamp : clamp, seg_mask)};
194 const auto src_thread_id{fmt::format("({}^{})", THREAD_ID, index)};
169 ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id); 195 ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id);
170 SetInBoundsFlag(ctx, inst); 196 SetInBoundsFlag(ctx, inst);
171 ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value); 197 ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
diff --git a/src/shader_recompiler/backend/spirv/emit_context.cpp b/src/shader_recompiler/backend/spirv/emit_context.cpp
index 2d29d8c14..2885e6799 100644
--- a/src/shader_recompiler/backend/spirv/emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_context.cpp
@@ -15,6 +15,8 @@
15 15
16namespace Shader::Backend::SPIRV { 16namespace Shader::Backend::SPIRV {
17namespace { 17namespace {
18constexpr size_t NUM_FIXEDFNCTEXTURE = 10;
19
18enum class Operation { 20enum class Operation {
19 Increment, 21 Increment,
20 Decrement, 22 Decrement,
@@ -427,6 +429,16 @@ Id DescType(EmitContext& ctx, Id sampled_type, Id pointer_type, u32 count) {
427 return pointer_type; 429 return pointer_type;
428 } 430 }
429} 431}
432
433size_t FindNextUnusedLocation(const std::bitset<IR::NUM_GENERICS>& used_locations,
434 size_t start_offset) {
435 for (size_t location = start_offset; location < used_locations.size(); ++location) {
436 if (!used_locations.test(location)) {
437 return location;
438 }
439 }
440 throw RuntimeError("Unable to get an unused location for legacy attribute");
441}
430} // Anonymous namespace 442} // Anonymous namespace
431 443
432void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name) { 444void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name) {
@@ -1227,6 +1239,7 @@ void EmitContext::DefineInputs(const IR::Program& program) {
1227 loads[IR::Attribute::TessellationEvaluationPointV]) { 1239 loads[IR::Attribute::TessellationEvaluationPointV]) {
1228 tess_coord = DefineInput(*this, F32[3], false, spv::BuiltIn::TessCoord); 1240 tess_coord = DefineInput(*this, F32[3], false, spv::BuiltIn::TessCoord);
1229 } 1241 }
1242 std::bitset<IR::NUM_GENERICS> used_locations{};
1230 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) { 1243 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
1231 const AttributeType input_type{runtime_info.generic_input_types[index]}; 1244 const AttributeType input_type{runtime_info.generic_input_types[index]};
1232 if (!runtime_info.previous_stage_stores.Generic(index)) { 1245 if (!runtime_info.previous_stage_stores.Generic(index)) {
@@ -1238,6 +1251,7 @@ void EmitContext::DefineInputs(const IR::Program& program) {
1238 if (input_type == AttributeType::Disabled) { 1251 if (input_type == AttributeType::Disabled) {
1239 continue; 1252 continue;
1240 } 1253 }
1254 used_locations.set(index);
1241 const Id type{GetAttributeType(*this, input_type)}; 1255 const Id type{GetAttributeType(*this, input_type)};
1242 const Id id{DefineInput(*this, type, true)}; 1256 const Id id{DefineInput(*this, type, true)};
1243 Decorate(id, spv::Decoration::Location, static_cast<u32>(index)); 1257 Decorate(id, spv::Decoration::Location, static_cast<u32>(index));
@@ -1263,6 +1277,26 @@ void EmitContext::DefineInputs(const IR::Program& program) {
1263 break; 1277 break;
1264 } 1278 }
1265 } 1279 }
1280 size_t previous_unused_location = 0;
1281 if (loads.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
1282 const size_t location = FindNextUnusedLocation(used_locations, previous_unused_location);
1283 previous_unused_location = location;
1284 used_locations.set(location);
1285 const Id id{DefineInput(*this, F32[4], true)};
1286 Decorate(id, spv::Decoration::Location, location);
1287 input_front_color = id;
1288 }
1289 for (size_t index = 0; index < NUM_FIXEDFNCTEXTURE; ++index) {
1290 if (loads.AnyComponent(IR::Attribute::FixedFncTexture0S + index * 4)) {
1291 const size_t location =
1292 FindNextUnusedLocation(used_locations, previous_unused_location);
1293 previous_unused_location = location;
1294 used_locations.set(location);
1295 const Id id{DefineInput(*this, F32[4], true)};
1296 Decorate(id, spv::Decoration::Location, location);
1297 input_fixed_fnc_textures[index] = id;
1298 }
1299 }
1266 if (stage == Stage::TessellationEval) { 1300 if (stage == Stage::TessellationEval) {
1267 for (size_t index = 0; index < info.uses_patches.size(); ++index) { 1301 for (size_t index = 0; index < info.uses_patches.size(); ++index) {
1268 if (!info.uses_patches[index]) { 1302 if (!info.uses_patches[index]) {
@@ -1313,9 +1347,31 @@ void EmitContext::DefineOutputs(const IR::Program& program) {
1313 viewport_mask = DefineOutput(*this, TypeArray(U32[1], Const(1u)), std::nullopt, 1347 viewport_mask = DefineOutput(*this, TypeArray(U32[1], Const(1u)), std::nullopt,
1314 spv::BuiltIn::ViewportMaskNV); 1348 spv::BuiltIn::ViewportMaskNV);
1315 } 1349 }
1350 std::bitset<IR::NUM_GENERICS> used_locations{};
1316 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) { 1351 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
1317 if (info.stores.Generic(index)) { 1352 if (info.stores.Generic(index)) {
1318 DefineGenericOutput(*this, index, invocations); 1353 DefineGenericOutput(*this, index, invocations);
1354 used_locations.set(index);
1355 }
1356 }
1357 size_t previous_unused_location = 0;
1358 if (info.stores.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
1359 const size_t location = FindNextUnusedLocation(used_locations, previous_unused_location);
1360 previous_unused_location = location;
1361 used_locations.set(location);
1362 const Id id{DefineOutput(*this, F32[4], invocations)};
1363 Decorate(id, spv::Decoration::Location, static_cast<u32>(location));
1364 output_front_color = id;
1365 }
1366 for (size_t index = 0; index < NUM_FIXEDFNCTEXTURE; ++index) {
1367 if (info.stores.AnyComponent(IR::Attribute::FixedFncTexture0S + index * 4)) {
1368 const size_t location =
1369 FindNextUnusedLocation(used_locations, previous_unused_location);
1370 previous_unused_location = location;
1371 used_locations.set(location);
1372 const Id id{DefineOutput(*this, F32[4], invocations)};
1373 Decorate(id, spv::Decoration::Location, location);
1374 output_fixed_fnc_textures[index] = id;
1319 } 1375 }
1320 } 1376 }
1321 switch (stage) { 1377 switch (stage) {
diff --git a/src/shader_recompiler/backend/spirv/emit_context.h b/src/shader_recompiler/backend/spirv/emit_context.h
index e277bc358..847d0c0e6 100644
--- a/src/shader_recompiler/backend/spirv/emit_context.h
+++ b/src/shader_recompiler/backend/spirv/emit_context.h
@@ -268,10 +268,14 @@ public:
268 Id write_global_func_u32x4{}; 268 Id write_global_func_u32x4{};
269 269
270 Id input_position{}; 270 Id input_position{};
271 Id input_front_color{};
272 std::array<Id, 10> input_fixed_fnc_textures{};
271 std::array<Id, 32> input_generics{}; 273 std::array<Id, 32> input_generics{};
272 274
273 Id output_point_size{}; 275 Id output_point_size{};
274 Id output_position{}; 276 Id output_position{};
277 Id output_front_color{};
278 std::array<Id, 10> output_fixed_fnc_textures{};
275 std::array<std::array<GenericElementInfo, 4>, 32> output_generics{}; 279 std::array<std::array<GenericElementInfo, 4>, 32> output_generics{};
276 280
277 Id output_tess_level_outer{}; 281 Id output_tess_level_outer{};
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
index 14c77f162..68f360b3c 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
@@ -43,6 +43,25 @@ Id AttrPointer(EmitContext& ctx, Id pointer_type, Id vertex, Id base, Args&&...
43 } 43 }
44} 44}
45 45
46bool IsFixedFncTexture(IR::Attribute attribute) {
47 return attribute >= IR::Attribute::FixedFncTexture0S &&
48 attribute <= IR::Attribute::FixedFncTexture9Q;
49}
50
51u32 FixedFncTextureAttributeIndex(IR::Attribute attribute) {
52 if (!IsFixedFncTexture(attribute)) {
53 throw InvalidArgument("Attribute {} is not a FixedFncTexture", attribute);
54 }
55 return (static_cast<u32>(attribute) - static_cast<u32>(IR::Attribute::FixedFncTexture0S)) / 4u;
56}
57
58u32 FixedFncTextureAttributeElement(IR::Attribute attribute) {
59 if (!IsFixedFncTexture(attribute)) {
60 throw InvalidArgument("Attribute {} is not a FixedFncTexture", attribute);
61 }
62 return static_cast<u32>(attribute) % 4u;
63}
64
46template <typename... Args> 65template <typename... Args>
47Id OutputAccessChain(EmitContext& ctx, Id result_type, Id base, Args&&... args) { 66Id OutputAccessChain(EmitContext& ctx, Id result_type, Id base, Args&&... args) {
48 if (ctx.stage == Stage::TessellationControl) { 67 if (ctx.stage == Stage::TessellationControl) {
@@ -74,6 +93,13 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
74 return OutputAccessChain(ctx, ctx.output_f32, info.id, index_id); 93 return OutputAccessChain(ctx, ctx.output_f32, info.id, index_id);
75 } 94 }
76 } 95 }
96 if (IsFixedFncTexture(attr)) {
97 const u32 index{FixedFncTextureAttributeIndex(attr)};
98 const u32 element{FixedFncTextureAttributeElement(attr)};
99 const Id element_id{ctx.Const(element)};
100 return OutputAccessChain(ctx, ctx.output_f32, ctx.output_fixed_fnc_textures[index],
101 element_id);
102 }
77 switch (attr) { 103 switch (attr) {
78 case IR::Attribute::PointSize: 104 case IR::Attribute::PointSize:
79 return ctx.output_point_size; 105 return ctx.output_point_size;
@@ -85,6 +111,14 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
85 const Id element_id{ctx.Const(element)}; 111 const Id element_id{ctx.Const(element)};
86 return OutputAccessChain(ctx, ctx.output_f32, ctx.output_position, element_id); 112 return OutputAccessChain(ctx, ctx.output_f32, ctx.output_position, element_id);
87 } 113 }
114 case IR::Attribute::ColorFrontDiffuseR:
115 case IR::Attribute::ColorFrontDiffuseG:
116 case IR::Attribute::ColorFrontDiffuseB:
117 case IR::Attribute::ColorFrontDiffuseA: {
118 const u32 element{static_cast<u32>(attr) % 4};
119 const Id element_id{ctx.Const(element)};
120 return OutputAccessChain(ctx, ctx.output_f32, ctx.output_front_color, element_id);
121 }
88 case IR::Attribute::ClipDistance0: 122 case IR::Attribute::ClipDistance0:
89 case IR::Attribute::ClipDistance1: 123 case IR::Attribute::ClipDistance1:
90 case IR::Attribute::ClipDistance2: 124 case IR::Attribute::ClipDistance2:
@@ -307,6 +341,12 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
307 const Id value{ctx.OpLoad(type->id, pointer)}; 341 const Id value{ctx.OpLoad(type->id, pointer)};
308 return type->needs_cast ? ctx.OpBitcast(ctx.F32[1], value) : value; 342 return type->needs_cast ? ctx.OpBitcast(ctx.F32[1], value) : value;
309 } 343 }
344 if (IsFixedFncTexture(attr)) {
345 const u32 index{FixedFncTextureAttributeIndex(attr)};
346 const Id attr_id{ctx.input_fixed_fnc_textures[index]};
347 const Id attr_ptr{AttrPointer(ctx, ctx.input_f32, vertex, attr_id, ctx.Const(element))};
348 return ctx.OpLoad(ctx.F32[1], attr_ptr);
349 }
310 switch (attr) { 350 switch (attr) {
311 case IR::Attribute::PrimitiveId: 351 case IR::Attribute::PrimitiveId:
312 return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.primitive_id)); 352 return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.primitive_id));
@@ -316,6 +356,13 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
316 case IR::Attribute::PositionW: 356 case IR::Attribute::PositionW:
317 return ctx.OpLoad(ctx.F32[1], AttrPointer(ctx, ctx.input_f32, vertex, ctx.input_position, 357 return ctx.OpLoad(ctx.F32[1], AttrPointer(ctx, ctx.input_f32, vertex, ctx.input_position,
318 ctx.Const(element))); 358 ctx.Const(element)));
359 case IR::Attribute::ColorFrontDiffuseR:
360 case IR::Attribute::ColorFrontDiffuseG:
361 case IR::Attribute::ColorFrontDiffuseB:
362 case IR::Attribute::ColorFrontDiffuseA: {
363 return ctx.OpLoad(ctx.F32[1], AttrPointer(ctx, ctx.input_f32, vertex, ctx.input_front_color,
364 ctx.Const(element)));
365 }
319 case IR::Attribute::InstanceId: 366 case IR::Attribute::InstanceId:
320 if (ctx.profile.support_vertex_instance_id) { 367 if (ctx.profile.support_vertex_instance_id) {
321 return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.instance_id)); 368 return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.instance_id));
@@ -333,8 +380,9 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
333 return ctx.OpBitcast(ctx.F32[1], ctx.OpISub(ctx.U32[1], index, base)); 380 return ctx.OpBitcast(ctx.F32[1], ctx.OpISub(ctx.U32[1], index, base));
334 } 381 }
335 case IR::Attribute::FrontFace: 382 case IR::Attribute::FrontFace:
336 return ctx.OpSelect(ctx.U32[1], ctx.OpLoad(ctx.U1, ctx.front_face), 383 return ctx.OpSelect(ctx.F32[1], ctx.OpLoad(ctx.U1, ctx.front_face),
337 ctx.Const(std::numeric_limits<u32>::max()), ctx.u32_zero_value); 384 ctx.OpBitcast(ctx.F32[1], ctx.Const(std::numeric_limits<u32>::max())),
385 ctx.f32_zero_value);
338 case IR::Attribute::PointSpriteS: 386 case IR::Attribute::PointSpriteS:
339 return ctx.OpLoad(ctx.F32[1], 387 return ctx.OpLoad(ctx.F32[1],
340 ctx.OpAccessChain(ctx.input_f32, ctx.point_coord, ctx.u32_zero_value)); 388 ctx.OpAccessChain(ctx.input_f32, ctx.point_coord, ctx.u32_zero_value));
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
index 78b1e1ba7..cef52c56e 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
@@ -7,8 +7,13 @@
7 7
8namespace Shader::Backend::SPIRV { 8namespace Shader::Backend::SPIRV {
9namespace { 9namespace {
10Id GetThreadId(EmitContext& ctx) {
11 return ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id);
12}
13
10Id WarpExtract(EmitContext& ctx, Id value) { 14Id WarpExtract(EmitContext& ctx, Id value) {
11 const Id local_index{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)}; 15 const Id thread_id{GetThreadId(ctx)};
16 const Id local_index{ctx.OpShiftRightArithmetic(ctx.U32[1], thread_id, ctx.Const(5U))};
12 return ctx.OpVectorExtractDynamic(ctx.U32[1], value, local_index); 17 return ctx.OpVectorExtractDynamic(ctx.U32[1], value, local_index);
13} 18}
14 19
@@ -48,10 +53,17 @@ Id SelectValue(EmitContext& ctx, Id in_range, Id value, Id src_thread_id) {
48 return ctx.OpSelect(ctx.U32[1], in_range, 53 return ctx.OpSelect(ctx.U32[1], in_range,
49 ctx.OpSubgroupReadInvocationKHR(ctx.U32[1], value, src_thread_id), value); 54 ctx.OpSubgroupReadInvocationKHR(ctx.U32[1], value, src_thread_id), value);
50} 55}
56
57Id GetUpperClamp(EmitContext& ctx, Id invocation_id, Id clamp) {
58 const Id thirty_two{ctx.Const(32u)};
59 const Id is_upper_partition{ctx.OpSGreaterThanEqual(ctx.U1, invocation_id, thirty_two)};
60 const Id upper_clamp{ctx.OpIAdd(ctx.U32[1], thirty_two, clamp)};
61 return ctx.OpSelect(ctx.U32[1], is_upper_partition, upper_clamp, clamp);
62}
51} // Anonymous namespace 63} // Anonymous namespace
52 64
53Id EmitLaneId(EmitContext& ctx) { 65Id EmitLaneId(EmitContext& ctx) {
54 const Id id{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)}; 66 const Id id{GetThreadId(ctx)};
55 if (!ctx.profile.warp_size_potentially_larger_than_guest) { 67 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
56 return id; 68 return id;
57 } 69 }
@@ -123,7 +135,15 @@ Id EmitSubgroupGeMask(EmitContext& ctx) {
123Id EmitShuffleIndex(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, 135Id EmitShuffleIndex(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
124 Id segmentation_mask) { 136 Id segmentation_mask) {
125 const Id not_seg_mask{ctx.OpNot(ctx.U32[1], segmentation_mask)}; 137 const Id not_seg_mask{ctx.OpNot(ctx.U32[1], segmentation_mask)};
126 const Id thread_id{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)}; 138 const Id thread_id{GetThreadId(ctx)};
139 if (ctx.profile.warp_size_potentially_larger_than_guest) {
140 const Id thirty_two{ctx.Const(32u)};
141 const Id is_upper_partition{ctx.OpSGreaterThanEqual(ctx.U1, thread_id, thirty_two)};
142 const Id upper_index{ctx.OpIAdd(ctx.U32[1], thirty_two, index)};
143 const Id upper_clamp{ctx.OpIAdd(ctx.U32[1], thirty_two, clamp)};
144 index = ctx.OpSelect(ctx.U32[1], is_upper_partition, upper_index, index);
145 clamp = ctx.OpSelect(ctx.U32[1], is_upper_partition, upper_clamp, clamp);
146 }
127 const Id min_thread_id{ComputeMinThreadId(ctx, thread_id, segmentation_mask)}; 147 const Id min_thread_id{ComputeMinThreadId(ctx, thread_id, segmentation_mask)};
128 const Id max_thread_id{ComputeMaxThreadId(ctx, min_thread_id, clamp, not_seg_mask)}; 148 const Id max_thread_id{ComputeMaxThreadId(ctx, min_thread_id, clamp, not_seg_mask)};
129 149
@@ -137,7 +157,10 @@ Id EmitShuffleIndex(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id cla
137 157
138Id EmitShuffleUp(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, 158Id EmitShuffleUp(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
139 Id segmentation_mask) { 159 Id segmentation_mask) {
140 const Id thread_id{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)}; 160 const Id thread_id{GetThreadId(ctx)};
161 if (ctx.profile.warp_size_potentially_larger_than_guest) {
162 clamp = GetUpperClamp(ctx, thread_id, clamp);
163 }
141 const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)}; 164 const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)};
142 const Id src_thread_id{ctx.OpISub(ctx.U32[1], thread_id, index)}; 165 const Id src_thread_id{ctx.OpISub(ctx.U32[1], thread_id, index)};
143 const Id in_range{ctx.OpSGreaterThanEqual(ctx.U1, src_thread_id, max_thread_id)}; 166 const Id in_range{ctx.OpSGreaterThanEqual(ctx.U1, src_thread_id, max_thread_id)};
@@ -148,7 +171,10 @@ Id EmitShuffleUp(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
148 171
149Id EmitShuffleDown(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, 172Id EmitShuffleDown(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
150 Id segmentation_mask) { 173 Id segmentation_mask) {
151 const Id thread_id{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)}; 174 const Id thread_id{GetThreadId(ctx)};
175 if (ctx.profile.warp_size_potentially_larger_than_guest) {
176 clamp = GetUpperClamp(ctx, thread_id, clamp);
177 }
152 const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)}; 178 const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)};
153 const Id src_thread_id{ctx.OpIAdd(ctx.U32[1], thread_id, index)}; 179 const Id src_thread_id{ctx.OpIAdd(ctx.U32[1], thread_id, index)};
154 const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)}; 180 const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)};
@@ -159,7 +185,10 @@ Id EmitShuffleDown(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clam
159 185
160Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, 186Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
161 Id segmentation_mask) { 187 Id segmentation_mask) {
162 const Id thread_id{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)}; 188 const Id thread_id{GetThreadId(ctx)};
189 if (ctx.profile.warp_size_potentially_larger_than_guest) {
190 clamp = GetUpperClamp(ctx, thread_id, clamp);
191 }
163 const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)}; 192 const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)};
164 const Id src_thread_id{ctx.OpBitwiseXor(ctx.U32[1], thread_id, index)}; 193 const Id src_thread_id{ctx.OpBitwiseXor(ctx.U32[1], thread_id, index)};
165 const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)}; 194 const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)};
diff --git a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
index 8b3e0a15c..69eeaa3e6 100644
--- a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
+++ b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
@@ -20,6 +20,7 @@
20#include "shader_recompiler/frontend/maxwell/decode.h" 20#include "shader_recompiler/frontend/maxwell/decode.h"
21#include "shader_recompiler/frontend/maxwell/structured_control_flow.h" 21#include "shader_recompiler/frontend/maxwell/structured_control_flow.h"
22#include "shader_recompiler/frontend/maxwell/translate/translate.h" 22#include "shader_recompiler/frontend/maxwell/translate/translate.h"
23#include "shader_recompiler/host_translate_info.h"
23#include "shader_recompiler/object_pool.h" 24#include "shader_recompiler/object_pool.h"
24 25
25namespace Shader::Maxwell { 26namespace Shader::Maxwell {
@@ -652,7 +653,7 @@ class TranslatePass {
652public: 653public:
653 TranslatePass(ObjectPool<IR::Inst>& inst_pool_, ObjectPool<IR::Block>& block_pool_, 654 TranslatePass(ObjectPool<IR::Inst>& inst_pool_, ObjectPool<IR::Block>& block_pool_,
654 ObjectPool<Statement>& stmt_pool_, Environment& env_, Statement& root_stmt, 655 ObjectPool<Statement>& stmt_pool_, Environment& env_, Statement& root_stmt,
655 IR::AbstractSyntaxList& syntax_list_) 656 IR::AbstractSyntaxList& syntax_list_, const HostTranslateInfo& host_info)
656 : stmt_pool{stmt_pool_}, inst_pool{inst_pool_}, block_pool{block_pool_}, env{env_}, 657 : stmt_pool{stmt_pool_}, inst_pool{inst_pool_}, block_pool{block_pool_}, env{env_},
657 syntax_list{syntax_list_} { 658 syntax_list{syntax_list_} {
658 Visit(root_stmt, nullptr, nullptr); 659 Visit(root_stmt, nullptr, nullptr);
@@ -660,6 +661,9 @@ public:
660 IR::Block& first_block{*syntax_list.front().data.block}; 661 IR::Block& first_block{*syntax_list.front().data.block};
661 IR::IREmitter ir(first_block, first_block.begin()); 662 IR::IREmitter ir(first_block, first_block.begin());
662 ir.Prologue(); 663 ir.Prologue();
664 if (uses_demote_to_helper && host_info.needs_demote_reorder) {
665 DemoteCombinationPass();
666 }
663 } 667 }
664 668
665private: 669private:
@@ -809,7 +813,14 @@ private:
809 } 813 }
810 case StatementType::Return: { 814 case StatementType::Return: {
811 ensure_block(); 815 ensure_block();
812 IR::IREmitter{*current_block}.Epilogue(); 816 IR::Block* return_block{block_pool.Create(inst_pool)};
817 IR::IREmitter{*return_block}.Epilogue();
818 current_block->AddBranch(return_block);
819
820 auto& merge{syntax_list.emplace_back()};
821 merge.type = IR::AbstractSyntaxNode::Type::Block;
822 merge.data.block = return_block;
823
813 current_block = nullptr; 824 current_block = nullptr;
814 syntax_list.emplace_back().type = IR::AbstractSyntaxNode::Type::Return; 825 syntax_list.emplace_back().type = IR::AbstractSyntaxNode::Type::Return;
815 break; 826 break;
@@ -824,6 +835,7 @@ private:
824 auto& merge{syntax_list.emplace_back()}; 835 auto& merge{syntax_list.emplace_back()};
825 merge.type = IR::AbstractSyntaxNode::Type::Block; 836 merge.type = IR::AbstractSyntaxNode::Type::Block;
826 merge.data.block = demote_block; 837 merge.data.block = demote_block;
838 uses_demote_to_helper = true;
827 break; 839 break;
828 } 840 }
829 case StatementType::Unreachable: { 841 case StatementType::Unreachable: {
@@ -855,11 +867,117 @@ private:
855 return block_pool.Create(inst_pool); 867 return block_pool.Create(inst_pool);
856 } 868 }
857 869
870 void DemoteCombinationPass() {
871 using Type = IR::AbstractSyntaxNode::Type;
872 std::vector<IR::Block*> demote_blocks;
873 std::vector<IR::U1> demote_conds;
874 u32 num_epilogues{};
875 u32 branch_depth{};
876 for (const IR::AbstractSyntaxNode& node : syntax_list) {
877 if (node.type == Type::If) {
878 ++branch_depth;
879 }
880 if (node.type == Type::EndIf) {
881 --branch_depth;
882 }
883 if (node.type != Type::Block) {
884 continue;
885 }
886 if (branch_depth > 1) {
887 // Skip reordering nested demote branches.
888 continue;
889 }
890 for (const IR::Inst& inst : node.data.block->Instructions()) {
891 const IR::Opcode op{inst.GetOpcode()};
892 if (op == IR::Opcode::DemoteToHelperInvocation) {
893 demote_blocks.push_back(node.data.block);
894 break;
895 }
896 if (op == IR::Opcode::Epilogue) {
897 ++num_epilogues;
898 }
899 }
900 }
901 if (demote_blocks.size() == 0) {
902 return;
903 }
904 if (num_epilogues > 1) {
905 LOG_DEBUG(Shader, "Combining demotes with more than one return is not implemented.");
906 return;
907 }
908 s64 last_iterator_offset{};
909 auto& asl{syntax_list};
910 for (const IR::Block* demote_block : demote_blocks) {
911 const auto start_it{asl.begin() + last_iterator_offset};
912 auto asl_it{std::find_if(start_it, asl.end(), [&](const IR::AbstractSyntaxNode& asn) {
913 return asn.type == Type::If && asn.data.if_node.body == demote_block;
914 })};
915 if (asl_it == asl.end()) {
916 // Demote without a conditional branch.
917 // No need to proceed since all fragment instances will be demoted regardless.
918 return;
919 }
920 const IR::Block* const end_if = asl_it->data.if_node.merge;
921 demote_conds.push_back(asl_it->data.if_node.cond);
922 last_iterator_offset = std::distance(asl.begin(), asl_it);
923
924 asl_it = asl.erase(asl_it);
925 asl_it = std::find_if(asl_it, asl.end(), [&](const IR::AbstractSyntaxNode& asn) {
926 return asn.type == Type::Block && asn.data.block == demote_block;
927 });
928
929 asl_it = asl.erase(asl_it);
930 asl_it = std::find_if(asl_it, asl.end(), [&](const IR::AbstractSyntaxNode& asn) {
931 return asn.type == Type::EndIf && asn.data.end_if.merge == end_if;
932 });
933 asl_it = asl.erase(asl_it);
934 }
935 const auto epilogue_func{[](const IR::AbstractSyntaxNode& asn) {
936 if (asn.type != Type::Block) {
937 return false;
938 }
939 for (const auto& inst : asn.data.block->Instructions()) {
940 if (inst.GetOpcode() == IR::Opcode::Epilogue) {
941 return true;
942 }
943 }
944 return false;
945 }};
946 const auto reverse_it{std::find_if(asl.rbegin(), asl.rend(), epilogue_func)};
947 const auto return_block_it{(reverse_it + 1).base()};
948
949 IR::IREmitter ir{*(return_block_it - 1)->data.block};
950 IR::U1 cond(IR::Value(false));
951 for (const auto& demote_cond : demote_conds) {
952 cond = ir.LogicalOr(cond, demote_cond);
953 }
954 cond.Inst()->DestructiveAddUsage(1);
955
956 IR::AbstractSyntaxNode demote_if_node{};
957 demote_if_node.type = Type::If;
958 demote_if_node.data.if_node.cond = cond;
959 demote_if_node.data.if_node.body = demote_blocks[0];
960 demote_if_node.data.if_node.merge = return_block_it->data.block;
961
962 IR::AbstractSyntaxNode demote_node{};
963 demote_node.type = Type::Block;
964 demote_node.data.block = demote_blocks[0];
965
966 IR::AbstractSyntaxNode demote_endif_node{};
967 demote_endif_node.type = Type::EndIf;
968 demote_endif_node.data.end_if.merge = return_block_it->data.block;
969
970 asl.insert(return_block_it, demote_endif_node);
971 asl.insert(return_block_it, demote_node);
972 asl.insert(return_block_it, demote_if_node);
973 }
974
858 ObjectPool<Statement>& stmt_pool; 975 ObjectPool<Statement>& stmt_pool;
859 ObjectPool<IR::Inst>& inst_pool; 976 ObjectPool<IR::Inst>& inst_pool;
860 ObjectPool<IR::Block>& block_pool; 977 ObjectPool<IR::Block>& block_pool;
861 Environment& env; 978 Environment& env;
862 IR::AbstractSyntaxList& syntax_list; 979 IR::AbstractSyntaxList& syntax_list;
980 bool uses_demote_to_helper{};
863 981
864// TODO: C++20 Remove this when all compilers support constexpr std::vector 982// TODO: C++20 Remove this when all compilers support constexpr std::vector
865#if __cpp_lib_constexpr_vector >= 201907 983#if __cpp_lib_constexpr_vector >= 201907
@@ -871,12 +989,13 @@ private:
871} // Anonymous namespace 989} // Anonymous namespace
872 990
873IR::AbstractSyntaxList BuildASL(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool, 991IR::AbstractSyntaxList BuildASL(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
874 Environment& env, Flow::CFG& cfg) { 992 Environment& env, Flow::CFG& cfg,
993 const HostTranslateInfo& host_info) {
875 ObjectPool<Statement> stmt_pool{64}; 994 ObjectPool<Statement> stmt_pool{64};
876 GotoPass goto_pass{cfg, stmt_pool}; 995 GotoPass goto_pass{cfg, stmt_pool};
877 Statement& root{goto_pass.RootStatement()}; 996 Statement& root{goto_pass.RootStatement()};
878 IR::AbstractSyntaxList syntax_list; 997 IR::AbstractSyntaxList syntax_list;
879 TranslatePass{inst_pool, block_pool, stmt_pool, env, root, syntax_list}; 998 TranslatePass{inst_pool, block_pool, stmt_pool, env, root, syntax_list, host_info};
880 return syntax_list; 999 return syntax_list;
881} 1000}
882 1001
diff --git a/src/shader_recompiler/frontend/maxwell/structured_control_flow.h b/src/shader_recompiler/frontend/maxwell/structured_control_flow.h
index 88b083649..e38158da3 100644
--- a/src/shader_recompiler/frontend/maxwell/structured_control_flow.h
+++ b/src/shader_recompiler/frontend/maxwell/structured_control_flow.h
@@ -11,10 +11,13 @@
11#include "shader_recompiler/frontend/maxwell/control_flow.h" 11#include "shader_recompiler/frontend/maxwell/control_flow.h"
12#include "shader_recompiler/object_pool.h" 12#include "shader_recompiler/object_pool.h"
13 13
14namespace Shader::Maxwell { 14namespace Shader {
15struct HostTranslateInfo;
16namespace Maxwell {
15 17
16[[nodiscard]] IR::AbstractSyntaxList BuildASL(ObjectPool<IR::Inst>& inst_pool, 18[[nodiscard]] IR::AbstractSyntaxList BuildASL(ObjectPool<IR::Inst>& inst_pool,
17 ObjectPool<IR::Block>& block_pool, Environment& env, 19 ObjectPool<IR::Block>& block_pool, Environment& env,
18 Flow::CFG& cfg); 20 Flow::CFG& cfg, const HostTranslateInfo& host_info);
19 21
20} // namespace Shader::Maxwell 22} // namespace Maxwell
23} // namespace Shader
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
index c067d459c..012d55357 100644
--- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
@@ -130,7 +130,7 @@ void AddNVNStorageBuffers(IR::Program& program) {
130IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool, 130IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
131 Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info) { 131 Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info) {
132 IR::Program program; 132 IR::Program program;
133 program.syntax_list = BuildASL(inst_pool, block_pool, env, cfg); 133 program.syntax_list = BuildASL(inst_pool, block_pool, env, cfg, host_info);
134 program.blocks = GenerateBlocks(program.syntax_list); 134 program.blocks = GenerateBlocks(program.syntax_list);
135 program.post_order_blocks = PostOrder(program.syntax_list.front()); 135 program.post_order_blocks = PostOrder(program.syntax_list.front());
136 program.stage = env.ShaderStage(); 136 program.stage = env.ShaderStage();
diff --git a/src/shader_recompiler/host_translate_info.h b/src/shader_recompiler/host_translate_info.h
index 94a584219..96468b2e7 100644
--- a/src/shader_recompiler/host_translate_info.h
+++ b/src/shader_recompiler/host_translate_info.h
@@ -11,8 +11,9 @@ namespace Shader {
11 11
12/// Misc information about the host 12/// Misc information about the host
13struct HostTranslateInfo { 13struct HostTranslateInfo {
14 bool support_float16{}; ///< True when the device supports 16-bit floats 14 bool support_float16{}; ///< True when the device supports 16-bit floats
15 bool support_int64{}; ///< True when the device supports 64-bit integers 15 bool support_int64{}; ///< True when the device supports 64-bit integers
16 bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered
16}; 17};
17 18
18} // namespace Shader 19} // namespace Shader
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 2f6cdd216..269db21a5 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -231,6 +231,7 @@ endif()
231 231
232target_include_directories(video_core PRIVATE ${FFmpeg_INCLUDE_DIR}) 232target_include_directories(video_core PRIVATE ${FFmpeg_INCLUDE_DIR})
233target_link_libraries(video_core PRIVATE ${FFmpeg_LIBRARIES}) 233target_link_libraries(video_core PRIVATE ${FFmpeg_LIBRARIES})
234target_link_options(video_core PRIVATE ${FFmpeg_LDFLAGS})
234 235
235add_dependencies(video_core host_shaders) 236add_dependencies(video_core host_shaders)
236target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE}) 237target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE})
diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h
index c3318095c..be2113f5a 100644
--- a/src/video_core/buffer_cache/buffer_base.h
+++ b/src/video_core/buffer_cache/buffer_base.h
@@ -261,16 +261,6 @@ public:
261 stream_score += score; 261 stream_score += score;
262 } 262 }
263 263
264 /// Sets the new frame tick
265 void SetFrameTick(u64 new_frame_tick) noexcept {
266 frame_tick = new_frame_tick;
267 }
268
269 /// Returns the new frame tick
270 [[nodiscard]] u64 FrameTick() const noexcept {
271 return frame_tick;
272 }
273
274 /// Returns the likeliness of this being a stream buffer 264 /// Returns the likeliness of this being a stream buffer
275 [[nodiscard]] int StreamScore() const noexcept { 265 [[nodiscard]] int StreamScore() const noexcept {
276 return stream_score; 266 return stream_score;
@@ -307,6 +297,14 @@ public:
307 return words.size_bytes; 297 return words.size_bytes;
308 } 298 }
309 299
300 size_t getLRUID() const noexcept {
301 return lru_id;
302 }
303
304 void setLRUID(size_t lru_id_) {
305 lru_id = lru_id_;
306 }
307
310private: 308private:
311 template <Type type> 309 template <Type type>
312 u64* Array() noexcept { 310 u64* Array() noexcept {
@@ -603,9 +601,9 @@ private:
603 RasterizerInterface* rasterizer = nullptr; 601 RasterizerInterface* rasterizer = nullptr;
604 VAddr cpu_addr = 0; 602 VAddr cpu_addr = 0;
605 Words words; 603 Words words;
606 u64 frame_tick = 0;
607 BufferFlagBits flags{}; 604 BufferFlagBits flags{};
608 int stream_score = 0; 605 int stream_score = 0;
606 size_t lru_id = SIZE_MAX;
609}; 607};
610 608
611} // namespace VideoCommon 609} // namespace VideoCommon
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 3b43554f9..7bfd57369 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -20,6 +20,7 @@
20#include "common/common_types.h" 20#include "common/common_types.h"
21#include "common/div_ceil.h" 21#include "common/div_ceil.h"
22#include "common/literals.h" 22#include "common/literals.h"
23#include "common/lru_cache.h"
23#include "common/microprofile.h" 24#include "common/microprofile.h"
24#include "common/scope_exit.h" 25#include "common/scope_exit.h"
25#include "common/settings.h" 26#include "common/settings.h"
@@ -330,7 +331,7 @@ private:
330 template <bool insert> 331 template <bool insert>
331 void ChangeRegister(BufferId buffer_id); 332 void ChangeRegister(BufferId buffer_id);
332 333
333 void TouchBuffer(Buffer& buffer) const noexcept; 334 void TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept;
334 335
335 bool SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size); 336 bool SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size);
336 337
@@ -428,7 +429,11 @@ private:
428 size_t immediate_buffer_capacity = 0; 429 size_t immediate_buffer_capacity = 0;
429 std::unique_ptr<u8[]> immediate_buffer_alloc; 430 std::unique_ptr<u8[]> immediate_buffer_alloc;
430 431
431 typename SlotVector<Buffer>::Iterator deletion_iterator; 432 struct LRUItemParams {
433 using ObjectType = BufferId;
434 using TickType = u64;
435 };
436 Common::LeastRecentlyUsedCache<LRUItemParams> lru_cache;
432 u64 frame_tick = 0; 437 u64 frame_tick = 0;
433 u64 total_used_memory = 0; 438 u64 total_used_memory = 0;
434 439
@@ -445,7 +450,6 @@ BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_,
445 kepler_compute{kepler_compute_}, gpu_memory{gpu_memory_}, cpu_memory{cpu_memory_} { 450 kepler_compute{kepler_compute_}, gpu_memory{gpu_memory_}, cpu_memory{cpu_memory_} {
446 // Ensure the first slot is used for the null buffer 451 // Ensure the first slot is used for the null buffer
447 void(slot_buffers.insert(runtime, NullBufferParams{})); 452 void(slot_buffers.insert(runtime, NullBufferParams{}));
448 deletion_iterator = slot_buffers.end();
449 common_ranges.clear(); 453 common_ranges.clear();
450} 454}
451 455
@@ -454,20 +458,17 @@ void BufferCache<P>::RunGarbageCollector() {
454 const bool aggressive_gc = total_used_memory >= CRITICAL_MEMORY; 458 const bool aggressive_gc = total_used_memory >= CRITICAL_MEMORY;
455 const u64 ticks_to_destroy = aggressive_gc ? 60 : 120; 459 const u64 ticks_to_destroy = aggressive_gc ? 60 : 120;
456 int num_iterations = aggressive_gc ? 64 : 32; 460 int num_iterations = aggressive_gc ? 64 : 32;
457 for (; num_iterations > 0; --num_iterations) { 461 const auto clean_up = [this, &num_iterations](BufferId buffer_id) {
458 if (deletion_iterator == slot_buffers.end()) { 462 if (num_iterations == 0) {
459 deletion_iterator = slot_buffers.begin(); 463 return true;
460 }
461 ++deletion_iterator;
462 if (deletion_iterator == slot_buffers.end()) {
463 break;
464 }
465 const auto [buffer_id, buffer] = *deletion_iterator;
466 if (buffer->FrameTick() + ticks_to_destroy < frame_tick) {
467 DownloadBufferMemory(*buffer);
468 DeleteBuffer(buffer_id);
469 } 464 }
470 } 465 --num_iterations;
466 auto& buffer = slot_buffers[buffer_id];
467 DownloadBufferMemory(buffer);
468 DeleteBuffer(buffer_id);
469 return false;
470 };
471 lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, clean_up);
471} 472}
472 473
473template <class P> 474template <class P>
@@ -485,7 +486,7 @@ void BufferCache<P>::TickFrame() {
485 const bool skip_preferred = hits * 256 < shots * 251; 486 const bool skip_preferred = hits * 256 < shots * 251;
486 uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0; 487 uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0;
487 488
488 if (Settings::values.use_caches_gc.GetValue() && total_used_memory >= EXPECTED_MEMORY) { 489 if (total_used_memory >= EXPECTED_MEMORY) {
489 RunGarbageCollector(); 490 RunGarbageCollector();
490 } 491 }
491 ++frame_tick; 492 ++frame_tick;
@@ -954,7 +955,7 @@ bool BufferCache<P>::IsRegionCpuModified(VAddr addr, size_t size) {
954template <class P> 955template <class P>
955void BufferCache<P>::BindHostIndexBuffer() { 956void BufferCache<P>::BindHostIndexBuffer() {
956 Buffer& buffer = slot_buffers[index_buffer.buffer_id]; 957 Buffer& buffer = slot_buffers[index_buffer.buffer_id];
957 TouchBuffer(buffer); 958 TouchBuffer(buffer, index_buffer.buffer_id);
958 const u32 offset = buffer.Offset(index_buffer.cpu_addr); 959 const u32 offset = buffer.Offset(index_buffer.cpu_addr);
959 const u32 size = index_buffer.size; 960 const u32 size = index_buffer.size;
960 SynchronizeBuffer(buffer, index_buffer.cpu_addr, size); 961 SynchronizeBuffer(buffer, index_buffer.cpu_addr, size);
@@ -975,7 +976,7 @@ void BufferCache<P>::BindHostVertexBuffers() {
975 for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) { 976 for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) {
976 const Binding& binding = vertex_buffers[index]; 977 const Binding& binding = vertex_buffers[index];
977 Buffer& buffer = slot_buffers[binding.buffer_id]; 978 Buffer& buffer = slot_buffers[binding.buffer_id];
978 TouchBuffer(buffer); 979 TouchBuffer(buffer, binding.buffer_id);
979 SynchronizeBuffer(buffer, binding.cpu_addr, binding.size); 980 SynchronizeBuffer(buffer, binding.cpu_addr, binding.size);
980 if (!flags[Dirty::VertexBuffer0 + index]) { 981 if (!flags[Dirty::VertexBuffer0 + index]) {
981 continue; 982 continue;
@@ -1011,7 +1012,7 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
1011 const VAddr cpu_addr = binding.cpu_addr; 1012 const VAddr cpu_addr = binding.cpu_addr;
1012 const u32 size = std::min(binding.size, (*uniform_buffer_sizes)[stage][index]); 1013 const u32 size = std::min(binding.size, (*uniform_buffer_sizes)[stage][index]);
1013 Buffer& buffer = slot_buffers[binding.buffer_id]; 1014 Buffer& buffer = slot_buffers[binding.buffer_id];
1014 TouchBuffer(buffer); 1015 TouchBuffer(buffer, binding.buffer_id);
1015 const bool use_fast_buffer = binding.buffer_id != NULL_BUFFER_ID && 1016 const bool use_fast_buffer = binding.buffer_id != NULL_BUFFER_ID &&
1016 size <= uniform_buffer_skip_cache_size && 1017 size <= uniform_buffer_skip_cache_size &&
1017 !buffer.IsRegionGpuModified(cpu_addr, size); 1018 !buffer.IsRegionGpuModified(cpu_addr, size);
@@ -1083,7 +1084,7 @@ void BufferCache<P>::BindHostGraphicsStorageBuffers(size_t stage) {
1083 ForEachEnabledBit(enabled_storage_buffers[stage], [&](u32 index) { 1084 ForEachEnabledBit(enabled_storage_buffers[stage], [&](u32 index) {
1084 const Binding& binding = storage_buffers[stage][index]; 1085 const Binding& binding = storage_buffers[stage][index];
1085 Buffer& buffer = slot_buffers[binding.buffer_id]; 1086 Buffer& buffer = slot_buffers[binding.buffer_id];
1086 TouchBuffer(buffer); 1087 TouchBuffer(buffer, binding.buffer_id);
1087 const u32 size = binding.size; 1088 const u32 size = binding.size;
1088 SynchronizeBuffer(buffer, binding.cpu_addr, size); 1089 SynchronizeBuffer(buffer, binding.cpu_addr, size);
1089 1090
@@ -1128,7 +1129,7 @@ void BufferCache<P>::BindHostTransformFeedbackBuffers() {
1128 for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) { 1129 for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) {
1129 const Binding& binding = transform_feedback_buffers[index]; 1130 const Binding& binding = transform_feedback_buffers[index];
1130 Buffer& buffer = slot_buffers[binding.buffer_id]; 1131 Buffer& buffer = slot_buffers[binding.buffer_id];
1131 TouchBuffer(buffer); 1132 TouchBuffer(buffer, binding.buffer_id);
1132 const u32 size = binding.size; 1133 const u32 size = binding.size;
1133 SynchronizeBuffer(buffer, binding.cpu_addr, size); 1134 SynchronizeBuffer(buffer, binding.cpu_addr, size);
1134 1135
@@ -1148,7 +1149,7 @@ void BufferCache<P>::BindHostComputeUniformBuffers() {
1148 ForEachEnabledBit(enabled_compute_uniform_buffer_mask, [&](u32 index) { 1149 ForEachEnabledBit(enabled_compute_uniform_buffer_mask, [&](u32 index) {
1149 const Binding& binding = compute_uniform_buffers[index]; 1150 const Binding& binding = compute_uniform_buffers[index];
1150 Buffer& buffer = slot_buffers[binding.buffer_id]; 1151 Buffer& buffer = slot_buffers[binding.buffer_id];
1151 TouchBuffer(buffer); 1152 TouchBuffer(buffer, binding.buffer_id);
1152 const u32 size = std::min(binding.size, (*compute_uniform_buffer_sizes)[index]); 1153 const u32 size = std::min(binding.size, (*compute_uniform_buffer_sizes)[index]);
1153 SynchronizeBuffer(buffer, binding.cpu_addr, size); 1154 SynchronizeBuffer(buffer, binding.cpu_addr, size);
1154 1155
@@ -1168,7 +1169,7 @@ void BufferCache<P>::BindHostComputeStorageBuffers() {
1168 ForEachEnabledBit(enabled_compute_storage_buffers, [&](u32 index) { 1169 ForEachEnabledBit(enabled_compute_storage_buffers, [&](u32 index) {
1169 const Binding& binding = compute_storage_buffers[index]; 1170 const Binding& binding = compute_storage_buffers[index];
1170 Buffer& buffer = slot_buffers[binding.buffer_id]; 1171 Buffer& buffer = slot_buffers[binding.buffer_id];
1171 TouchBuffer(buffer); 1172 TouchBuffer(buffer, binding.buffer_id);
1172 const u32 size = binding.size; 1173 const u32 size = binding.size;
1173 SynchronizeBuffer(buffer, binding.cpu_addr, size); 1174 SynchronizeBuffer(buffer, binding.cpu_addr, size);
1174 1175
@@ -1513,11 +1514,11 @@ BufferId BufferCache<P>::CreateBuffer(VAddr cpu_addr, u32 wanted_size) {
1513 const OverlapResult overlap = ResolveOverlaps(cpu_addr, wanted_size); 1514 const OverlapResult overlap = ResolveOverlaps(cpu_addr, wanted_size);
1514 const u32 size = static_cast<u32>(overlap.end - overlap.begin); 1515 const u32 size = static_cast<u32>(overlap.end - overlap.begin);
1515 const BufferId new_buffer_id = slot_buffers.insert(runtime, rasterizer, overlap.begin, size); 1516 const BufferId new_buffer_id = slot_buffers.insert(runtime, rasterizer, overlap.begin, size);
1516 TouchBuffer(slot_buffers[new_buffer_id]);
1517 for (const BufferId overlap_id : overlap.ids) { 1517 for (const BufferId overlap_id : overlap.ids) {
1518 JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap); 1518 JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap);
1519 } 1519 }
1520 Register(new_buffer_id); 1520 Register(new_buffer_id);
1521 TouchBuffer(slot_buffers[new_buffer_id], new_buffer_id);
1521 return new_buffer_id; 1522 return new_buffer_id;
1522} 1523}
1523 1524
@@ -1534,12 +1535,14 @@ void BufferCache<P>::Unregister(BufferId buffer_id) {
1534template <class P> 1535template <class P>
1535template <bool insert> 1536template <bool insert>
1536void BufferCache<P>::ChangeRegister(BufferId buffer_id) { 1537void BufferCache<P>::ChangeRegister(BufferId buffer_id) {
1537 const Buffer& buffer = slot_buffers[buffer_id]; 1538 Buffer& buffer = slot_buffers[buffer_id];
1538 const auto size = buffer.SizeBytes(); 1539 const auto size = buffer.SizeBytes();
1539 if (insert) { 1540 if (insert) {
1540 total_used_memory += Common::AlignUp(size, 1024); 1541 total_used_memory += Common::AlignUp(size, 1024);
1542 buffer.setLRUID(lru_cache.Insert(buffer_id, frame_tick));
1541 } else { 1543 } else {
1542 total_used_memory -= Common::AlignUp(size, 1024); 1544 total_used_memory -= Common::AlignUp(size, 1024);
1545 lru_cache.Free(buffer.getLRUID());
1543 } 1546 }
1544 const VAddr cpu_addr_begin = buffer.CpuAddr(); 1547 const VAddr cpu_addr_begin = buffer.CpuAddr();
1545 const VAddr cpu_addr_end = cpu_addr_begin + size; 1548 const VAddr cpu_addr_end = cpu_addr_begin + size;
@@ -1555,8 +1558,10 @@ void BufferCache<P>::ChangeRegister(BufferId buffer_id) {
1555} 1558}
1556 1559
1557template <class P> 1560template <class P>
1558void BufferCache<P>::TouchBuffer(Buffer& buffer) const noexcept { 1561void BufferCache<P>::TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept {
1559 buffer.SetFrameTick(frame_tick); 1562 if (buffer_id != NULL_BUFFER_ID) {
1563 lru_cache.Touch(buffer.getLRUID(), frame_tick);
1564 }
1560} 1565}
1561 1566
1562template <class P> 1567template <class P>
diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp
index f798a0053..61966cbfe 100644
--- a/src/video_core/command_classes/codecs/codec.cpp
+++ b/src/video_core/command_classes/codecs/codec.cpp
@@ -5,6 +5,7 @@
5#include <fstream> 5#include <fstream>
6#include <vector> 6#include <vector>
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/settings.h"
8#include "video_core/command_classes/codecs/codec.h" 9#include "video_core/command_classes/codecs/codec.h"
9#include "video_core/command_classes/codecs/h264.h" 10#include "video_core/command_classes/codecs/h264.h"
10#include "video_core/command_classes/codecs/vp9.h" 11#include "video_core/command_classes/codecs/vp9.h"
@@ -16,108 +17,146 @@ extern "C" {
16} 17}
17 18
18namespace Tegra { 19namespace Tegra {
19#if defined(LIBVA_FOUND)
20// Hardware acceleration code from FFmpeg/doc/examples/hw_decode.c originally under MIT license
21namespace { 20namespace {
22constexpr std::array<const char*, 2> VAAPI_DRIVERS = { 21constexpr AVPixelFormat PREFERRED_GPU_FMT = AV_PIX_FMT_NV12;
23 "i915", 22constexpr AVPixelFormat PREFERRED_CPU_FMT = AV_PIX_FMT_YUV420P;
24 "amdgpu", 23
25}; 24void AVPacketDeleter(AVPacket* ptr) {
25 av_packet_free(&ptr);
26}
26 27
27AVPixelFormat GetHwFormat(AVCodecContext*, const AVPixelFormat* pix_fmts) { 28using AVPacketPtr = std::unique_ptr<AVPacket, decltype(&AVPacketDeleter)>;
29
30AVPixelFormat GetGpuFormat(AVCodecContext* av_codec_ctx, const AVPixelFormat* pix_fmts) {
28 for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) { 31 for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) {
29 if (*p == AV_PIX_FMT_VAAPI) { 32 if (*p == av_codec_ctx->pix_fmt) {
30 return AV_PIX_FMT_VAAPI; 33 return av_codec_ctx->pix_fmt;
31 } 34 }
32 } 35 }
33 LOG_INFO(Service_NVDRV, "Could not find compatible GPU AV format, falling back to CPU"); 36 LOG_INFO(Service_NVDRV, "Could not find compatible GPU AV format, falling back to CPU");
34 return *pix_fmts; 37 av_buffer_unref(&av_codec_ctx->hw_device_ctx);
38 av_codec_ctx->pix_fmt = PREFERRED_CPU_FMT;
39 return PREFERRED_CPU_FMT;
40}
41} // namespace
42
43void AVFrameDeleter(AVFrame* ptr) {
44 av_frame_free(&ptr);
35} 45}
36 46
37bool CreateVaapiHwdevice(AVBufferRef** av_hw_device) { 47Codec::Codec(GPU& gpu_, const NvdecCommon::NvdecRegisters& regs)
48 : gpu(gpu_), state{regs}, h264_decoder(std::make_unique<Decoder::H264>(gpu)),
49 vp9_decoder(std::make_unique<Decoder::VP9>(gpu)) {}
50
51Codec::~Codec() {
52 if (!initialized) {
53 return;
54 }
55 // Free libav memory
56 avcodec_free_context(&av_codec_ctx);
57 av_buffer_unref(&av_gpu_decoder);
58}
59
60bool Codec::CreateGpuAvDevice() {
61#if defined(LIBVA_FOUND)
62 static constexpr std::array<const char*, 3> VAAPI_DRIVERS = {
63 "i915",
64 "iHD",
65 "amdgpu",
66 };
38 AVDictionary* hwdevice_options = nullptr; 67 AVDictionary* hwdevice_options = nullptr;
39 av_dict_set(&hwdevice_options, "connection_type", "drm", 0); 68 av_dict_set(&hwdevice_options, "connection_type", "drm", 0);
40 for (const auto& driver : VAAPI_DRIVERS) { 69 for (const auto& driver : VAAPI_DRIVERS) {
41 av_dict_set(&hwdevice_options, "kernel_driver", driver, 0); 70 av_dict_set(&hwdevice_options, "kernel_driver", driver, 0);
42 const int hwdevice_error = av_hwdevice_ctx_create(av_hw_device, AV_HWDEVICE_TYPE_VAAPI, 71 const int hwdevice_error = av_hwdevice_ctx_create(&av_gpu_decoder, AV_HWDEVICE_TYPE_VAAPI,
43 nullptr, hwdevice_options, 0); 72 nullptr, hwdevice_options, 0);
44 if (hwdevice_error >= 0) { 73 if (hwdevice_error >= 0) {
45 LOG_INFO(Service_NVDRV, "Using VA-API with {}", driver); 74 LOG_INFO(Service_NVDRV, "Using VA-API with {}", driver);
46 av_dict_free(&hwdevice_options); 75 av_dict_free(&hwdevice_options);
76 av_codec_ctx->pix_fmt = AV_PIX_FMT_VAAPI;
47 return true; 77 return true;
48 } 78 }
49 LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed {}", hwdevice_error); 79 LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed {}", hwdevice_error);
50 } 80 }
51 LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed for all drivers"); 81 LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed for all drivers");
52 av_dict_free(&hwdevice_options); 82 av_dict_free(&hwdevice_options);
53 return false;
54}
55} // namespace
56#endif 83#endif
57 84 static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX;
58void AVFrameDeleter(AVFrame* ptr) { 85 static constexpr std::array GPU_DECODER_TYPES{
59 av_frame_free(&ptr); 86 AV_HWDEVICE_TYPE_CUDA,
87#ifdef _WIN32
88 AV_HWDEVICE_TYPE_D3D11VA,
89#else
90 AV_HWDEVICE_TYPE_VDPAU,
91#endif
92 };
93 for (const auto& type : GPU_DECODER_TYPES) {
94 const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0);
95 if (hwdevice_res < 0) {
96 LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}",
97 av_hwdevice_get_type_name(type), hwdevice_res);
98 continue;
99 }
100 for (int i = 0;; i++) {
101 const AVCodecHWConfig* config = avcodec_get_hw_config(av_codec, i);
102 if (!config) {
103 LOG_DEBUG(Service_NVDRV, "{} decoder does not support device type {}.",
104 av_codec->name, av_hwdevice_get_type_name(type));
105 break;
106 }
107 if (config->methods & HW_CONFIG_METHOD && config->device_type == type) {
108 av_codec_ctx->pix_fmt = config->pix_fmt;
109 LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type));
110 return true;
111 }
112 }
113 }
114 return false;
60} 115}
61 116
62Codec::Codec(GPU& gpu_, const NvdecCommon::NvdecRegisters& regs) 117void Codec::InitializeAvCodecContext() {
63 : gpu(gpu_), state{regs}, h264_decoder(std::make_unique<Decoder::H264>(gpu)), 118 av_codec_ctx = avcodec_alloc_context3(av_codec);
64 vp9_decoder(std::make_unique<Decoder::VP9>(gpu)) {} 119 av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0);
65
66Codec::~Codec() {
67 if (!initialized) {
68 return;
69 }
70 // Free libav memory
71 avcodec_send_packet(av_codec_ctx, nullptr);
72 AVFrame* av_frame = av_frame_alloc();
73 avcodec_receive_frame(av_codec_ctx, av_frame);
74 avcodec_flush_buffers(av_codec_ctx);
75 av_frame_free(&av_frame);
76 avcodec_close(av_codec_ctx);
77 av_buffer_unref(&av_hw_device);
78} 120}
79 121
80void Codec::InitializeHwdec() { 122void Codec::InitializeGpuDecoder() {
81 // Prioritize integrated GPU to mitigate bandwidth bottlenecks 123 if (!CreateGpuAvDevice()) {
82#if defined(LIBVA_FOUND) 124 av_buffer_unref(&av_gpu_decoder);
83 if (CreateVaapiHwdevice(&av_hw_device)) {
84 const auto hw_device_ctx = av_buffer_ref(av_hw_device);
85 ASSERT_MSG(hw_device_ctx, "av_buffer_ref failed");
86 av_codec_ctx->hw_device_ctx = hw_device_ctx;
87 av_codec_ctx->get_format = GetHwFormat;
88 return; 125 return;
89 } 126 }
90#endif 127 auto* hw_device_ctx = av_buffer_ref(av_gpu_decoder);
91 // TODO more GPU accelerated decoders 128 ASSERT_MSG(hw_device_ctx, "av_buffer_ref failed");
129 av_codec_ctx->hw_device_ctx = hw_device_ctx;
130 av_codec_ctx->get_format = GetGpuFormat;
92} 131}
93 132
94void Codec::Initialize() { 133void Codec::Initialize() {
95 AVCodecID codec; 134 const AVCodecID codec = [&] {
96 switch (current_codec) { 135 switch (current_codec) {
97 case NvdecCommon::VideoCodec::H264: 136 case NvdecCommon::VideoCodec::H264:
98 codec = AV_CODEC_ID_H264; 137 return AV_CODEC_ID_H264;
99 break; 138 case NvdecCommon::VideoCodec::Vp9:
100 case NvdecCommon::VideoCodec::Vp9: 139 return AV_CODEC_ID_VP9;
101 codec = AV_CODEC_ID_VP9; 140 default:
102 break; 141 UNIMPLEMENTED_MSG("Unknown codec {}", current_codec);
103 default: 142 return AV_CODEC_ID_NONE;
104 UNIMPLEMENTED_MSG("Unknown codec {}", current_codec); 143 }
144 }();
145 av_codec = avcodec_find_decoder(codec);
146
147 InitializeAvCodecContext();
148 if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::GPU) {
149 InitializeGpuDecoder();
150 }
151 if (const int res = avcodec_open2(av_codec_ctx, av_codec, nullptr); res < 0) {
152 LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed with result {}", res);
153 avcodec_free_context(&av_codec_ctx);
154 av_buffer_unref(&av_gpu_decoder);
105 return; 155 return;
106 } 156 }
107 av_codec = avcodec_find_decoder(codec);
108 av_codec_ctx = avcodec_alloc_context3(av_codec);
109 av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0);
110 InitializeHwdec();
111 if (!av_codec_ctx->hw_device_ctx) { 157 if (!av_codec_ctx->hw_device_ctx) {
112 LOG_INFO(Service_NVDRV, "Using FFmpeg software decoding"); 158 LOG_INFO(Service_NVDRV, "Using FFmpeg software decoding");
113 } 159 }
114 const auto av_error = avcodec_open2(av_codec_ctx, av_codec, nullptr);
115 if (av_error < 0) {
116 LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed.");
117 avcodec_close(av_codec_ctx);
118 av_buffer_unref(&av_hw_device);
119 return;
120 }
121 initialized = true; 160 initialized = true;
122} 161}
123 162
@@ -133,6 +172,9 @@ void Codec::Decode() {
133 if (is_first_frame) { 172 if (is_first_frame) {
134 Initialize(); 173 Initialize();
135 } 174 }
175 if (!initialized) {
176 return;
177 }
136 bool vp9_hidden_frame = false; 178 bool vp9_hidden_frame = false;
137 std::vector<u8> frame_data; 179 std::vector<u8> frame_data;
138 if (current_codec == NvdecCommon::VideoCodec::H264) { 180 if (current_codec == NvdecCommon::VideoCodec::H264) {
@@ -141,50 +183,48 @@ void Codec::Decode() {
141 frame_data = vp9_decoder->ComposeFrameHeader(state); 183 frame_data = vp9_decoder->ComposeFrameHeader(state);
142 vp9_hidden_frame = vp9_decoder->WasFrameHidden(); 184 vp9_hidden_frame = vp9_decoder->WasFrameHidden();
143 } 185 }
144 AVPacket packet{}; 186 AVPacketPtr packet{av_packet_alloc(), AVPacketDeleter};
145 av_init_packet(&packet); 187 if (!packet) {
146 packet.data = frame_data.data(); 188 LOG_ERROR(Service_NVDRV, "av_packet_alloc failed");
147 packet.size = static_cast<s32>(frame_data.size()); 189 return;
148 if (const int ret = avcodec_send_packet(av_codec_ctx, &packet); ret) { 190 }
149 LOG_DEBUG(Service_NVDRV, "avcodec_send_packet error {}", ret); 191 packet->data = frame_data.data();
192 packet->size = static_cast<s32>(frame_data.size());
193 if (const int res = avcodec_send_packet(av_codec_ctx, packet.get()); res != 0) {
194 LOG_DEBUG(Service_NVDRV, "avcodec_send_packet error {}", res);
150 return; 195 return;
151 } 196 }
152 // Only receive/store visible frames 197 // Only receive/store visible frames
153 if (vp9_hidden_frame) { 198 if (vp9_hidden_frame) {
154 return; 199 return;
155 } 200 }
156 AVFrame* hw_frame = av_frame_alloc(); 201 AVFramePtr initial_frame{av_frame_alloc(), AVFrameDeleter};
157 AVFrame* sw_frame = hw_frame; 202 AVFramePtr final_frame{nullptr, AVFrameDeleter};
158 ASSERT_MSG(hw_frame, "av_frame_alloc hw_frame failed"); 203 ASSERT_MSG(initial_frame, "av_frame_alloc initial_frame failed");
159 if (const int ret = avcodec_receive_frame(av_codec_ctx, hw_frame); ret) { 204 if (const int ret = avcodec_receive_frame(av_codec_ctx, initial_frame.get()); ret) {
160 LOG_DEBUG(Service_NVDRV, "avcodec_receive_frame error {}", ret); 205 LOG_DEBUG(Service_NVDRV, "avcodec_receive_frame error {}", ret);
161 av_frame_free(&hw_frame);
162 return; 206 return;
163 } 207 }
164 if (!hw_frame->width || !hw_frame->height) { 208 if (initial_frame->width == 0 || initial_frame->height == 0) {
165 LOG_WARNING(Service_NVDRV, "Zero width or height in frame"); 209 LOG_WARNING(Service_NVDRV, "Zero width or height in frame");
166 av_frame_free(&hw_frame);
167 return; 210 return;
168 } 211 }
169#if defined(LIBVA_FOUND) 212 if (av_codec_ctx->hw_device_ctx) {
170 // Hardware acceleration code from FFmpeg/doc/examples/hw_decode.c under MIT license 213 final_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter};
171 if (hw_frame->format == AV_PIX_FMT_VAAPI) { 214 ASSERT_MSG(final_frame, "av_frame_alloc final_frame failed");
172 sw_frame = av_frame_alloc();
173 ASSERT_MSG(sw_frame, "av_frame_alloc sw_frame failed");
174 // Can't use AV_PIX_FMT_YUV420P and share code with software decoding in vic.cpp 215 // Can't use AV_PIX_FMT_YUV420P and share code with software decoding in vic.cpp
175 // because Intel drivers crash unless using AV_PIX_FMT_NV12 216 // because Intel drivers crash unless using AV_PIX_FMT_NV12
176 sw_frame->format = AV_PIX_FMT_NV12; 217 final_frame->format = PREFERRED_GPU_FMT;
177 const int transfer_data_ret = av_hwframe_transfer_data(sw_frame, hw_frame, 0); 218 const int ret = av_hwframe_transfer_data(final_frame.get(), initial_frame.get(), 0);
178 ASSERT_MSG(!transfer_data_ret, "av_hwframe_transfer_data error {}", transfer_data_ret); 219 ASSERT_MSG(!ret, "av_hwframe_transfer_data error {}", ret);
179 av_frame_free(&hw_frame); 220 } else {
221 final_frame = std::move(initial_frame);
180 } 222 }
181#endif 223 if (final_frame->format != PREFERRED_CPU_FMT && final_frame->format != PREFERRED_GPU_FMT) {
182 if (sw_frame->format != AV_PIX_FMT_YUV420P && sw_frame->format != AV_PIX_FMT_NV12) { 224 UNIMPLEMENTED_MSG("Unexpected video format: {}", final_frame->format);
183 UNIMPLEMENTED_MSG("Unexpected video format from host graphics: {}", sw_frame->format);
184 av_frame_free(&sw_frame);
185 return; 225 return;
186 } 226 }
187 av_frames.push(AVFramePtr{sw_frame, AVFrameDeleter}); 227 av_frames.push(std::move(final_frame));
188 if (av_frames.size() > 10) { 228 if (av_frames.size() > 10) {
189 LOG_TRACE(Service_NVDRV, "av_frames.push overflow dropped frame"); 229 LOG_TRACE(Service_NVDRV, "av_frames.push overflow dropped frame");
190 av_frames.pop(); 230 av_frames.pop();
diff --git a/src/video_core/command_classes/codecs/codec.h b/src/video_core/command_classes/codecs/codec.h
index 71936203f..f9a80886f 100644
--- a/src/video_core/command_classes/codecs/codec.h
+++ b/src/video_core/command_classes/codecs/codec.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <string_view>
8#include <queue> 9#include <queue>
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "video_core/command_classes/nvdec_common.h" 11#include "video_core/command_classes/nvdec_common.h"
@@ -50,18 +51,23 @@ public:
50 51
51 /// Returns the value of current_codec 52 /// Returns the value of current_codec
52 [[nodiscard]] NvdecCommon::VideoCodec GetCurrentCodec() const; 53 [[nodiscard]] NvdecCommon::VideoCodec GetCurrentCodec() const;
54
53 /// Return name of the current codec 55 /// Return name of the current codec
54 [[nodiscard]] std::string_view GetCurrentCodecName() const; 56 [[nodiscard]] std::string_view GetCurrentCodecName() const;
55 57
56private: 58private:
57 void InitializeHwdec(); 59 void InitializeAvCodecContext();
60
61 void InitializeGpuDecoder();
62
63 bool CreateGpuAvDevice();
58 64
59 bool initialized{}; 65 bool initialized{};
60 NvdecCommon::VideoCodec current_codec{NvdecCommon::VideoCodec::None}; 66 NvdecCommon::VideoCodec current_codec{NvdecCommon::VideoCodec::None};
61 67
62 AVCodec* av_codec{nullptr}; 68 AVCodec* av_codec{nullptr};
63 AVBufferRef* av_hw_device{nullptr};
64 AVCodecContext* av_codec_ctx{nullptr}; 69 AVCodecContext* av_codec_ctx{nullptr};
70 AVBufferRef* av_gpu_decoder{nullptr};
65 71
66 GPU& gpu; 72 GPU& gpu;
67 const NvdecCommon::NvdecRegisters& state; 73 const NvdecCommon::NvdecRegisters& state;
diff --git a/src/video_core/command_classes/codecs/h264.cpp b/src/video_core/command_classes/codecs/h264.cpp
index 5fb6d45ee..51ee14c13 100644
--- a/src/video_core/command_classes/codecs/h264.cpp
+++ b/src/video_core/command_classes/codecs/h264.cpp
@@ -95,7 +95,8 @@ const std::vector<u8>& H264::ComposeFrameHeader(const NvdecCommon::NvdecRegister
95 const s32 pic_height = context.h264_parameter_set.frame_height_in_map_units / 95 const s32 pic_height = context.h264_parameter_set.frame_height_in_map_units /
96 (context.h264_parameter_set.frame_mbs_only_flag ? 1 : 2); 96 (context.h264_parameter_set.frame_mbs_only_flag ? 1 : 2);
97 97
98 writer.WriteUe(16); 98 // TODO (ameerj): Where do we get this number, it seems to be particular for each stream
99 writer.WriteUe(6); // Max number of reference frames
99 writer.WriteBit(false); 100 writer.WriteBit(false);
100 writer.WriteUe(context.h264_parameter_set.pic_width_in_mbs - 1); 101 writer.WriteUe(context.h264_parameter_set.pic_width_in_mbs - 1);
101 writer.WriteUe(pic_height - 1); 102 writer.WriteUe(pic_height - 1);
diff --git a/src/video_core/command_classes/codecs/vp9.cpp b/src/video_core/command_classes/codecs/vp9.cpp
index 70030066a..d7e749485 100644
--- a/src/video_core/command_classes/codecs/vp9.cpp
+++ b/src/video_core/command_classes/codecs/vp9.cpp
@@ -742,6 +742,7 @@ VpxBitStreamWriter VP9::ComposeUncompressedHeader() {
742 uncomp_writer.WriteDeltaQ(current_frame_info.uv_dc_delta_q); 742 uncomp_writer.WriteDeltaQ(current_frame_info.uv_dc_delta_q);
743 uncomp_writer.WriteDeltaQ(current_frame_info.uv_ac_delta_q); 743 uncomp_writer.WriteDeltaQ(current_frame_info.uv_ac_delta_q);
744 744
745 ASSERT(!current_frame_info.segment_enabled);
745 uncomp_writer.WriteBit(false); // Segmentation enabled (TODO). 746 uncomp_writer.WriteBit(false); // Segmentation enabled (TODO).
746 747
747 const s32 min_tile_cols_log2 = CalcMinLog2TileCols(current_frame_info.frame_size.width); 748 const s32 min_tile_cols_log2 = CalcMinLog2TileCols(current_frame_info.frame_size.width);
diff --git a/src/video_core/command_classes/codecs/vp9_types.h b/src/video_core/command_classes/codecs/vp9_types.h
index 87eafdb03..3b1ed4b3a 100644
--- a/src/video_core/command_classes/codecs/vp9_types.h
+++ b/src/video_core/command_classes/codecs/vp9_types.h
@@ -22,7 +22,7 @@ struct Vp9FrameDimensions {
22}; 22};
23static_assert(sizeof(Vp9FrameDimensions) == 0x8, "Vp9 Vp9FrameDimensions is an invalid size"); 23static_assert(sizeof(Vp9FrameDimensions) == 0x8, "Vp9 Vp9FrameDimensions is an invalid size");
24 24
25enum FrameFlags : u32 { 25enum class FrameFlags : u32 {
26 IsKeyFrame = 1 << 0, 26 IsKeyFrame = 1 << 0,
27 LastFrameIsKeyFrame = 1 << 1, 27 LastFrameIsKeyFrame = 1 << 1,
28 FrameSizeChanged = 1 << 2, 28 FrameSizeChanged = 1 << 2,
@@ -30,6 +30,7 @@ enum FrameFlags : u32 {
30 LastShowFrame = 1 << 4, 30 LastShowFrame = 1 << 4,
31 IntraOnly = 1 << 5, 31 IntraOnly = 1 << 5,
32}; 32};
33DECLARE_ENUM_FLAG_OPERATORS(FrameFlags)
33 34
34enum class TxSize { 35enum class TxSize {
35 Tx4x4 = 0, // 4x4 transform 36 Tx4x4 = 0, // 4x4 transform
@@ -92,44 +93,34 @@ struct Vp9EntropyProbs {
92static_assert(sizeof(Vp9EntropyProbs) == 0x7B4, "Vp9EntropyProbs is an invalid size"); 93static_assert(sizeof(Vp9EntropyProbs) == 0x7B4, "Vp9EntropyProbs is an invalid size");
93 94
94struct Vp9PictureInfo { 95struct Vp9PictureInfo {
95 bool is_key_frame; 96 u32 bitstream_size;
96 bool intra_only; 97 std::array<u64, 4> frame_offsets;
97 bool last_frame_was_key;
98 bool frame_size_changed;
99 bool error_resilient_mode;
100 bool last_frame_shown;
101 bool show_frame;
102 std::array<s8, 4> ref_frame_sign_bias; 98 std::array<s8, 4> ref_frame_sign_bias;
103 s32 base_q_index; 99 s32 base_q_index;
104 s32 y_dc_delta_q; 100 s32 y_dc_delta_q;
105 s32 uv_dc_delta_q; 101 s32 uv_dc_delta_q;
106 s32 uv_ac_delta_q; 102 s32 uv_ac_delta_q;
107 bool lossless;
108 s32 transform_mode; 103 s32 transform_mode;
109 bool allow_high_precision_mv;
110 s32 interp_filter; 104 s32 interp_filter;
111 s32 reference_mode; 105 s32 reference_mode;
112 s8 comp_fixed_ref;
113 std::array<s8, 2> comp_var_ref;
114 s32 log2_tile_cols; 106 s32 log2_tile_cols;
115 s32 log2_tile_rows; 107 s32 log2_tile_rows;
116 bool segment_enabled;
117 bool segment_map_update;
118 bool segment_map_temporal_update;
119 s32 segment_abs_delta;
120 std::array<u32, 8> segment_feature_enable;
121 std::array<std::array<s16, 4>, 8> segment_feature_data;
122 bool mode_ref_delta_enabled;
123 bool use_prev_in_find_mv_refs;
124 std::array<s8, 4> ref_deltas; 108 std::array<s8, 4> ref_deltas;
125 std::array<s8, 2> mode_deltas; 109 std::array<s8, 2> mode_deltas;
126 Vp9EntropyProbs entropy; 110 Vp9EntropyProbs entropy;
127 Vp9FrameDimensions frame_size; 111 Vp9FrameDimensions frame_size;
128 u8 first_level; 112 u8 first_level;
129 u8 sharpness_level; 113 u8 sharpness_level;
130 u32 bitstream_size; 114 bool is_key_frame;
131 std::array<u64, 4> frame_offsets; 115 bool intra_only;
132 std::array<bool, 4> refresh_frame; 116 bool last_frame_was_key;
117 bool error_resilient_mode;
118 bool last_frame_shown;
119 bool show_frame;
120 bool lossless;
121 bool allow_high_precision_mv;
122 bool segment_enabled;
123 bool mode_ref_delta_enabled;
133}; 124};
134 125
135struct Vp9FrameContainer { 126struct Vp9FrameContainer {
@@ -145,7 +136,7 @@ struct PictureInfo {
145 Vp9FrameDimensions golden_frame_size; ///< 0x50 136 Vp9FrameDimensions golden_frame_size; ///< 0x50
146 Vp9FrameDimensions alt_frame_size; ///< 0x58 137 Vp9FrameDimensions alt_frame_size; ///< 0x58
147 Vp9FrameDimensions current_frame_size; ///< 0x60 138 Vp9FrameDimensions current_frame_size; ///< 0x60
148 u32 vp9_flags; ///< 0x68 139 FrameFlags vp9_flags; ///< 0x68
149 std::array<s8, 4> ref_frame_sign_bias; ///< 0x6C 140 std::array<s8, 4> ref_frame_sign_bias; ///< 0x6C
150 u8 first_level; ///< 0x70 141 u8 first_level; ///< 0x70
151 u8 sharpness_level; ///< 0x71 142 u8 sharpness_level; ///< 0x71
@@ -158,60 +149,43 @@ struct PictureInfo {
158 u8 allow_high_precision_mv; ///< 0x78 149 u8 allow_high_precision_mv; ///< 0x78
159 u8 interp_filter; ///< 0x79 150 u8 interp_filter; ///< 0x79
160 u8 reference_mode; ///< 0x7A 151 u8 reference_mode; ///< 0x7A
161 s8 comp_fixed_ref; ///< 0x7B 152 INSERT_PADDING_BYTES_NOINIT(3); ///< 0x7B
162 std::array<s8, 2> comp_var_ref; ///< 0x7C
163 u8 log2_tile_cols; ///< 0x7E 153 u8 log2_tile_cols; ///< 0x7E
164 u8 log2_tile_rows; ///< 0x7F 154 u8 log2_tile_rows; ///< 0x7F
165 Segmentation segmentation; ///< 0x80 155 Segmentation segmentation; ///< 0x80
166 LoopFilter loop_filter; ///< 0xE4 156 LoopFilter loop_filter; ///< 0xE4
167 INSERT_PADDING_BYTES_NOINIT(5); ///< 0xEB 157 INSERT_PADDING_BYTES_NOINIT(21); ///< 0xEB
168 u32 surface_params; ///< 0xF0
169 INSERT_PADDING_WORDS_NOINIT(3); ///< 0xF4
170 158
171 [[nodiscard]] Vp9PictureInfo Convert() const { 159 [[nodiscard]] Vp9PictureInfo Convert() const {
172 return { 160 return {
173 .is_key_frame = (vp9_flags & FrameFlags::IsKeyFrame) != 0, 161 .bitstream_size = bitstream_size,
174 .intra_only = (vp9_flags & FrameFlags::IntraOnly) != 0, 162 .frame_offsets{},
175 .last_frame_was_key = (vp9_flags & FrameFlags::LastFrameIsKeyFrame) != 0,
176 .frame_size_changed = (vp9_flags & FrameFlags::FrameSizeChanged) != 0,
177 .error_resilient_mode = (vp9_flags & FrameFlags::ErrorResilientMode) != 0,
178 .last_frame_shown = (vp9_flags & FrameFlags::LastShowFrame) != 0,
179 .show_frame = true,
180 .ref_frame_sign_bias = ref_frame_sign_bias, 163 .ref_frame_sign_bias = ref_frame_sign_bias,
181 .base_q_index = base_q_index, 164 .base_q_index = base_q_index,
182 .y_dc_delta_q = y_dc_delta_q, 165 .y_dc_delta_q = y_dc_delta_q,
183 .uv_dc_delta_q = uv_dc_delta_q, 166 .uv_dc_delta_q = uv_dc_delta_q,
184 .uv_ac_delta_q = uv_ac_delta_q, 167 .uv_ac_delta_q = uv_ac_delta_q,
185 .lossless = lossless != 0,
186 .transform_mode = tx_mode, 168 .transform_mode = tx_mode,
187 .allow_high_precision_mv = allow_high_precision_mv != 0,
188 .interp_filter = interp_filter, 169 .interp_filter = interp_filter,
189 .reference_mode = reference_mode, 170 .reference_mode = reference_mode,
190 .comp_fixed_ref = comp_fixed_ref,
191 .comp_var_ref = comp_var_ref,
192 .log2_tile_cols = log2_tile_cols, 171 .log2_tile_cols = log2_tile_cols,
193 .log2_tile_rows = log2_tile_rows, 172 .log2_tile_rows = log2_tile_rows,
194 .segment_enabled = segmentation.enabled != 0,
195 .segment_map_update = segmentation.update_map != 0,
196 .segment_map_temporal_update = segmentation.temporal_update != 0,
197 .segment_abs_delta = segmentation.abs_delta,
198 .segment_feature_enable = segmentation.feature_mask,
199 .segment_feature_data = segmentation.feature_data,
200 .mode_ref_delta_enabled = loop_filter.mode_ref_delta_enabled != 0,
201 .use_prev_in_find_mv_refs = !(vp9_flags == (FrameFlags::ErrorResilientMode)) &&
202 !(vp9_flags == (FrameFlags::FrameSizeChanged)) &&
203 !(vp9_flags == (FrameFlags::IntraOnly)) &&
204 (vp9_flags == (FrameFlags::LastShowFrame)) &&
205 !(vp9_flags == (FrameFlags::LastFrameIsKeyFrame)),
206 .ref_deltas = loop_filter.ref_deltas, 173 .ref_deltas = loop_filter.ref_deltas,
207 .mode_deltas = loop_filter.mode_deltas, 174 .mode_deltas = loop_filter.mode_deltas,
208 .entropy{}, 175 .entropy{},
209 .frame_size = current_frame_size, 176 .frame_size = current_frame_size,
210 .first_level = first_level, 177 .first_level = first_level,
211 .sharpness_level = sharpness_level, 178 .sharpness_level = sharpness_level,
212 .bitstream_size = bitstream_size, 179 .is_key_frame = True(vp9_flags & FrameFlags::IsKeyFrame),
213 .frame_offsets{}, 180 .intra_only = True(vp9_flags & FrameFlags::IntraOnly),
214 .refresh_frame{}, 181 .last_frame_was_key = True(vp9_flags & FrameFlags::LastFrameIsKeyFrame),
182 .error_resilient_mode = True(vp9_flags & FrameFlags::ErrorResilientMode),
183 .last_frame_shown = True(vp9_flags & FrameFlags::LastShowFrame),
184 .show_frame = true,
185 .lossless = lossless != 0,
186 .allow_high_precision_mv = allow_high_precision_mv != 0,
187 .segment_enabled = segmentation.enabled != 0,
188 .mode_ref_delta_enabled = loop_filter.mode_ref_delta_enabled != 0,
215 }; 189 };
216 } 190 }
217}; 191};
@@ -316,7 +290,6 @@ ASSERT_POSITION(last_frame_size, 0x48);
316ASSERT_POSITION(first_level, 0x70); 290ASSERT_POSITION(first_level, 0x70);
317ASSERT_POSITION(segmentation, 0x80); 291ASSERT_POSITION(segmentation, 0x80);
318ASSERT_POSITION(loop_filter, 0xE4); 292ASSERT_POSITION(loop_filter, 0xE4);
319ASSERT_POSITION(surface_params, 0xF0);
320#undef ASSERT_POSITION 293#undef ASSERT_POSITION
321 294
322#define ASSERT_POSITION(field_name, position) \ 295#define ASSERT_POSITION(field_name, position) \
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 1aa43523a..7f4ca6282 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -475,10 +475,10 @@ public:
475 475
476 // These values are used by Nouveau and some games. 476 // These values are used by Nouveau and some games.
477 AddGL = 0x8006, 477 AddGL = 0x8006,
478 SubtractGL = 0x8007, 478 MinGL = 0x8007,
479 ReverseSubtractGL = 0x8008, 479 MaxGL = 0x8008,
480 MinGL = 0x800a, 480 SubtractGL = 0x800a,
481 MaxGL = 0x800b 481 ReverseSubtractGL = 0x800b
482 }; 482 };
483 483
484 enum class Factor : u32 { 484 enum class Factor : u32 {
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index c60ed6453..dce00e829 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
6
5#include "common/alignment.h" 7#include "common/alignment.h"
6#include "common/assert.h" 8#include "common/assert.h"
7#include "common/logging/log.h" 9#include "common/logging/log.h"
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h
index ee992aed4..de9e41659 100644
--- a/src/video_core/renderer_opengl/gl_device.h
+++ b/src/video_core/renderer_opengl/gl_device.h
@@ -156,6 +156,10 @@ public:
156 return shader_backend; 156 return shader_backend;
157 } 157 }
158 158
159 bool IsAmd() const {
160 return vendor_name == "ATI Technologies Inc.";
161 }
162
159private: 163private:
160 static bool TestVariableAoffi(); 164 static bool TestVariableAoffi();
161 static bool TestPreciseBug(); 165 static bool TestPreciseBug();
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 1f4dda17e..b0e14182e 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -219,6 +219,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
219 host_info{ 219 host_info{
220 .support_float16 = false, 220 .support_float16 = false,
221 .support_int64 = device.HasShaderInt64(), 221 .support_int64 = device.HasShaderInt64(),
222 .needs_demote_reorder = device.IsAmd(),
222 } { 223 } {
223 if (use_asynchronous_shaders) { 224 if (use_asynchronous_shaders) {
224 workers = CreateWorkers(); 225 workers = CreateWorkers();
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index 7c9b0d6db..9ff0a28cd 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -164,7 +164,8 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
164 blit_screen.Recreate(); 164 blit_screen.Recreate();
165 } 165 }
166 const VkSemaphore render_semaphore = blit_screen.DrawToSwapchain(*framebuffer, use_accelerated); 166 const VkSemaphore render_semaphore = blit_screen.DrawToSwapchain(*framebuffer, use_accelerated);
167 scheduler.Flush(render_semaphore); 167 const VkSemaphore present_semaphore = swapchain.CurrentPresentSemaphore();
168 scheduler.Flush(render_semaphore, present_semaphore);
168 scheduler.WaitWorker(); 169 scheduler.WaitWorker();
169 swapchain.Present(render_semaphore); 170 swapchain.Present(render_semaphore);
170 171
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 5c43b8acf..888bc7392 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -159,11 +159,13 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
159 159
160 const VAddr framebuffer_addr = framebuffer.address + framebuffer.offset; 160 const VAddr framebuffer_addr = framebuffer.address + framebuffer.offset;
161 const u8* const host_ptr = cpu_memory.GetPointer(framebuffer_addr); 161 const u8* const host_ptr = cpu_memory.GetPointer(framebuffer_addr);
162 const size_t size_bytes = GetSizeInBytes(framebuffer);
163 162
164 // TODO(Rodrigo): Read this from HLE 163 // TODO(Rodrigo): Read this from HLE
165 constexpr u32 block_height_log2 = 4; 164 constexpr u32 block_height_log2 = 4;
166 const u32 bytes_per_pixel = GetBytesPerPixel(framebuffer); 165 const u32 bytes_per_pixel = GetBytesPerPixel(framebuffer);
166 const u64 size_bytes{Tegra::Texture::CalculateSize(true, bytes_per_pixel,
167 framebuffer.stride, framebuffer.height,
168 1, block_height_log2, 0)};
167 Tegra::Texture::UnswizzleTexture( 169 Tegra::Texture::UnswizzleTexture(
168 mapped_span.subspan(image_offset, size_bytes), std::span(host_ptr, size_bytes), 170 mapped_span.subspan(image_offset, size_bytes), std::span(host_ptr, size_bytes),
169 bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0); 171 bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0);
@@ -356,7 +358,7 @@ void VKBlitScreen::CreateDescriptorPool() {
356void VKBlitScreen::CreateRenderPass() { 358void VKBlitScreen::CreateRenderPass() {
357 const VkAttachmentDescription color_attachment{ 359 const VkAttachmentDescription color_attachment{
358 .flags = 0, 360 .flags = 0,
359 .format = swapchain.GetImageFormat(), 361 .format = swapchain.GetImageViewFormat(),
360 .samples = VK_SAMPLE_COUNT_1_BIT, 362 .samples = VK_SAMPLE_COUNT_1_BIT,
361 .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, 363 .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
362 .storeOp = VK_ATTACHMENT_STORE_OP_STORE, 364 .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp
index 60e45f1b9..d87da2a34 100644
--- a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp
+++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
5#include <mutex> 6#include <mutex>
6#include <span> 7#include <span>
7#include <vector> 8#include <vector>
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index f316c4f92..31bfbcb06 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -325,6 +325,8 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, Tegra::Engines::Maxw
325 host_info = Shader::HostTranslateInfo{ 325 host_info = Shader::HostTranslateInfo{
326 .support_float16 = device.IsFloat16Supported(), 326 .support_float16 = device.IsFloat16Supported(),
327 .support_int64 = device.IsShaderInt64Supported(), 327 .support_int64 = device.IsShaderInt64Supported(),
328 .needs_demote_reorder = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY_KHR ||
329 driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR,
328 }; 330 };
329} 331}
330 332
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 3ac18ea54..3bcd6d6cc 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -228,9 +228,7 @@ void RasterizerVulkan::Clear() {
228 }; 228 };
229 229
230 const u32 color_attachment = regs.clear_buffers.RT; 230 const u32 color_attachment = regs.clear_buffers.RT;
231 const auto attachment_aspect_mask = framebuffer->ImageRanges()[color_attachment].aspectMask; 231 if (use_color && framebuffer->HasAspectColorBit(color_attachment)) {
232 const bool is_color_rt = (attachment_aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) != 0;
233 if (use_color && is_color_rt) {
234 VkClearValue clear_value; 232 VkClearValue clear_value;
235 std::memcpy(clear_value.color.float32, regs.clear_color, sizeof(regs.clear_color)); 233 std::memcpy(clear_value.color.float32, regs.clear_color, sizeof(regs.clear_color));
236 234
@@ -248,12 +246,15 @@ void RasterizerVulkan::Clear() {
248 return; 246 return;
249 } 247 }
250 VkImageAspectFlags aspect_flags = 0; 248 VkImageAspectFlags aspect_flags = 0;
251 if (use_depth) { 249 if (use_depth && framebuffer->HasAspectDepthBit()) {
252 aspect_flags |= VK_IMAGE_ASPECT_DEPTH_BIT; 250 aspect_flags |= VK_IMAGE_ASPECT_DEPTH_BIT;
253 } 251 }
254 if (use_stencil) { 252 if (use_stencil && framebuffer->HasAspectStencilBit()) {
255 aspect_flags |= VK_IMAGE_ASPECT_STENCIL_BIT; 253 aspect_flags |= VK_IMAGE_ASPECT_STENCIL_BIT;
256 } 254 }
255 if (aspect_flags == 0) {
256 return;
257 }
257 scheduler.Record([clear_depth = regs.clear_depth, clear_stencil = regs.clear_stencil, 258 scheduler.Record([clear_depth = regs.clear_depth, clear_stencil = regs.clear_stencil,
258 clear_rect, aspect_flags](vk::CommandBuffer cmdbuf) { 259 clear_rect, aspect_flags](vk::CommandBuffer cmdbuf) {
259 VkClearAttachment attachment; 260 VkClearAttachment attachment;
@@ -764,12 +765,7 @@ void RasterizerVulkan::UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs) {
764 const Maxwell::StencilOp zpass = regs.stencil_front_op_zpass; 765 const Maxwell::StencilOp zpass = regs.stencil_front_op_zpass;
765 const Maxwell::ComparisonOp compare = regs.stencil_front_func_func; 766 const Maxwell::ComparisonOp compare = regs.stencil_front_func_func;
766 if (regs.stencil_two_side_enable) { 767 if (regs.stencil_two_side_enable) {
767 scheduler.Record([fail, zfail, zpass, compare](vk::CommandBuffer cmdbuf) { 768 // Separate stencil op per face
768 cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_AND_BACK, MaxwellToVK::StencilOp(fail),
769 MaxwellToVK::StencilOp(zpass), MaxwellToVK::StencilOp(zfail),
770 MaxwellToVK::ComparisonOp(compare));
771 });
772 } else {
773 const Maxwell::StencilOp back_fail = regs.stencil_back_op_fail; 769 const Maxwell::StencilOp back_fail = regs.stencil_back_op_fail;
774 const Maxwell::StencilOp back_zfail = regs.stencil_back_op_zfail; 770 const Maxwell::StencilOp back_zfail = regs.stencil_back_op_zfail;
775 const Maxwell::StencilOp back_zpass = regs.stencil_back_op_zpass; 771 const Maxwell::StencilOp back_zpass = regs.stencil_back_op_zpass;
@@ -784,6 +780,13 @@ void RasterizerVulkan::UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs) {
784 MaxwellToVK::StencilOp(back_zfail), 780 MaxwellToVK::StencilOp(back_zfail),
785 MaxwellToVK::ComparisonOp(back_compare)); 781 MaxwellToVK::ComparisonOp(back_compare));
786 }); 782 });
783 } else {
784 // Front face defines the stencil op of both faces
785 scheduler.Record([fail, zfail, zpass, compare](vk::CommandBuffer cmdbuf) {
786 cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_AND_BACK, MaxwellToVK::StencilOp(fail),
787 MaxwellToVK::StencilOp(zpass), MaxwellToVK::StencilOp(zfail),
788 MaxwellToVK::ComparisonOp(compare));
789 });
787 } 790 }
788} 791}
789 792
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index 4840962de..1d438787a 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -55,14 +55,14 @@ VKScheduler::~VKScheduler() {
55 worker_thread.join(); 55 worker_thread.join();
56} 56}
57 57
58void VKScheduler::Flush(VkSemaphore semaphore) { 58void VKScheduler::Flush(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
59 SubmitExecution(semaphore); 59 SubmitExecution(signal_semaphore, wait_semaphore);
60 AllocateNewContext(); 60 AllocateNewContext();
61} 61}
62 62
63void VKScheduler::Finish(VkSemaphore semaphore) { 63void VKScheduler::Finish(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
64 const u64 presubmit_tick = CurrentTick(); 64 const u64 presubmit_tick = CurrentTick();
65 SubmitExecution(semaphore); 65 SubmitExecution(signal_semaphore, wait_semaphore);
66 WaitWorker(); 66 WaitWorker();
67 Wait(presubmit_tick); 67 Wait(presubmit_tick);
68 AllocateNewContext(); 68 AllocateNewContext();
@@ -171,37 +171,41 @@ void VKScheduler::AllocateWorkerCommandBuffer() {
171 }); 171 });
172} 172}
173 173
174void VKScheduler::SubmitExecution(VkSemaphore semaphore) { 174void VKScheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
175 EndPendingOperations(); 175 EndPendingOperations();
176 InvalidateState(); 176 InvalidateState();
177 177
178 const u64 signal_value = master_semaphore->NextTick(); 178 const u64 signal_value = master_semaphore->NextTick();
179 Record([semaphore, signal_value, this](vk::CommandBuffer cmdbuf) { 179 Record([signal_semaphore, wait_semaphore, signal_value, this](vk::CommandBuffer cmdbuf) {
180 cmdbuf.End(); 180 cmdbuf.End();
181
182 const u32 num_signal_semaphores = semaphore ? 2U : 1U;
183
184 const u64 wait_value = signal_value - 1;
185 const VkPipelineStageFlags wait_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
186
187 const VkSemaphore timeline_semaphore = master_semaphore->Handle(); 181 const VkSemaphore timeline_semaphore = master_semaphore->Handle();
182
183 const u32 num_signal_semaphores = signal_semaphore ? 2U : 1U;
188 const std::array signal_values{signal_value, u64(0)}; 184 const std::array signal_values{signal_value, u64(0)};
189 const std::array signal_semaphores{timeline_semaphore, semaphore}; 185 const std::array signal_semaphores{timeline_semaphore, signal_semaphore};
186
187 const u32 num_wait_semaphores = wait_semaphore ? 2U : 1U;
188 const std::array wait_values{signal_value - 1, u64(1)};
189 const std::array wait_semaphores{timeline_semaphore, wait_semaphore};
190 static constexpr std::array<VkPipelineStageFlags, 2> wait_stage_masks{
191 VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
192 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
193 };
190 194
191 const VkTimelineSemaphoreSubmitInfoKHR timeline_si{ 195 const VkTimelineSemaphoreSubmitInfoKHR timeline_si{
192 .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR, 196 .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR,
193 .pNext = nullptr, 197 .pNext = nullptr,
194 .waitSemaphoreValueCount = 1, 198 .waitSemaphoreValueCount = num_wait_semaphores,
195 .pWaitSemaphoreValues = &wait_value, 199 .pWaitSemaphoreValues = wait_values.data(),
196 .signalSemaphoreValueCount = num_signal_semaphores, 200 .signalSemaphoreValueCount = num_signal_semaphores,
197 .pSignalSemaphoreValues = signal_values.data(), 201 .pSignalSemaphoreValues = signal_values.data(),
198 }; 202 };
199 const VkSubmitInfo submit_info{ 203 const VkSubmitInfo submit_info{
200 .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, 204 .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
201 .pNext = &timeline_si, 205 .pNext = &timeline_si,
202 .waitSemaphoreCount = 1, 206 .waitSemaphoreCount = num_wait_semaphores,
203 .pWaitSemaphores = &timeline_semaphore, 207 .pWaitSemaphores = wait_semaphores.data(),
204 .pWaitDstStageMask = &wait_stage_mask, 208 .pWaitDstStageMask = wait_stage_masks.data(),
205 .commandBufferCount = 1, 209 .commandBufferCount = 1,
206 .pCommandBuffers = cmdbuf.address(), 210 .pCommandBuffers = cmdbuf.address(),
207 .signalSemaphoreCount = num_signal_semaphores, 211 .signalSemaphoreCount = num_signal_semaphores,
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h
index cf39a2363..759ed5a48 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.h
+++ b/src/video_core/renderer_vulkan/vk_scheduler.h
@@ -34,10 +34,10 @@ public:
34 ~VKScheduler(); 34 ~VKScheduler();
35 35
36 /// Sends the current execution context to the GPU. 36 /// Sends the current execution context to the GPU.
37 void Flush(VkSemaphore semaphore = nullptr); 37 void Flush(VkSemaphore signal_semaphore = nullptr, VkSemaphore wait_semaphore = nullptr);
38 38
39 /// Sends the current execution context to the GPU and waits for it to complete. 39 /// Sends the current execution context to the GPU and waits for it to complete.
40 void Finish(VkSemaphore semaphore = nullptr); 40 void Finish(VkSemaphore signal_semaphore = nullptr, VkSemaphore wait_semaphore = nullptr);
41 41
42 /// Waits for the worker thread to finish executing everything. After this function returns it's 42 /// Waits for the worker thread to finish executing everything. After this function returns it's
43 /// safe to touch worker resources. 43 /// safe to touch worker resources.
@@ -191,7 +191,7 @@ private:
191 191
192 void AllocateWorkerCommandBuffer(); 192 void AllocateWorkerCommandBuffer();
193 193
194 void SubmitExecution(VkSemaphore semaphore); 194 void SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore);
195 195
196 void AllocateNewContext(); 196 void AllocateNewContext();
197 197
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h
index 5f78f6950..d90935f52 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.h
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.h
@@ -110,10 +110,6 @@ public:
110 return Exchange(Dirty::DepthTestEnable, false); 110 return Exchange(Dirty::DepthTestEnable, false);
111 } 111 }
112 112
113 bool TouchDepthBoundsEnable() {
114 return Exchange(Dirty::DepthBoundsEnable, false);
115 }
116
117 bool TouchDepthWriteEnable() { 113 bool TouchDepthWriteEnable() {
118 return Exchange(Dirty::DepthWriteEnable, false); 114 return Exchange(Dirty::DepthWriteEnable, false);
119 } 115 }
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp
index d990eefba..aadf03cb0 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.cpp
+++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp
@@ -20,16 +20,15 @@ namespace Vulkan {
20 20
21namespace { 21namespace {
22 22
23VkSurfaceFormatKHR ChooseSwapSurfaceFormat(vk::Span<VkSurfaceFormatKHR> formats, bool srgb) { 23VkSurfaceFormatKHR ChooseSwapSurfaceFormat(vk::Span<VkSurfaceFormatKHR> formats) {
24 if (formats.size() == 1 && formats[0].format == VK_FORMAT_UNDEFINED) { 24 if (formats.size() == 1 && formats[0].format == VK_FORMAT_UNDEFINED) {
25 VkSurfaceFormatKHR format; 25 VkSurfaceFormatKHR format;
26 format.format = VK_FORMAT_B8G8R8A8_UNORM; 26 format.format = VK_FORMAT_B8G8R8A8_UNORM;
27 format.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; 27 format.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
28 return format; 28 return format;
29 } 29 }
30 const auto& found = std::find_if(formats.begin(), formats.end(), [srgb](const auto& format) { 30 const auto& found = std::find_if(formats.begin(), formats.end(), [](const auto& format) {
31 const auto request_format = srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM; 31 return format.format == VK_FORMAT_B8G8R8A8_UNORM &&
32 return format.format == request_format &&
33 format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; 32 format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
34 }); 33 });
35 return found != formats.end() ? *found : formats[0]; 34 return found != formats.end() ? *found : formats[0];
@@ -107,14 +106,12 @@ void VKSwapchain::AcquireNextImage() {
107} 106}
108 107
109void VKSwapchain::Present(VkSemaphore render_semaphore) { 108void VKSwapchain::Present(VkSemaphore render_semaphore) {
110 const VkSemaphore present_semaphore{*present_semaphores[frame_index]};
111 const std::array<VkSemaphore, 2> semaphores{present_semaphore, render_semaphore};
112 const auto present_queue{device.GetPresentQueue()}; 109 const auto present_queue{device.GetPresentQueue()};
113 const VkPresentInfoKHR present_info{ 110 const VkPresentInfoKHR present_info{
114 .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, 111 .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
115 .pNext = nullptr, 112 .pNext = nullptr,
116 .waitSemaphoreCount = render_semaphore ? 2U : 1U, 113 .waitSemaphoreCount = render_semaphore ? 1U : 0U,
117 .pWaitSemaphores = semaphores.data(), 114 .pWaitSemaphores = &render_semaphore,
118 .swapchainCount = 1, 115 .swapchainCount = 1,
119 .pSwapchains = swapchain.address(), 116 .pSwapchains = swapchain.address(),
120 .pImageIndices = &image_index, 117 .pImageIndices = &image_index,
@@ -145,7 +142,7 @@ void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities,
145 const auto formats{physical_device.GetSurfaceFormatsKHR(surface)}; 142 const auto formats{physical_device.GetSurfaceFormatsKHR(surface)};
146 const auto present_modes{physical_device.GetSurfacePresentModesKHR(surface)}; 143 const auto present_modes{physical_device.GetSurfacePresentModesKHR(surface)};
147 144
148 const VkSurfaceFormatKHR surface_format{ChooseSwapSurfaceFormat(formats, srgb)}; 145 const VkSurfaceFormatKHR surface_format{ChooseSwapSurfaceFormat(formats)};
149 const VkPresentModeKHR present_mode{ChooseSwapPresentMode(present_modes)}; 146 const VkPresentModeKHR present_mode{ChooseSwapPresentMode(present_modes)};
150 147
151 u32 requested_image_count{capabilities.minImageCount + 1}; 148 u32 requested_image_count{capabilities.minImageCount + 1};
@@ -180,6 +177,17 @@ void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities,
180 swapchain_ci.queueFamilyIndexCount = static_cast<u32>(queue_indices.size()); 177 swapchain_ci.queueFamilyIndexCount = static_cast<u32>(queue_indices.size());
181 swapchain_ci.pQueueFamilyIndices = queue_indices.data(); 178 swapchain_ci.pQueueFamilyIndices = queue_indices.data();
182 } 179 }
180 static constexpr std::array view_formats{VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SRGB};
181 VkImageFormatListCreateInfo format_list{
182 .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR,
183 .pNext = nullptr,
184 .viewFormatCount = static_cast<u32>(view_formats.size()),
185 .pViewFormats = view_formats.data(),
186 };
187 if (device.IsKhrSwapchainMutableFormatEnabled()) {
188 format_list.pNext = std::exchange(swapchain_ci.pNext, &format_list);
189 swapchain_ci.flags |= VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR;
190 }
183 // Request the size again to reduce the possibility of a TOCTOU race condition. 191 // Request the size again to reduce the possibility of a TOCTOU race condition.
184 const auto updated_capabilities = physical_device.GetSurfaceCapabilitiesKHR(surface); 192 const auto updated_capabilities = physical_device.GetSurfaceCapabilitiesKHR(surface);
185 swapchain_ci.imageExtent = ChooseSwapExtent(updated_capabilities, width, height); 193 swapchain_ci.imageExtent = ChooseSwapExtent(updated_capabilities, width, height);
@@ -191,7 +199,7 @@ void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities,
191 199
192 images = swapchain.GetImages(); 200 images = swapchain.GetImages();
193 image_count = static_cast<u32>(images.size()); 201 image_count = static_cast<u32>(images.size());
194 image_format = surface_format.format; 202 image_view_format = srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM;
195} 203}
196 204
197void VKSwapchain::CreateSemaphores() { 205void VKSwapchain::CreateSemaphores() {
@@ -207,7 +215,7 @@ void VKSwapchain::CreateImageViews() {
207 .flags = 0, 215 .flags = 0,
208 .image = {}, 216 .image = {},
209 .viewType = VK_IMAGE_VIEW_TYPE_2D, 217 .viewType = VK_IMAGE_VIEW_TYPE_2D,
210 .format = image_format, 218 .format = image_view_format,
211 .components = 219 .components =
212 { 220 {
213 .r = VK_COMPONENT_SWIZZLE_IDENTITY, 221 .r = VK_COMPONENT_SWIZZLE_IDENTITY,
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h
index 35c2cdc14..5bce41e21 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.h
+++ b/src/video_core/renderer_vulkan/vk_swapchain.h
@@ -68,8 +68,12 @@ public:
68 return *image_views[index]; 68 return *image_views[index];
69 } 69 }
70 70
71 VkFormat GetImageFormat() const { 71 VkFormat GetImageViewFormat() const {
72 return image_format; 72 return image_view_format;
73 }
74
75 VkSemaphore CurrentPresentSemaphore() const {
76 return *present_semaphores[frame_index];
73 } 77 }
74 78
75private: 79private:
@@ -96,7 +100,7 @@ private:
96 u32 image_index{}; 100 u32 image_index{};
97 u32 frame_index{}; 101 u32 frame_index{};
98 102
99 VkFormat image_format{}; 103 VkFormat image_view_format{};
100 VkExtent2D extent{}; 104 VkExtent2D extent{};
101 105
102 bool current_srgb{}; 106 bool current_srgb{};
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 8f4df7122..ff979a7ac 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -1186,9 +1186,12 @@ Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM
1186 renderpass_key.depth_format = depth_buffer->format; 1186 renderpass_key.depth_format = depth_buffer->format;
1187 num_layers = std::max(num_layers, depth_buffer->range.extent.layers); 1187 num_layers = std::max(num_layers, depth_buffer->range.extent.layers);
1188 images[num_images] = depth_buffer->ImageHandle(); 1188 images[num_images] = depth_buffer->ImageHandle();
1189 image_ranges[num_images] = MakeSubresourceRange(depth_buffer); 1189 const VkImageSubresourceRange subresource_range = MakeSubresourceRange(depth_buffer);
1190 image_ranges[num_images] = subresource_range;
1190 samples = depth_buffer->Samples(); 1191 samples = depth_buffer->Samples();
1191 ++num_images; 1192 ++num_images;
1193 has_depth = (subresource_range.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0;
1194 has_stencil = (subresource_range.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0;
1192 } else { 1195 } else {
1193 renderpass_key.depth_format = PixelFormat::Invalid; 1196 renderpass_key.depth_format = PixelFormat::Invalid;
1194 } 1197 }
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index 5fe6b7ba3..6d5a68bfe 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -232,6 +232,18 @@ public:
232 return image_ranges; 232 return image_ranges;
233 } 233 }
234 234
235 [[nodiscard]] bool HasAspectColorBit(size_t index) const noexcept {
236 return (image_ranges.at(index).aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) != 0;
237 }
238
239 [[nodiscard]] bool HasAspectDepthBit() const noexcept {
240 return has_depth;
241 }
242
243 [[nodiscard]] bool HasAspectStencilBit() const noexcept {
244 return has_stencil;
245 }
246
235private: 247private:
236 vk::Framebuffer framebuffer; 248 vk::Framebuffer framebuffer;
237 VkRenderPass renderpass{}; 249 VkRenderPass renderpass{};
@@ -241,6 +253,8 @@ private:
241 u32 num_images = 0; 253 u32 num_images = 0;
242 std::array<VkImage, 9> images{}; 254 std::array<VkImage, 9> images{};
243 std::array<VkImageSubresourceRange, 9> image_ranges{}; 255 std::array<VkImageSubresourceRange, 9> image_ranges{};
256 bool has_depth{};
257 bool has_stencil{};
244}; 258};
245 259
246struct TextureCacheParams { 260struct TextureCacheParams {
diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp
index 8a4581c19..81a878bb2 100644
--- a/src/video_core/shader_environment.cpp
+++ b/src/video_core/shader_environment.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
5#include <filesystem> 6#include <filesystem>
6#include <fstream> 7#include <fstream>
7#include <memory> 8#include <memory>
diff --git a/src/video_core/texture_cache/image_base.h b/src/video_core/texture_cache/image_base.h
index ff1feda9b..0c17a791b 100644
--- a/src/video_core/texture_cache/image_base.h
+++ b/src/video_core/texture_cache/image_base.h
@@ -80,7 +80,7 @@ struct ImageBase {
80 VAddr cpu_addr_end = 0; 80 VAddr cpu_addr_end = 0;
81 81
82 u64 modification_tick = 0; 82 u64 modification_tick = 0;
83 u64 frame_tick = 0; 83 size_t lru_index = SIZE_MAX;
84 84
85 std::array<u32, MAX_MIP_LEVELS> mip_level_offsets{}; 85 std::array<u32, MAX_MIP_LEVELS> mip_level_offsets{};
86 86
diff --git a/src/video_core/texture_cache/slot_vector.h b/src/video_core/texture_cache/slot_vector.h
index 6180b8c0e..74cd3c9d8 100644
--- a/src/video_core/texture_cache/slot_vector.h
+++ b/src/video_core/texture_cache/slot_vector.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 <bit> 9#include <bit>
9#include <concepts> 10#include <concepts>
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index a087498ff..24b809242 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include "common/alignment.h" 7#include "common/alignment.h"
8#include "common/settings.h"
9#include "video_core/dirty_flags.h" 8#include "video_core/dirty_flags.h"
10#include "video_core/texture_cache/samples_helper.h" 9#include "video_core/texture_cache/samples_helper.h"
11#include "video_core/texture_cache/texture_cache_base.h" 10#include "video_core/texture_cache/texture_cache_base.h"
@@ -43,8 +42,6 @@ TextureCache<P>::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface&
43 void(slot_image_views.insert(runtime, NullImageParams{})); 42 void(slot_image_views.insert(runtime, NullImageParams{}));
44 void(slot_samplers.insert(runtime, sampler_descriptor)); 43 void(slot_samplers.insert(runtime, sampler_descriptor));
45 44
46 deletion_iterator = slot_images.begin();
47
48 if constexpr (HAS_DEVICE_MEMORY_INFO) { 45 if constexpr (HAS_DEVICE_MEMORY_INFO) {
49 const auto device_memory = runtime.GetDeviceLocalMemory(); 46 const auto device_memory = runtime.GetDeviceLocalMemory();
50 const u64 possible_expected_memory = (device_memory * 3) / 10; 47 const u64 possible_expected_memory = (device_memory * 3) / 10;
@@ -64,70 +61,38 @@ template <class P>
64void TextureCache<P>::RunGarbageCollector() { 61void TextureCache<P>::RunGarbageCollector() {
65 const bool high_priority_mode = total_used_memory >= expected_memory; 62 const bool high_priority_mode = total_used_memory >= expected_memory;
66 const bool aggressive_mode = total_used_memory >= critical_memory; 63 const bool aggressive_mode = total_used_memory >= critical_memory;
67 const u64 ticks_to_destroy = high_priority_mode ? 60 : 100; 64 const u64 ticks_to_destroy = aggressive_mode ? 10ULL : high_priority_mode ? 25ULL : 100ULL;
68 int num_iterations = aggressive_mode ? 256 : (high_priority_mode ? 128 : 64); 65 size_t num_iterations = aggressive_mode ? 10000 : (high_priority_mode ? 100 : 5);
69 for (; num_iterations > 0; --num_iterations) { 66 const auto clean_up = [this, &num_iterations, high_priority_mode](ImageId image_id) {
70 if (deletion_iterator == slot_images.end()) { 67 if (num_iterations == 0) {
71 deletion_iterator = slot_images.begin(); 68 return true;
72 if (deletion_iterator == slot_images.end()) {
73 break;
74 }
75 } 69 }
76 auto [image_id, image_tmp] = *deletion_iterator; 70 --num_iterations;
77 Image* image = image_tmp; // fix clang error. 71 auto& image = slot_images[image_id];
78 const bool is_alias = True(image->flags & ImageFlagBits::Alias); 72 const bool must_download = image.IsSafeDownload();
79 const bool is_bad_overlap = True(image->flags & ImageFlagBits::BadOverlap); 73 if (!high_priority_mode && must_download) {
80 const bool must_download = image->IsSafeDownload(); 74 return false;
81 bool should_care = is_bad_overlap || is_alias || (high_priority_mode && !must_download);
82 const u64 ticks_needed =
83 is_bad_overlap
84 ? ticks_to_destroy >> 4
85 : ((should_care && aggressive_mode) ? ticks_to_destroy >> 1 : ticks_to_destroy);
86 should_care |= aggressive_mode;
87 if (should_care && image->frame_tick + ticks_needed < frame_tick) {
88 if (is_bad_overlap) {
89 const bool overlap_check = std::ranges::all_of(
90 image->overlapping_images, [&, image](const ImageId& overlap_id) {
91 auto& overlap = slot_images[overlap_id];
92 return overlap.frame_tick >= image->frame_tick;
93 });
94 if (!overlap_check) {
95 ++deletion_iterator;
96 continue;
97 }
98 }
99 if (!is_bad_overlap && must_download) {
100 const bool alias_check = std::ranges::none_of(
101 image->aliased_images, [&, image](const AliasedImage& alias) {
102 auto& alias_image = slot_images[alias.id];
103 return (alias_image.frame_tick < image->frame_tick) ||
104 (alias_image.modification_tick < image->modification_tick);
105 });
106
107 if (alias_check) {
108 auto map = runtime.DownloadStagingBuffer(image->unswizzled_size_bytes);
109 const auto copies = FullDownloadCopies(image->info);
110 image->DownloadMemory(map, copies);
111 runtime.Finish();
112 SwizzleImage(gpu_memory, image->gpu_addr, image->info, copies, map.mapped_span);
113 }
114 }
115 if (True(image->flags & ImageFlagBits::Tracked)) {
116 UntrackImage(*image, image_id);
117 }
118 UnregisterImage(image_id);
119 DeleteImage(image_id);
120 if (is_bad_overlap) {
121 ++num_iterations;
122 }
123 } 75 }
124 ++deletion_iterator; 76 if (must_download) {
125 } 77 auto map = runtime.DownloadStagingBuffer(image.unswizzled_size_bytes);
78 const auto copies = FullDownloadCopies(image.info);
79 image.DownloadMemory(map, copies);
80 runtime.Finish();
81 SwizzleImage(gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span);
82 }
83 if (True(image.flags & ImageFlagBits::Tracked)) {
84 UntrackImage(image, image_id);
85 }
86 UnregisterImage(image_id);
87 DeleteImage(image_id);
88 return false;
89 };
90 lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, clean_up);
126} 91}
127 92
128template <class P> 93template <class P>
129void TextureCache<P>::TickFrame() { 94void TextureCache<P>::TickFrame() {
130 if (Settings::values.use_caches_gc.GetValue() && total_used_memory > minimum_memory) { 95 if (total_used_memory > minimum_memory) {
131 RunGarbageCollector(); 96 RunGarbageCollector();
132 } 97 }
133 sentenced_images.Tick(); 98 sentenced_images.Tick();
@@ -1078,6 +1043,8 @@ void TextureCache<P>::RegisterImage(ImageId image_id) {
1078 tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format); 1043 tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format);
1079 } 1044 }
1080 total_used_memory += Common::AlignUp(tentative_size, 1024); 1045 total_used_memory += Common::AlignUp(tentative_size, 1024);
1046 image.lru_index = lru_cache.Insert(image_id, frame_tick);
1047
1081 ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, 1048 ForEachGPUPage(image.gpu_addr, image.guest_size_bytes,
1082 [this, image_id](u64 page) { gpu_page_table[page].push_back(image_id); }); 1049 [this, image_id](u64 page) { gpu_page_table[page].push_back(image_id); });
1083 if (False(image.flags & ImageFlagBits::Sparse)) { 1050 if (False(image.flags & ImageFlagBits::Sparse)) {
@@ -1115,6 +1082,7 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
1115 tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format); 1082 tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format);
1116 } 1083 }
1117 total_used_memory -= Common::AlignUp(tentative_size, 1024); 1084 total_used_memory -= Common::AlignUp(tentative_size, 1024);
1085 lru_cache.Free(image.lru_index);
1118 const auto& clear_page_table = 1086 const auto& clear_page_table =
1119 [this, image_id]( 1087 [this, image_id](
1120 u64 page, 1088 u64 page,
@@ -1384,7 +1352,7 @@ void TextureCache<P>::PrepareImage(ImageId image_id, bool is_modification, bool
1384 if (is_modification) { 1352 if (is_modification) {
1385 MarkModification(image); 1353 MarkModification(image);
1386 } 1354 }
1387 image.frame_tick = frame_tick; 1355 lru_cache.Touch(image.lru_index, frame_tick);
1388} 1356}
1389 1357
1390template <class P> 1358template <class P>
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index e4ae351cb..d7528ed24 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -14,6 +14,7 @@
14 14
15#include "common/common_types.h" 15#include "common/common_types.h"
16#include "common/literals.h" 16#include "common/literals.h"
17#include "common/lru_cache.h"
17#include "video_core/compatible_formats.h" 18#include "video_core/compatible_formats.h"
18#include "video_core/delayed_destruction_ring.h" 19#include "video_core/delayed_destruction_ring.h"
19#include "video_core/engines/fermi_2d.h" 20#include "video_core/engines/fermi_2d.h"
@@ -370,6 +371,12 @@ private:
370 std::vector<ImageId> uncommitted_downloads; 371 std::vector<ImageId> uncommitted_downloads;
371 std::queue<std::vector<ImageId>> committed_downloads; 372 std::queue<std::vector<ImageId>> committed_downloads;
372 373
374 struct LRUItemParams {
375 using ObjectType = ImageId;
376 using TickType = u64;
377 };
378 Common::LeastRecentlyUsedCache<LRUItemParams> lru_cache;
379
373 static constexpr size_t TICKS_TO_DESTROY = 6; 380 static constexpr size_t TICKS_TO_DESTROY = 6;
374 DelayedDestructionRing<Image, TICKS_TO_DESTROY> sentenced_images; 381 DelayedDestructionRing<Image, TICKS_TO_DESTROY> sentenced_images;
375 DelayedDestructionRing<ImageView, TICKS_TO_DESTROY> sentenced_image_view; 382 DelayedDestructionRing<ImageView, TICKS_TO_DESTROY> sentenced_image_view;
@@ -379,7 +386,6 @@ private:
379 386
380 u64 modification_tick = 0; 387 u64 modification_tick = 0;
381 u64 frame_tick = 0; 388 u64 frame_tick = 0;
382 typename SlotVector<Image>::Iterator deletion_iterator;
383}; 389};
384 390
385} // namespace VideoCommon 391} // namespace VideoCommon
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index c010b9353..24e943e4c 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -63,14 +63,6 @@ void SwizzleImpl(std::span<u8> output, std::span<const u8> input, u32 width, u32
63 const u32 unswizzled_offset = 63 const u32 unswizzled_offset =
64 slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL; 64 slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL;
65 65
66 if (const auto offset = (TO_LINEAR ? unswizzled_offset : swizzled_offset);
67 offset >= input.size()) {
68 // TODO(Rodrigo): This is an out of bounds access that should never happen. To
69 // avoid crashing the emulator, break.
70 ASSERT_MSG(false, "offset {} exceeds input size {}!", offset, input.size());
71 break;
72 }
73
74 u8* const dst = &output[TO_LINEAR ? swizzled_offset : unswizzled_offset]; 66 u8* const dst = &output[TO_LINEAR ? swizzled_offset : unswizzled_offset];
75 const u8* const src = &input[TO_LINEAR ? unswizzled_offset : swizzled_offset]; 67 const u8* const src = &input[TO_LINEAR ? unswizzled_offset : swizzled_offset];
76 68
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index 3b575db4d..cae543a51 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -37,7 +37,8 @@ std::unique_ptr<VideoCore::RendererBase> CreateRenderer(
37namespace VideoCore { 37namespace VideoCore {
38 38
39std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) { 39std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) {
40 const bool use_nvdec = Settings::values.use_nvdec_emulation.GetValue(); 40 const auto nvdec_value = Settings::values.nvdec_emulation.GetValue();
41 const bool use_nvdec = nvdec_value != Settings::NvdecEmulation::Off;
41 const bool use_async = Settings::values.use_asynchronous_gpu_emulation.GetValue(); 42 const bool use_async = Settings::values.use_asynchronous_gpu_emulation.GetValue();
42 auto gpu = std::make_unique<Tegra::GPU>(system, use_async, use_nvdec); 43 auto gpu = std::make_unique<Tegra::GPU>(system, use_async, use_nvdec);
43 auto context = emu_window.CreateSharedContext(); 44 auto context = emu_window.CreateSharedContext();
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 5662a7bdc..40b7ea90f 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -845,6 +845,8 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
845 bool has_khr_shader_float16_int8{}; 845 bool has_khr_shader_float16_int8{};
846 bool has_khr_workgroup_memory_explicit_layout{}; 846 bool has_khr_workgroup_memory_explicit_layout{};
847 bool has_khr_pipeline_executable_properties{}; 847 bool has_khr_pipeline_executable_properties{};
848 bool has_khr_image_format_list{};
849 bool has_khr_swapchain_mutable_format{};
848 bool has_ext_subgroup_size_control{}; 850 bool has_ext_subgroup_size_control{};
849 bool has_ext_transform_feedback{}; 851 bool has_ext_transform_feedback{};
850 bool has_ext_custom_border_color{}; 852 bool has_ext_custom_border_color{};
@@ -894,6 +896,9 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
894 test(has_ext_shader_atomic_int64, VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME, false); 896 test(has_ext_shader_atomic_int64, VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME, false);
895 test(has_khr_workgroup_memory_explicit_layout, 897 test(has_khr_workgroup_memory_explicit_layout,
896 VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME, false); 898 VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME, false);
899 test(has_khr_image_format_list, VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME, false);
900 test(has_khr_swapchain_mutable_format, VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME,
901 false);
897 test(has_ext_line_rasterization, VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME, false); 902 test(has_ext_line_rasterization, VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME, false);
898 if (Settings::values.enable_nsight_aftermath) { 903 if (Settings::values.enable_nsight_aftermath) {
899 test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, 904 test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME,
@@ -1072,6 +1077,11 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
1072 khr_pipeline_executable_properties = true; 1077 khr_pipeline_executable_properties = true;
1073 } 1078 }
1074 } 1079 }
1080 if (has_khr_image_format_list && has_khr_swapchain_mutable_format) {
1081 extensions.push_back(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME);
1082 extensions.push_back(VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME);
1083 khr_swapchain_mutable_format = true;
1084 }
1075 if (khr_push_descriptor) { 1085 if (khr_push_descriptor) {
1076 VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor; 1086 VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor;
1077 push_descriptor.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR; 1087 push_descriptor.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR;
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index 9d1cb9181..bc180a32a 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -224,6 +224,11 @@ public:
224 return khr_pipeline_executable_properties; 224 return khr_pipeline_executable_properties;
225 } 225 }
226 226
227 /// Returns true if VK_KHR_swapchain_mutable_format is enabled.
228 bool IsKhrSwapchainMutableFormatEnabled() const {
229 return khr_swapchain_mutable_format;
230 }
231
227 /// Returns true if the device supports VK_KHR_workgroup_memory_explicit_layout. 232 /// Returns true if the device supports VK_KHR_workgroup_memory_explicit_layout.
228 bool IsKhrWorkgroupMemoryExplicitLayoutSupported() const { 233 bool IsKhrWorkgroupMemoryExplicitLayoutSupported() const {
229 return khr_workgroup_memory_explicit_layout; 234 return khr_workgroup_memory_explicit_layout;
@@ -395,6 +400,7 @@ private:
395 bool khr_workgroup_memory_explicit_layout{}; ///< Support for explicit workgroup layouts. 400 bool khr_workgroup_memory_explicit_layout{}; ///< Support for explicit workgroup layouts.
396 bool khr_push_descriptor{}; ///< Support for VK_KHR_push_descritor. 401 bool khr_push_descriptor{}; ///< Support for VK_KHR_push_descritor.
397 bool khr_pipeline_executable_properties{}; ///< Support for executable properties. 402 bool khr_pipeline_executable_properties{}; ///< Support for executable properties.
403 bool khr_swapchain_mutable_format{}; ///< Support for VK_KHR_swapchain_mutable_format.
398 bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8. 404 bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8.
399 bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax. 405 bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax.
400 bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted. 406 bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted.
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 377795326..8744d8e5d 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -812,13 +812,12 @@ void Config::ReadRendererValues() {
812 ReadGlobalSetting(Settings::values.use_disk_shader_cache); 812 ReadGlobalSetting(Settings::values.use_disk_shader_cache);
813 ReadGlobalSetting(Settings::values.gpu_accuracy); 813 ReadGlobalSetting(Settings::values.gpu_accuracy);
814 ReadGlobalSetting(Settings::values.use_asynchronous_gpu_emulation); 814 ReadGlobalSetting(Settings::values.use_asynchronous_gpu_emulation);
815 ReadGlobalSetting(Settings::values.use_nvdec_emulation); 815 ReadGlobalSetting(Settings::values.nvdec_emulation);
816 ReadGlobalSetting(Settings::values.accelerate_astc); 816 ReadGlobalSetting(Settings::values.accelerate_astc);
817 ReadGlobalSetting(Settings::values.use_vsync); 817 ReadGlobalSetting(Settings::values.use_vsync);
818 ReadGlobalSetting(Settings::values.shader_backend); 818 ReadGlobalSetting(Settings::values.shader_backend);
819 ReadGlobalSetting(Settings::values.use_asynchronous_shaders); 819 ReadGlobalSetting(Settings::values.use_asynchronous_shaders);
820 ReadGlobalSetting(Settings::values.use_fast_gpu_time); 820 ReadGlobalSetting(Settings::values.use_fast_gpu_time);
821 ReadGlobalSetting(Settings::values.use_caches_gc);
822 ReadGlobalSetting(Settings::values.bg_red); 821 ReadGlobalSetting(Settings::values.bg_red);
823 ReadGlobalSetting(Settings::values.bg_green); 822 ReadGlobalSetting(Settings::values.bg_green);
824 ReadGlobalSetting(Settings::values.bg_blue); 823 ReadGlobalSetting(Settings::values.bg_blue);
@@ -1350,7 +1349,10 @@ void Config::SaveRendererValues() {
1350 static_cast<u32>(Settings::values.gpu_accuracy.GetDefault()), 1349 static_cast<u32>(Settings::values.gpu_accuracy.GetDefault()),
1351 Settings::values.gpu_accuracy.UsingGlobal()); 1350 Settings::values.gpu_accuracy.UsingGlobal());
1352 WriteGlobalSetting(Settings::values.use_asynchronous_gpu_emulation); 1351 WriteGlobalSetting(Settings::values.use_asynchronous_gpu_emulation);
1353 WriteGlobalSetting(Settings::values.use_nvdec_emulation); 1352 WriteSetting(QString::fromStdString(Settings::values.nvdec_emulation.GetLabel()),
1353 static_cast<u32>(Settings::values.nvdec_emulation.GetValue(global)),
1354 static_cast<u32>(Settings::values.nvdec_emulation.GetDefault()),
1355 Settings::values.nvdec_emulation.UsingGlobal());
1354 WriteGlobalSetting(Settings::values.accelerate_astc); 1356 WriteGlobalSetting(Settings::values.accelerate_astc);
1355 WriteGlobalSetting(Settings::values.use_vsync); 1357 WriteGlobalSetting(Settings::values.use_vsync);
1356 WriteSetting(QString::fromStdString(Settings::values.shader_backend.GetLabel()), 1358 WriteSetting(QString::fromStdString(Settings::values.shader_backend.GetLabel()),
@@ -1359,7 +1361,6 @@ void Config::SaveRendererValues() {
1359 Settings::values.shader_backend.UsingGlobal()); 1361 Settings::values.shader_backend.UsingGlobal());
1360 WriteGlobalSetting(Settings::values.use_asynchronous_shaders); 1362 WriteGlobalSetting(Settings::values.use_asynchronous_shaders);
1361 WriteGlobalSetting(Settings::values.use_fast_gpu_time); 1363 WriteGlobalSetting(Settings::values.use_fast_gpu_time);
1362 WriteGlobalSetting(Settings::values.use_caches_gc);
1363 WriteGlobalSetting(Settings::values.bg_red); 1364 WriteGlobalSetting(Settings::values.bg_red);
1364 WriteGlobalSetting(Settings::values.bg_green); 1365 WriteGlobalSetting(Settings::values.bg_green);
1365 WriteGlobalSetting(Settings::values.bg_blue); 1366 WriteGlobalSetting(Settings::values.bg_blue);
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index 9555f4498..4733227b6 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -182,5 +182,6 @@ private:
182Q_DECLARE_METATYPE(Settings::CPUAccuracy); 182Q_DECLARE_METATYPE(Settings::CPUAccuracy);
183Q_DECLARE_METATYPE(Settings::GPUAccuracy); 183Q_DECLARE_METATYPE(Settings::GPUAccuracy);
184Q_DECLARE_METATYPE(Settings::FullscreenMode); 184Q_DECLARE_METATYPE(Settings::FullscreenMode);
185Q_DECLARE_METATYPE(Settings::NvdecEmulation);
185Q_DECLARE_METATYPE(Settings::RendererBackend); 186Q_DECLARE_METATYPE(Settings::RendererBackend);
186Q_DECLARE_METATYPE(Settings::ShaderBackend); 187Q_DECLARE_METATYPE(Settings::ShaderBackend);
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 37e896258..c594164be 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -88,24 +88,30 @@ void ConfigureGraphics::SetConfiguration() {
88 ui->api_widget->setEnabled(runtime_lock); 88 ui->api_widget->setEnabled(runtime_lock);
89 ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock); 89 ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock);
90 ui->use_disk_shader_cache->setEnabled(runtime_lock); 90 ui->use_disk_shader_cache->setEnabled(runtime_lock);
91 ui->use_nvdec_emulation->setEnabled(runtime_lock); 91 ui->nvdec_emulation_widget->setEnabled(runtime_lock);
92 ui->accelerate_astc->setEnabled(runtime_lock); 92 ui->accelerate_astc->setEnabled(runtime_lock);
93 ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue()); 93 ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue());
94 ui->use_asynchronous_gpu_emulation->setChecked( 94 ui->use_asynchronous_gpu_emulation->setChecked(
95 Settings::values.use_asynchronous_gpu_emulation.GetValue()); 95 Settings::values.use_asynchronous_gpu_emulation.GetValue());
96 ui->use_nvdec_emulation->setChecked(Settings::values.use_nvdec_emulation.GetValue());
97 ui->accelerate_astc->setChecked(Settings::values.accelerate_astc.GetValue()); 96 ui->accelerate_astc->setChecked(Settings::values.accelerate_astc.GetValue());
98 97
99 if (Settings::IsConfiguringGlobal()) { 98 if (Settings::IsConfiguringGlobal()) {
100 ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue())); 99 ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue()));
101 ui->fullscreen_mode_combobox->setCurrentIndex( 100 ui->fullscreen_mode_combobox->setCurrentIndex(
102 static_cast<int>(Settings::values.fullscreen_mode.GetValue())); 101 static_cast<int>(Settings::values.fullscreen_mode.GetValue()));
102 ui->nvdec_emulation->setCurrentIndex(
103 static_cast<int>(Settings::values.nvdec_emulation.GetValue()));
103 ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue()); 104 ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue());
104 } else { 105 } else {
105 ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend); 106 ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend);
106 ConfigurationShared::SetHighlight(ui->api_widget, 107 ConfigurationShared::SetHighlight(ui->api_widget,
107 !Settings::values.renderer_backend.UsingGlobal()); 108 !Settings::values.renderer_backend.UsingGlobal());
108 109
110 ConfigurationShared::SetPerGameSetting(ui->nvdec_emulation,
111 &Settings::values.nvdec_emulation);
112 ConfigurationShared::SetHighlight(ui->nvdec_emulation_widget,
113 !Settings::values.nvdec_emulation.UsingGlobal());
114
109 ConfigurationShared::SetPerGameSetting(ui->fullscreen_mode_combobox, 115 ConfigurationShared::SetPerGameSetting(ui->fullscreen_mode_combobox,
110 &Settings::values.fullscreen_mode); 116 &Settings::values.fullscreen_mode);
111 ConfigurationShared::SetHighlight(ui->fullscreen_mode_label, 117 ConfigurationShared::SetHighlight(ui->fullscreen_mode_label,
@@ -137,8 +143,6 @@ void ConfigureGraphics::ApplyConfiguration() {
137 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_gpu_emulation, 143 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_gpu_emulation,
138 ui->use_asynchronous_gpu_emulation, 144 ui->use_asynchronous_gpu_emulation,
139 use_asynchronous_gpu_emulation); 145 use_asynchronous_gpu_emulation);
140 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_nvdec_emulation,
141 ui->use_nvdec_emulation, use_nvdec_emulation);
142 ConfigurationShared::ApplyPerGameSetting(&Settings::values.accelerate_astc, ui->accelerate_astc, 146 ConfigurationShared::ApplyPerGameSetting(&Settings::values.accelerate_astc, ui->accelerate_astc,
143 accelerate_astc); 147 accelerate_astc);
144 148
@@ -147,6 +151,9 @@ void ConfigureGraphics::ApplyConfiguration() {
147 if (Settings::values.renderer_backend.UsingGlobal()) { 151 if (Settings::values.renderer_backend.UsingGlobal()) {
148 Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend()); 152 Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend());
149 } 153 }
154 if (Settings::values.nvdec_emulation.UsingGlobal()) {
155 Settings::values.nvdec_emulation.SetValue(GetCurrentNvdecEmulation());
156 }
150 if (Settings::values.shader_backend.UsingGlobal()) { 157 if (Settings::values.shader_backend.UsingGlobal()) {
151 Settings::values.shader_backend.SetValue(shader_backend); 158 Settings::values.shader_backend.SetValue(shader_backend);
152 } 159 }
@@ -180,6 +187,13 @@ void ConfigureGraphics::ApplyConfiguration() {
180 } 187 }
181 } 188 }
182 189
190 if (ui->nvdec_emulation->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
191 Settings::values.nvdec_emulation.SetGlobal(true);
192 } else {
193 Settings::values.nvdec_emulation.SetGlobal(false);
194 Settings::values.nvdec_emulation.SetValue(GetCurrentNvdecEmulation());
195 }
196
183 if (ui->bg_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { 197 if (ui->bg_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
184 Settings::values.bg_red.SetGlobal(true); 198 Settings::values.bg_red.SetGlobal(true);
185 Settings::values.bg_green.SetGlobal(true); 199 Settings::values.bg_green.SetGlobal(true);
@@ -278,6 +292,20 @@ Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
278 ConfigurationShared::USE_GLOBAL_OFFSET); 292 ConfigurationShared::USE_GLOBAL_OFFSET);
279} 293}
280 294
295Settings::NvdecEmulation ConfigureGraphics::GetCurrentNvdecEmulation() const {
296 if (Settings::IsConfiguringGlobal()) {
297 return static_cast<Settings::NvdecEmulation>(ui->nvdec_emulation->currentIndex());
298 }
299
300 if (ui->nvdec_emulation->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
301 Settings::values.nvdec_emulation.SetGlobal(true);
302 return Settings::values.nvdec_emulation.GetValue();
303 }
304 Settings::values.nvdec_emulation.SetGlobal(false);
305 return static_cast<Settings::NvdecEmulation>(ui->nvdec_emulation->currentIndex() -
306 ConfigurationShared::USE_GLOBAL_OFFSET);
307}
308
281void ConfigureGraphics::SetupPerGameUI() { 309void ConfigureGraphics::SetupPerGameUI() {
282 if (Settings::IsConfiguringGlobal()) { 310 if (Settings::IsConfiguringGlobal()) {
283 ui->api->setEnabled(Settings::values.renderer_backend.UsingGlobal()); 311 ui->api->setEnabled(Settings::values.renderer_backend.UsingGlobal());
@@ -286,7 +314,7 @@ void ConfigureGraphics::SetupPerGameUI() {
286 ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal()); 314 ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal());
287 ui->use_asynchronous_gpu_emulation->setEnabled( 315 ui->use_asynchronous_gpu_emulation->setEnabled(
288 Settings::values.use_asynchronous_gpu_emulation.UsingGlobal()); 316 Settings::values.use_asynchronous_gpu_emulation.UsingGlobal());
289 ui->use_nvdec_emulation->setEnabled(Settings::values.use_nvdec_emulation.UsingGlobal()); 317 ui->nvdec_emulation->setEnabled(Settings::values.nvdec_emulation.UsingGlobal());
290 ui->accelerate_astc->setEnabled(Settings::values.accelerate_astc.UsingGlobal()); 318 ui->accelerate_astc->setEnabled(Settings::values.accelerate_astc.UsingGlobal());
291 ui->use_disk_shader_cache->setEnabled(Settings::values.use_disk_shader_cache.UsingGlobal()); 319 ui->use_disk_shader_cache->setEnabled(Settings::values.use_disk_shader_cache.UsingGlobal());
292 ui->bg_button->setEnabled(Settings::values.bg_red.UsingGlobal()); 320 ui->bg_button->setEnabled(Settings::values.bg_red.UsingGlobal());
@@ -301,8 +329,6 @@ void ConfigureGraphics::SetupPerGameUI() {
301 329
302 ConfigurationShared::SetColoredTristate( 330 ConfigurationShared::SetColoredTristate(
303 ui->use_disk_shader_cache, Settings::values.use_disk_shader_cache, use_disk_shader_cache); 331 ui->use_disk_shader_cache, Settings::values.use_disk_shader_cache, use_disk_shader_cache);
304 ConfigurationShared::SetColoredTristate(
305 ui->use_nvdec_emulation, Settings::values.use_nvdec_emulation, use_nvdec_emulation);
306 ConfigurationShared::SetColoredTristate(ui->accelerate_astc, Settings::values.accelerate_astc, 332 ConfigurationShared::SetColoredTristate(ui->accelerate_astc, Settings::values.accelerate_astc,
307 accelerate_astc); 333 accelerate_astc);
308 ConfigurationShared::SetColoredTristate(ui->use_asynchronous_gpu_emulation, 334 ConfigurationShared::SetColoredTristate(ui->use_asynchronous_gpu_emulation,
@@ -316,4 +342,6 @@ void ConfigureGraphics::SetupPerGameUI() {
316 static_cast<int>(Settings::values.fullscreen_mode.GetValue(true))); 342 static_cast<int>(Settings::values.fullscreen_mode.GetValue(true)));
317 ConfigurationShared::InsertGlobalItem( 343 ConfigurationShared::InsertGlobalItem(
318 ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true))); 344 ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true)));
345 ConfigurationShared::InsertGlobalItem(
346 ui->nvdec_emulation, static_cast<int>(Settings::values.nvdec_emulation.GetValue(true)));
319} 347}
diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h
index c866b911b..7d7ac329d 100644
--- a/src/yuzu/configuration/configure_graphics.h
+++ b/src/yuzu/configuration/configure_graphics.h
@@ -43,6 +43,7 @@ private:
43 void SetupPerGameUI(); 43 void SetupPerGameUI();
44 44
45 Settings::RendererBackend GetCurrentGraphicsBackend() const; 45 Settings::RendererBackend GetCurrentGraphicsBackend() const;
46 Settings::NvdecEmulation GetCurrentNvdecEmulation() const;
46 47
47 std::unique_ptr<Ui::ConfigureGraphics> ui; 48 std::unique_ptr<Ui::ConfigureGraphics> ui;
48 QColor bg_color; 49 QColor bg_color;
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index 099ddbb7c..1a12cfa4d 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -156,7 +156,7 @@
156 <item> 156 <item>
157 <widget class="QCheckBox" name="use_disk_shader_cache"> 157 <widget class="QCheckBox" name="use_disk_shader_cache">
158 <property name="text"> 158 <property name="text">
159 <string>Use disk shader cache</string> 159 <string>Use disk pipeline cache</string>
160 </property> 160 </property>
161 </widget> 161 </widget>
162 </item> 162 </item>
@@ -168,13 +168,6 @@
168 </widget> 168 </widget>
169 </item> 169 </item>
170 <item> 170 <item>
171 <widget class="QCheckBox" name="use_nvdec_emulation">
172 <property name="text">
173 <string>Use NVDEC emulation</string>
174 </property>
175 </widget>
176 </item>
177 <item>
178 <widget class="QCheckBox" name="accelerate_astc"> 171 <widget class="QCheckBox" name="accelerate_astc">
179 <property name="text"> 172 <property name="text">
180 <string>Accelerate ASTC texture decoding</string> 173 <string>Accelerate ASTC texture decoding</string>
@@ -182,6 +175,50 @@
182 </widget> 175 </widget>
183 </item> 176 </item>
184 <item> 177 <item>
178 <widget class="QWidget" name="nvdec_emulation_widget" native="true">
179 <layout class="QHBoxLayout" name="nvdec_emulation_layout">
180 <property name="leftMargin">
181 <number>0</number>
182 </property>
183 <property name="topMargin">
184 <number>0</number>
185 </property>
186 <property name="rightMargin">
187 <number>0</number>
188 </property>
189 <property name="bottomMargin">
190 <number>0</number>
191 </property>
192 <item>
193 <widget class="QLabel" name="nvdec_emulation_label">
194 <property name="text">
195 <string>NVDEC emulation:</string>
196 </property>
197 </widget>
198 </item>
199 <item>
200 <widget class="QComboBox" name="nvdec_emulation">
201 <item>
202 <property name="text">
203 <string>Disabled</string>
204 </property>
205 </item>
206 <item>
207 <property name="text">
208 <string>CPU Decoding</string>
209 </property>
210 </item>
211 <item>
212 <property name="text">
213 <string>GPU Decoding</string>
214 </property>
215 </item>
216 </widget>
217 </item>
218 </layout>
219 </widget>
220 </item>
221 <item>
185 <widget class="QWidget" name="fullscreen_mode_layout" native="true"> 222 <widget class="QWidget" name="fullscreen_mode_layout" native="true">
186 <layout class="QHBoxLayout" name="horizontalLayout_1"> 223 <layout class="QHBoxLayout" name="horizontalLayout_1">
187 <property name="leftMargin"> 224 <property name="leftMargin">
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp
index a31b8e192..bfd464061 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.cpp
+++ b/src/yuzu/configuration/configure_graphics_advanced.cpp
@@ -28,7 +28,6 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
28 28
29 ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue()); 29 ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue());
30 ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue()); 30 ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue());
31 ui->use_caches_gc->setChecked(Settings::values.use_caches_gc.GetValue());
32 ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue()); 31 ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue());
33 32
34 if (Settings::IsConfiguringGlobal()) { 33 if (Settings::IsConfiguringGlobal()) {
@@ -55,8 +54,6 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
55 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders, 54 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders,
56 ui->use_asynchronous_shaders, 55 ui->use_asynchronous_shaders,
57 use_asynchronous_shaders); 56 use_asynchronous_shaders);
58 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_caches_gc, ui->use_caches_gc,
59 use_caches_gc);
60 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time, 57 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time,
61 ui->use_fast_gpu_time, use_fast_gpu_time); 58 ui->use_fast_gpu_time, use_fast_gpu_time);
62} 59}
@@ -81,7 +78,6 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
81 ui->use_asynchronous_shaders->setEnabled( 78 ui->use_asynchronous_shaders->setEnabled(
82 Settings::values.use_asynchronous_shaders.UsingGlobal()); 79 Settings::values.use_asynchronous_shaders.UsingGlobal());
83 ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal()); 80 ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal());
84 ui->use_caches_gc->setEnabled(Settings::values.use_caches_gc.UsingGlobal());
85 ui->anisotropic_filtering_combobox->setEnabled( 81 ui->anisotropic_filtering_combobox->setEnabled(
86 Settings::values.max_anisotropy.UsingGlobal()); 82 Settings::values.max_anisotropy.UsingGlobal());
87 83
@@ -94,8 +90,6 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
94 use_asynchronous_shaders); 90 use_asynchronous_shaders);
95 ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time, 91 ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time,
96 Settings::values.use_fast_gpu_time, use_fast_gpu_time); 92 Settings::values.use_fast_gpu_time, use_fast_gpu_time);
97 ConfigurationShared::SetColoredTristate(ui->use_caches_gc, Settings::values.use_caches_gc,
98 use_caches_gc);
99 ConfigurationShared::SetColoredComboBox( 93 ConfigurationShared::SetColoredComboBox(
100 ui->gpu_accuracy, ui->label_gpu_accuracy, 94 ui->gpu_accuracy, ui->label_gpu_accuracy,
101 static_cast<int>(Settings::values.gpu_accuracy.GetValue(true))); 95 static_cast<int>(Settings::values.gpu_accuracy.GetValue(true)));
diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h
index 7356e6916..13ba4ff6b 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.h
+++ b/src/yuzu/configuration/configure_graphics_advanced.h
@@ -37,5 +37,4 @@ private:
37 ConfigurationShared::CheckState use_vsync; 37 ConfigurationShared::CheckState use_vsync;
38 ConfigurationShared::CheckState use_asynchronous_shaders; 38 ConfigurationShared::CheckState use_asynchronous_shaders;
39 ConfigurationShared::CheckState use_fast_gpu_time; 39 ConfigurationShared::CheckState use_fast_gpu_time;
40 ConfigurationShared::CheckState use_caches_gc;
41}; 40};
diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui
index 4fe6b86ae..b91abc2f0 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.ui
+++ b/src/yuzu/configuration/configure_graphics_advanced.ui
@@ -82,7 +82,7 @@
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 (hack)</string> 85 <string>Use asynchronous shader building (Hack)</string>
86 </property> 86 </property>
87 </widget> 87 </widget>
88 </item> 88 </item>
@@ -92,17 +92,7 @@
92 <string>Enables Fast GPU Time. This option will force most games to run at their highest native resolution.</string> 92 <string>Enables Fast GPU Time. This option will force most games to run at their highest native resolution.</string>
93 </property> 93 </property>
94 <property name="text"> 94 <property name="text">
95 <string>Use Fast GPU Time (hack)</string> 95 <string>Use Fast GPU Time (Hack)</string>
96 </property>
97 </widget>
98 </item>
99 <item>
100 <widget class="QCheckBox" name="use_caches_gc">
101 <property name="toolTip">
102 <string>Enables garbage collection for the GPU caches, this will try to keep VRAM within 3-4 GB by flushing the least used textures/buffers. May cause issues in a few games.</string>
103 </property>
104 <property name="text">
105 <string>Enable GPU cache garbage collection (experimental)</string>
106 </property> 96 </property>
107 </widget> 97 </widget>
108 </item> 98 </item>
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index e97804220..f9d949e75 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -515,16 +515,16 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
515 QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location")); 515 QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location"));
516 QAction* open_mod_location = context_menu.addAction(tr("Open Mod Data Location")); 516 QAction* open_mod_location = context_menu.addAction(tr("Open Mod Data Location"));
517 QAction* open_transferable_shader_cache = 517 QAction* open_transferable_shader_cache =
518 context_menu.addAction(tr("Open Transferable Shader Cache")); 518 context_menu.addAction(tr("Open Transferable Pipeline Cache"));
519 context_menu.addSeparator(); 519 context_menu.addSeparator();
520 QMenu* remove_menu = context_menu.addMenu(tr("Remove")); 520 QMenu* remove_menu = context_menu.addMenu(tr("Remove"));
521 QAction* remove_update = remove_menu->addAction(tr("Remove Installed Update")); 521 QAction* remove_update = remove_menu->addAction(tr("Remove Installed Update"));
522 QAction* remove_dlc = remove_menu->addAction(tr("Remove All Installed DLC")); 522 QAction* remove_dlc = remove_menu->addAction(tr("Remove All Installed DLC"));
523 QAction* remove_custom_config = remove_menu->addAction(tr("Remove Custom Configuration")); 523 QAction* remove_custom_config = remove_menu->addAction(tr("Remove Custom Configuration"));
524 QAction* remove_gl_shader_cache = remove_menu->addAction(tr("Remove OpenGL Shader Cache")); 524 QAction* remove_gl_shader_cache = remove_menu->addAction(tr("Remove OpenGL Pipeline Cache"));
525 QAction* remove_vk_shader_cache = remove_menu->addAction(tr("Remove Vulkan Shader Cache")); 525 QAction* remove_vk_shader_cache = remove_menu->addAction(tr("Remove Vulkan Pipeline Cache"));
526 remove_menu->addSeparator(); 526 remove_menu->addSeparator();
527 QAction* remove_shader_cache = remove_menu->addAction(tr("Remove All Shader Caches")); 527 QAction* remove_shader_cache = remove_menu->addAction(tr("Remove All Pipeline Caches"));
528 QAction* remove_all_content = remove_menu->addAction(tr("Remove All Installed Contents")); 528 QAction* remove_all_content = remove_menu->addAction(tr("Remove All Installed Contents"));
529 QMenu* dump_romfs_menu = context_menu.addMenu(tr("Dump RomFS")); 529 QMenu* dump_romfs_menu = context_menu.addMenu(tr("Dump RomFS"));
530 QAction* dump_romfs = dump_romfs_menu->addAction(tr("Dump RomFS")); 530 QAction* dump_romfs = dump_romfs_menu->addAction(tr("Dump RomFS"));
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index e36774cc6..77d53e7bc 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -3174,12 +3174,11 @@ std::optional<u64> GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProv
3174} 3174}
3175 3175
3176bool GMainWindow::ConfirmClose() { 3176bool GMainWindow::ConfirmClose() {
3177 if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) 3177 if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) {
3178 return true; 3178 return true;
3179 3179 }
3180 QMessageBox::StandardButton answer = 3180 const auto text = tr("Are you sure you want to close yuzu?");
3181 QMessageBox::question(this, tr("yuzu"), tr("Are you sure you want to close yuzu?"), 3181 const auto answer = QMessageBox::question(this, tr("yuzu"), text);
3182 QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
3183 return answer != QMessageBox::No; 3182 return answer != QMessageBox::No;
3184} 3183}
3185 3184
@@ -3261,14 +3260,13 @@ bool GMainWindow::ConfirmChangeGame() {
3261} 3260}
3262 3261
3263bool GMainWindow::ConfirmForceLockedExit() { 3262bool GMainWindow::ConfirmForceLockedExit() {
3264 if (emu_thread == nullptr) 3263 if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) {
3265 return true; 3264 return true;
3265 }
3266 const auto text = tr("The currently running application has requested yuzu to not exit.\n\n"
3267 "Would you like to bypass this and exit anyway?");
3266 3268
3267 const auto answer = 3269 const auto answer = QMessageBox::question(this, tr("yuzu"), text);
3268 QMessageBox::question(this, tr("yuzu"),
3269 tr("The currently running application has requested yuzu to not "
3270 "exit.\n\nWould you like to bypass this and exit anyway?"),
3271 QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
3272 return answer != QMessageBox::No; 3270 return answer != QMessageBox::No;
3273} 3271}
3274 3272
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 4f14be524..891f7be6f 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -465,10 +465,9 @@ void Config::ReadValues() {
465 ReadSetting("Renderer", Settings::values.disable_fps_limit); 465 ReadSetting("Renderer", Settings::values.disable_fps_limit);
466 ReadSetting("Renderer", Settings::values.shader_backend); 466 ReadSetting("Renderer", Settings::values.shader_backend);
467 ReadSetting("Renderer", Settings::values.use_asynchronous_shaders); 467 ReadSetting("Renderer", Settings::values.use_asynchronous_shaders);
468 ReadSetting("Renderer", Settings::values.use_nvdec_emulation); 468 ReadSetting("Renderer", Settings::values.nvdec_emulation);
469 ReadSetting("Renderer", Settings::values.accelerate_astc); 469 ReadSetting("Renderer", Settings::values.accelerate_astc);
470 ReadSetting("Renderer", Settings::values.use_fast_gpu_time); 470 ReadSetting("Renderer", Settings::values.use_fast_gpu_time);
471 ReadSetting("Renderer", Settings::values.use_caches_gc);
472 471
473 ReadSetting("Renderer", Settings::values.bg_red); 472 ReadSetting("Renderer", Settings::values.bg_red);
474 ReadSetting("Renderer", Settings::values.bg_green); 473 ReadSetting("Renderer", Settings::values.bg_green);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index e02eceb99..72f3213fb 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -261,9 +261,9 @@ shader_backend =
261# 0 (default): Off, 1: On 261# 0 (default): Off, 1: On
262use_asynchronous_shaders = 262use_asynchronous_shaders =
263 263
264# Enable NVDEC emulation. 264# NVDEC emulation.
265# 0: Off, 1 (default): On 265# 0: Disabled, 1: CPU Decoding, 2 (default): GPU Decoding
266use_nvdec_emulation = 266nvdec_emulation =
267 267
268# Accelerate ASTC texture decoding. 268# Accelerate ASTC texture decoding.
269# 0: Off, 1 (default): On 269# 0: Off, 1 (default): On