summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ci/scripts/linux/docker.sh2
-rw-r--r--.ci/scripts/windows/docker.sh2
-rw-r--r--dist/qt_themes/colorful/style.qrc2
-rw-r--r--dist/qt_themes/colorful/style.qss4
-rw-r--r--dist/qt_themes/default/default.qrc13
-rw-r--r--dist/qt_themes/default/style.qss35
-rw-r--r--dist/qt_themes/qdarkstyle/style.qss38
-rw-r--r--src/core/core.cpp4
-rw-r--r--src/core/frontend/emu_window.h7
-rw-r--r--src/core/frontend/input.h10
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp50
-rw-r--r--src/core/hle/kernel/svc.cpp8
-rw-r--r--src/core/hle/kernel/transfer_memory.cpp66
-rw-r--r--src/core/hle/kernel/transfer_memory.h19
-rw-r--r--src/core/hle/kernel/vm_manager.cpp3
-rw-r--r--src/core/hle/kernel/vm_manager.h60
-rw-r--r--src/core/hle/kernel/wait_object.cpp13
-rw-r--r--src/core/hle/service/am/am.cpp92
-rw-r--r--src/core/hle/service/am/am.h30
-rw-r--r--src/core/hle/service/am/applets/applets.cpp26
-rw-r--r--src/core/hle/service/am/applets/applets.h24
-rw-r--r--src/core/hle/service/am/applets/error.cpp2
-rw-r--r--src/core/hle/service/am/applets/general_backend.cpp14
-rw-r--r--src/core/hle/service/am/applets/profile_select.cpp4
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.cpp13
-rw-r--r--src/core/hle/service/am/applets/web_browser.cpp2
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp16
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp39
-rw-r--r--src/core/hle/service/prepo/prepo.cpp30
-rw-r--r--src/core/settings.h9
-rw-r--r--src/core/telemetry_session.cpp12
-rw-r--r--src/input_common/main.cpp1
-rw-r--r--src/input_common/sdl/sdl_impl.cpp16
-rw-r--r--src/input_common/udp/client.cpp15
-rw-r--r--src/input_common/udp/client.h1
-rw-r--r--src/input_common/udp/protocol.h1
-rw-r--r--src/input_common/udp/udp.cpp8
-rw-r--r--src/input_common/udp/udp.h8
-rw-r--r--src/video_core/CMakeLists.txt3
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h5
-rw-r--r--src/video_core/engines/const_buffer_engine_interface.h4
-rw-r--r--src/video_core/engines/kepler_compute.cpp8
-rw-r--r--src/video_core/engines/kepler_compute.h4
-rw-r--r--src/video_core/engines/maxwell_3d.cpp8
-rw-r--r--src/video_core/engines/maxwell_3d.h4
-rw-r--r--src/video_core/engines/shader_bytecode.h29
-rw-r--r--src/video_core/gpu.cpp2
-rw-r--r--src/video_core/gpu_thread.h2
-rw-r--r--src/video_core/guest_driver.cpp36
-rw-r--r--src/video_core/guest_driver.h41
-rw-r--r--src/video_core/memory_manager.cpp14
-rw-r--r--src/video_core/memory_manager.h7
-rw-r--r--src/video_core/rasterizer_interface.h14
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp161
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h7
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp4
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp37
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.cpp12
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.h1
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h20
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp265
-rw-r--r--src/video_core/renderer_vulkan/vk_device.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp9
-rw-r--r--src/video_core/renderer_vulkan/vk_shader_decompiler.cpp19
-rw-r--r--src/video_core/shader/ast.h10
-rw-r--r--src/video_core/shader/const_buffer_locker.cpp17
-rw-r--r--src/video_core/shader/const_buffer_locker.h21
-rw-r--r--src/video_core/shader/decode.cpp68
-rw-r--r--src/video_core/shader/decode/arithmetic.cpp11
-rw-r--r--src/video_core/shader/decode/arithmetic_integer.cpp2
-rw-r--r--src/video_core/shader/decode/bfi.cpp7
-rw-r--r--src/video_core/shader/decode/other.cpp9
-rw-r--r--src/video_core/shader/decode/shift.cpp113
-rw-r--r--src/video_core/shader/decode/texture.cpp114
-rw-r--r--src/video_core/shader/node.h89
-rw-r--r--src/video_core/shader/node_helper.h6
-rw-r--r--src/video_core/shader/shader_ir.cpp9
-rw-r--r--src/video_core/shader/shader_ir.h16
-rw-r--r--src/video_core/shader/track.cpp106
-rw-r--r--src/video_core/video_core.cpp15
-rw-r--r--src/web_service/telemetry_json.cpp1
-rw-r--r--src/yuzu/CMakeLists.txt5
-rw-r--r--src/yuzu/bootmanager.cpp284
-rw-r--r--src/yuzu/bootmanager.h30
-rw-r--r--src/yuzu/configuration/config.cpp7
-rw-r--r--src/yuzu/configuration/configure_debug.cpp3
-rw-r--r--src/yuzu/configuration/configure_debug.ui116
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp94
-rw-r--r--src/yuzu/configuration/configure_graphics.h12
-rw-r--r--src/yuzu/configuration/configure_graphics.ui72
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp26
-rw-r--r--src/yuzu/configuration/configure_input_player.h2
-rw-r--r--src/yuzu/configuration/configure_input_player.ui79
-rw-r--r--src/yuzu/main.cpp184
-rw-r--r--src/yuzu/main.h5
-rw-r--r--src/yuzu_cmd/CMakeLists.txt11
-rw-r--r--src/yuzu_cmd/config.cpp6
-rw-r--r--src/yuzu_cmd/default_ini.h11
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp4
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h3
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp7
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h4
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp162
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h39
-rw-r--r--src/yuzu_cmd/yuzu.cpp18
-rw-r--r--src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp15
-rw-r--r--src/yuzu_tester/emu_window/emu_window_sdl2_hide.h7
107 files changed, 2628 insertions, 613 deletions
diff --git a/.ci/scripts/linux/docker.sh b/.ci/scripts/linux/docker.sh
index 5559a527c..f11878128 100644
--- a/.ci/scripts/linux/docker.sh
+++ b/.ci/scripts/linux/docker.sh
@@ -5,7 +5,7 @@ cd /yuzu
5ccache -s 5ccache -s
6 6
7mkdir build || true && cd build 7mkdir build || true && cd build
8cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON 8cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_VULKAN=No
9 9
10ninja 10ninja
11 11
diff --git a/.ci/scripts/windows/docker.sh b/.ci/scripts/windows/docker.sh
index e8f26933a..beb554b65 100644
--- a/.ci/scripts/windows/docker.sh
+++ b/.ci/scripts/windows/docker.sh
@@ -13,7 +13,7 @@ echo '' >> /bin/cmd
13chmod +x /bin/cmd 13chmod +x /bin/cmd
14 14
15mkdir build || true && cd build 15mkdir build || true && cd build
16cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release 16cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_VULKAN=No
17ninja 17ninja
18 18
19# Clean up the dirty hacks 19# Clean up the dirty hacks
diff --git a/dist/qt_themes/colorful/style.qrc b/dist/qt_themes/colorful/style.qrc
index af2f3fd56..36735519a 100644
--- a/dist/qt_themes/colorful/style.qrc
+++ b/dist/qt_themes/colorful/style.qrc
@@ -10,6 +10,6 @@
10 <file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file> 10 <file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file>
11 </qresource> 11 </qresource>
12 <qresource prefix="colorful"> 12 <qresource prefix="colorful">
13 <file>style.qss</file> 13 <file alias="style.qss">../default/style.qss</file>
14 </qresource> 14 </qresource>
15</RCC> 15</RCC>
diff --git a/dist/qt_themes/colorful/style.qss b/dist/qt_themes/colorful/style.qss
deleted file mode 100644
index 413fc81da..000000000
--- a/dist/qt_themes/colorful/style.qss
+++ /dev/null
@@ -1,4 +0,0 @@
1/*
2 This file is intentionally left blank.
3 We do not want to apply any stylesheet for colorful, only icons.
4*/
diff --git a/dist/qt_themes/default/default.qrc b/dist/qt_themes/default/default.qrc
index d1a0ee1be..c51fdb26c 100644
--- a/dist/qt_themes/default/default.qrc
+++ b/dist/qt_themes/default/default.qrc
@@ -1,25 +1,18 @@
1<RCC> 1<RCC>
2 <qresource prefix="icons/default"> 2 <qresource prefix="icons/default">
3 <file alias="index.theme">icons/index.theme</file> 3 <file alias="index.theme">icons/index.theme</file>
4
5 <file alias="16x16/checked.png">icons/16x16/checked.png</file> 4 <file alias="16x16/checked.png">icons/16x16/checked.png</file>
6
7 <file alias="16x16/failed.png">icons/16x16/failed.png</file> 5 <file alias="16x16/failed.png">icons/16x16/failed.png</file>
8
9 <file alias="16x16/lock.png">icons/16x16/lock.png</file> 6 <file alias="16x16/lock.png">icons/16x16/lock.png</file>
10
11 <file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file> 7 <file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
12
13 <file alias="48x48/chip.png">icons/48x48/chip.png</file> 8 <file alias="48x48/chip.png">icons/48x48/chip.png</file>
14
15 <file alias="48x48/folder.png">icons/48x48/folder.png</file> 9 <file alias="48x48/folder.png">icons/48x48/folder.png</file>
16
17 <file alias="48x48/plus.png">icons/48x48/plus.png</file> 10 <file alias="48x48/plus.png">icons/48x48/plus.png</file>
18
19 <file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file> 11 <file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
20
21 <file alias="256x256/yuzu.png">icons/256x256/yuzu.png</file> 12 <file alias="256x256/yuzu.png">icons/256x256/yuzu.png</file>
22
23 <file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file> 13 <file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file>
24 </qresource> 14 </qresource>
15 <qresource prefix="default">
16 <file>style.qss</file>
17 </qresource>
25</RCC> 18</RCC>
diff --git a/dist/qt_themes/default/style.qss b/dist/qt_themes/default/style.qss
new file mode 100644
index 000000000..6b5953e38
--- /dev/null
+++ b/dist/qt_themes/default/style.qss
@@ -0,0 +1,35 @@
1QPushButton#TogglableStatusBarButton {
2 color: #959595;
3 border: 1px solid transparent;
4 background-color: transparent;
5 padding: 0px 3px 0px 3px;
6 text-align: center;
7}
8
9QPushButton#TogglableStatusBarButton:checked {
10 color: #000000;
11}
12
13QPushButton#TogglableStatusBarButton:hover {
14 border: 1px solid #76797C;
15}
16
17QPushButton#RendererStatusBarButton {
18 color: #656565;
19 border: 1px solid transparent;
20 background-color: transparent;
21 padding: 0px 3px 0px 3px;
22 text-align: center;
23}
24
25QPushButton#RendererStatusBarButton:hover {
26 border: 1px solid #76797C;
27}
28
29QPushButton#RendererStatusBarButton:checked {
30 color: #e85c00;
31}
32
33QPushButton#RendererStatusBarButton:!checked{
34 color: #0066ff;
35}
diff --git a/dist/qt_themes/qdarkstyle/style.qss b/dist/qt_themes/qdarkstyle/style.qss
index 9814b06dd..7d088a719 100644
--- a/dist/qt_themes/qdarkstyle/style.qss
+++ b/dist/qt_themes/qdarkstyle/style.qss
@@ -1236,3 +1236,41 @@ QToolButton:disabled,
1236QPlainTextEdit:disabled { 1236QPlainTextEdit:disabled {
1237 background-color: #2b2e31; 1237 background-color: #2b2e31;
1238} 1238}
1239
1240QPushButton#TogglableStatusBarButton {
1241 min-width: 0px;
1242 color: #656565;
1243 border: 1px solid transparent;
1244 background-color: transparent;
1245 padding: 0px 3px 0px 3px;
1246 text-align: center;
1247}
1248
1249QPushButton#TogglableStatusBarButton:checked {
1250 color: #ffffff;
1251}
1252
1253QPushButton#TogglableStatusBarButton:hover {
1254 border: 1px solid #76797C;
1255}
1256
1257QPushButton#RendererStatusBarButton {
1258 min-width: 0px;
1259 color: #656565;
1260 border: 1px solid transparent;
1261 background-color: transparent;
1262 padding: 0px 3px 0px 3px;
1263 text-align: center;
1264}
1265
1266QPushButton#RendererStatusBarButton:hover {
1267 border: 1px solid #76797C;
1268}
1269
1270QPushButton#RendererStatusBarButton:checked {
1271 color: #e85c00;
1272}
1273
1274QPushButton#RendererStatusBarButton:!checked{
1275 color: #00ccdd;
1276} \ No newline at end of file
diff --git a/src/core/core.cpp b/src/core/core.cpp
index c53d122be..0eb0c0dca 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -268,7 +268,9 @@ struct System::Impl {
268 is_powered_on = false; 268 is_powered_on = false;
269 exit_lock = false; 269 exit_lock = false;
270 270
271 gpu_core->WaitIdle(); 271 if (gpu_core) {
272 gpu_core->WaitIdle();
273 }
272 274
273 // Shutdown emulation session 275 // Shutdown emulation session
274 renderer.reset(); 276 renderer.reset();
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index 4a9912641..3376eedc5 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -75,6 +75,13 @@ public:
75 return nullptr; 75 return nullptr;
76 } 76 }
77 77
78 /// Returns if window is shown (not minimized)
79 virtual bool IsShown() const = 0;
80
81 /// Retrieves Vulkan specific handlers from the window
82 virtual void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
83 void* surface) const = 0;
84
78 /** 85 /**
79 * Signal that a touch pressed event has occurred (e.g. mouse click pressed) 86 * Signal that a touch pressed event has occurred (e.g. mouse click pressed)
80 * @param framebuffer_x Framebuffer x-coordinate that was pressed 87 * @param framebuffer_x Framebuffer x-coordinate that was pressed
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h
index 7c11d7546..2b098b7c6 100644
--- a/src/core/frontend/input.h
+++ b/src/core/frontend/input.h
@@ -15,6 +15,13 @@
15 15
16namespace Input { 16namespace Input {
17 17
18enum class AnalogDirection : u8 {
19 RIGHT,
20 LEFT,
21 UP,
22 DOWN,
23};
24
18/// An abstract class template for an input device (a button, an analog input, etc.). 25/// An abstract class template for an input device (a button, an analog input, etc.).
19template <typename StatusType> 26template <typename StatusType>
20class InputDevice { 27class InputDevice {
@@ -23,6 +30,9 @@ public:
23 virtual StatusType GetStatus() const { 30 virtual StatusType GetStatus() const {
24 return {}; 31 return {};
25 } 32 }
33 virtual bool GetAnalogDirectionStatus(AnalogDirection direction) const {
34 return {};
35 }
26}; 36};
27 37
28/// An abstract class template for a factory that can create input devices. 38/// An abstract class template for a factory that can create input devices.
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 2db28dcf0..ab05788d7 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -284,13 +284,18 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
284 284
285std::vector<u8> HLERequestContext::ReadBuffer(int buffer_index) const { 285std::vector<u8> HLERequestContext::ReadBuffer(int buffer_index) const {
286 std::vector<u8> buffer; 286 std::vector<u8> buffer;
287 const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[buffer_index].Size()}; 287 const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
288 BufferDescriptorA()[buffer_index].Size()};
288 auto& memory = Core::System::GetInstance().Memory(); 289 auto& memory = Core::System::GetInstance().Memory();
289 290
290 if (is_buffer_a) { 291 if (is_buffer_a) {
292 ASSERT_MSG(BufferDescriptorA().size() > buffer_index,
293 "BufferDescriptorA invalid buffer_index {}", buffer_index);
291 buffer.resize(BufferDescriptorA()[buffer_index].Size()); 294 buffer.resize(BufferDescriptorA()[buffer_index].Size());
292 memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size()); 295 memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size());
293 } else { 296 } else {
297 ASSERT_MSG(BufferDescriptorX().size() > buffer_index,
298 "BufferDescriptorX invalid buffer_index {}", buffer_index);
294 buffer.resize(BufferDescriptorX()[buffer_index].Size()); 299 buffer.resize(BufferDescriptorX()[buffer_index].Size());
295 memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size()); 300 memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size());
296 } 301 }
@@ -305,7 +310,8 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
305 return 0; 310 return 0;
306 } 311 }
307 312
308 const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()}; 313 const bool is_buffer_b{BufferDescriptorB().size() > buffer_index &&
314 BufferDescriptorB()[buffer_index].Size()};
309 const std::size_t buffer_size{GetWriteBufferSize(buffer_index)}; 315 const std::size_t buffer_size{GetWriteBufferSize(buffer_index)};
310 if (size > buffer_size) { 316 if (size > buffer_size) {
311 LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size, 317 LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size,
@@ -315,8 +321,16 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
315 321
316 auto& memory = Core::System::GetInstance().Memory(); 322 auto& memory = Core::System::GetInstance().Memory();
317 if (is_buffer_b) { 323 if (is_buffer_b) {
324 ASSERT_MSG(BufferDescriptorB().size() > buffer_index,
325 "BufferDescriptorB invalid buffer_index {}", buffer_index);
326 ASSERT_MSG(BufferDescriptorB()[buffer_index].Size() >= size,
327 "BufferDescriptorB buffer_index {} is not large enough", buffer_index);
318 memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); 328 memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);
319 } else { 329 } else {
330 ASSERT_MSG(BufferDescriptorC().size() > buffer_index,
331 "BufferDescriptorC invalid buffer_index {}", buffer_index);
332 ASSERT_MSG(BufferDescriptorC()[buffer_index].Size() >= size,
333 "BufferDescriptorC buffer_index {} is not large enough", buffer_index);
320 memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); 334 memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size);
321 } 335 }
322 336
@@ -324,15 +338,35 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
324} 338}
325 339
326std::size_t HLERequestContext::GetReadBufferSize(int buffer_index) const { 340std::size_t HLERequestContext::GetReadBufferSize(int buffer_index) const {
327 const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[buffer_index].Size()}; 341 const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
328 return is_buffer_a ? BufferDescriptorA()[buffer_index].Size() 342 BufferDescriptorA()[buffer_index].Size()};
329 : BufferDescriptorX()[buffer_index].Size(); 343 if (is_buffer_a) {
344 ASSERT_MSG(BufferDescriptorA().size() > buffer_index,
345 "BufferDescriptorA invalid buffer_index {}", buffer_index);
346 ASSERT_MSG(BufferDescriptorA()[buffer_index].Size() > 0,
347 "BufferDescriptorA buffer_index {} is empty", buffer_index);
348 return BufferDescriptorA()[buffer_index].Size();
349 } else {
350 ASSERT_MSG(BufferDescriptorX().size() > buffer_index,
351 "BufferDescriptorX invalid buffer_index {}", buffer_index);
352 ASSERT_MSG(BufferDescriptorX()[buffer_index].Size() > 0,
353 "BufferDescriptorX buffer_index {} is empty", buffer_index);
354 return BufferDescriptorX()[buffer_index].Size();
355 }
330} 356}
331 357
332std::size_t HLERequestContext::GetWriteBufferSize(int buffer_index) const { 358std::size_t HLERequestContext::GetWriteBufferSize(int buffer_index) const {
333 const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()}; 359 const bool is_buffer_b{BufferDescriptorB().size() > buffer_index &&
334 return is_buffer_b ? BufferDescriptorB()[buffer_index].Size() 360 BufferDescriptorB()[buffer_index].Size()};
335 : BufferDescriptorC()[buffer_index].Size(); 361 if (is_buffer_b) {
362 ASSERT_MSG(BufferDescriptorB().size() > buffer_index,
363 "BufferDescriptorB invalid buffer_index {}", buffer_index);
364 return BufferDescriptorB()[buffer_index].Size();
365 } else {
366 ASSERT_MSG(BufferDescriptorC().size() > buffer_index,
367 "BufferDescriptorC invalid buffer_index {}", buffer_index);
368 return BufferDescriptorC()[buffer_index].Size();
369 }
336} 370}
337 371
338std::string HLERequestContext::Description() const { 372std::string HLERequestContext::Description() const {
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 1d99bf7a2..9cae5c73d 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -1863,10 +1863,14 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAd
1863 } 1863 }
1864 1864
1865 auto& kernel = system.Kernel(); 1865 auto& kernel = system.Kernel();
1866 auto transfer_mem_handle = TransferMemory::Create(kernel, addr, size, perms); 1866 auto transfer_mem_handle = TransferMemory::Create(kernel, system.Memory(), addr, size, perms);
1867
1868 if (const auto reserve_result{transfer_mem_handle->Reserve()}; reserve_result.IsError()) {
1869 return reserve_result;
1870 }
1867 1871
1868 auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); 1872 auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
1869 const auto result = handle_table.Create(std::move(transfer_mem_handle)); 1873 const auto result{handle_table.Create(std::move(transfer_mem_handle))};
1870 if (result.Failed()) { 1874 if (result.Failed()) {
1871 return result.Code(); 1875 return result.Code();
1872 } 1876 }
diff --git a/src/core/hle/kernel/transfer_memory.cpp b/src/core/hle/kernel/transfer_memory.cpp
index f0e73f57b..f2d3f8b49 100644
--- a/src/core/hle/kernel/transfer_memory.cpp
+++ b/src/core/hle/kernel/transfer_memory.cpp
@@ -8,15 +8,23 @@
8#include "core/hle/kernel/shared_memory.h" 8#include "core/hle/kernel/shared_memory.h"
9#include "core/hle/kernel/transfer_memory.h" 9#include "core/hle/kernel/transfer_memory.h"
10#include "core/hle/result.h" 10#include "core/hle/result.h"
11#include "core/memory.h"
11 12
12namespace Kernel { 13namespace Kernel {
13 14
14TransferMemory::TransferMemory(KernelCore& kernel) : Object{kernel} {} 15TransferMemory::TransferMemory(KernelCore& kernel, Memory::Memory& memory)
15TransferMemory::~TransferMemory() = default; 16 : Object{kernel}, memory{memory} {}
16 17
17std::shared_ptr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr base_address, 18TransferMemory::~TransferMemory() {
18 u64 size, MemoryPermission permissions) { 19 // Release memory region when transfer memory is destroyed
19 std::shared_ptr<TransferMemory> transfer_memory{std::make_shared<TransferMemory>(kernel)}; 20 Reset();
21}
22
23std::shared_ptr<TransferMemory> TransferMemory::Create(KernelCore& kernel, Memory::Memory& memory,
24 VAddr base_address, u64 size,
25 MemoryPermission permissions) {
26 std::shared_ptr<TransferMemory> transfer_memory{
27 std::make_shared<TransferMemory>(kernel, memory)};
20 28
21 transfer_memory->base_address = base_address; 29 transfer_memory->base_address = base_address;
22 transfer_memory->memory_size = size; 30 transfer_memory->memory_size = size;
@@ -27,7 +35,7 @@ std::shared_ptr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr
27} 35}
28 36
29const u8* TransferMemory::GetPointer() const { 37const u8* TransferMemory::GetPointer() const {
30 return backing_block.get()->data(); 38 return memory.GetPointer(base_address);
31} 39}
32 40
33u64 TransferMemory::GetSize() const { 41u64 TransferMemory::GetSize() const {
@@ -62,6 +70,52 @@ ResultCode TransferMemory::MapMemory(VAddr address, u64 size, MemoryPermission p
62 return RESULT_SUCCESS; 70 return RESULT_SUCCESS;
63} 71}
64 72
73ResultCode TransferMemory::Reserve() {
74 auto& vm_manager{owner_process->VMManager()};
75 const auto check_range_result{vm_manager.CheckRangeState(
76 base_address, memory_size, MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated,
77 MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated, VMAPermission::All,
78 VMAPermission::ReadWrite, MemoryAttribute::Mask, MemoryAttribute::None,
79 MemoryAttribute::IpcAndDeviceMapped)};
80
81 if (check_range_result.Failed()) {
82 return check_range_result.Code();
83 }
84
85 auto [state_, permissions_, attribute] = *check_range_result;
86
87 if (const auto result{vm_manager.ReprotectRange(
88 base_address, memory_size, SharedMemory::ConvertPermissions(owner_permissions))};
89 result.IsError()) {
90 return result;
91 }
92
93 return vm_manager.SetMemoryAttribute(base_address, memory_size, MemoryAttribute::Mask,
94 attribute | MemoryAttribute::Locked);
95}
96
97ResultCode TransferMemory::Reset() {
98 auto& vm_manager{owner_process->VMManager()};
99 if (const auto result{vm_manager.CheckRangeState(
100 base_address, memory_size,
101 MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated,
102 MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated, VMAPermission::None,
103 VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::Locked,
104 MemoryAttribute::IpcAndDeviceMapped)};
105 result.Failed()) {
106 return result.Code();
107 }
108
109 if (const auto result{
110 vm_manager.ReprotectRange(base_address, memory_size, VMAPermission::ReadWrite)};
111 result.IsError()) {
112 return result;
113 }
114
115 return vm_manager.SetMemoryAttribute(base_address, memory_size, MemoryAttribute::Mask,
116 MemoryAttribute::None);
117}
118
65ResultCode TransferMemory::UnmapMemory(VAddr address, u64 size) { 119ResultCode TransferMemory::UnmapMemory(VAddr address, u64 size) {
66 if (memory_size != size) { 120 if (memory_size != size) {
67 return ERR_INVALID_SIZE; 121 return ERR_INVALID_SIZE;
diff --git a/src/core/hle/kernel/transfer_memory.h b/src/core/hle/kernel/transfer_memory.h
index 0a6e15d18..6e388536a 100644
--- a/src/core/hle/kernel/transfer_memory.h
+++ b/src/core/hle/kernel/transfer_memory.h
@@ -11,6 +11,10 @@
11 11
12union ResultCode; 12union ResultCode;
13 13
14namespace Memory {
15class Memory;
16}
17
14namespace Kernel { 18namespace Kernel {
15 19
16class KernelCore; 20class KernelCore;
@@ -26,12 +30,13 @@ enum class MemoryPermission : u32;
26/// 30///
27class TransferMemory final : public Object { 31class TransferMemory final : public Object {
28public: 32public:
29 explicit TransferMemory(KernelCore& kernel); 33 explicit TransferMemory(KernelCore& kernel, Memory::Memory& memory);
30 ~TransferMemory() override; 34 ~TransferMemory() override;
31 35
32 static constexpr HandleType HANDLE_TYPE = HandleType::TransferMemory; 36 static constexpr HandleType HANDLE_TYPE = HandleType::TransferMemory;
33 37
34 static std::shared_ptr<TransferMemory> Create(KernelCore& kernel, VAddr base_address, u64 size, 38 static std::shared_ptr<TransferMemory> Create(KernelCore& kernel, Memory::Memory& memory,
39 VAddr base_address, u64 size,
35 MemoryPermission permissions); 40 MemoryPermission permissions);
36 41
37 TransferMemory(const TransferMemory&) = delete; 42 TransferMemory(const TransferMemory&) = delete;
@@ -80,6 +85,14 @@ public:
80 /// 85 ///
81 ResultCode UnmapMemory(VAddr address, u64 size); 86 ResultCode UnmapMemory(VAddr address, u64 size);
82 87
88 /// Reserves the region to be used for the transfer memory, called after the transfer memory is
89 /// created.
90 ResultCode Reserve();
91
92 /// Resets the region previously used for the transfer memory, called after the transfer memory
93 /// is closed.
94 ResultCode Reset();
95
83private: 96private:
84 /// Memory block backing this instance. 97 /// Memory block backing this instance.
85 std::shared_ptr<PhysicalMemory> backing_block; 98 std::shared_ptr<PhysicalMemory> backing_block;
@@ -98,6 +111,8 @@ private:
98 111
99 /// Whether or not this transfer memory instance has mapped memory. 112 /// Whether or not this transfer memory instance has mapped memory.
100 bool is_mapped = false; 113 bool is_mapped = false;
114
115 Memory::Memory& memory;
101}; 116};
102 117
103} // namespace Kernel 118} // namespace Kernel
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index 0b3500fce..024c22901 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -544,7 +544,8 @@ MemoryInfo VMManager::QueryMemory(VAddr address) const {
544 544
545ResultCode VMManager::SetMemoryAttribute(VAddr address, u64 size, MemoryAttribute mask, 545ResultCode VMManager::SetMemoryAttribute(VAddr address, u64 size, MemoryAttribute mask,
546 MemoryAttribute attribute) { 546 MemoryAttribute attribute) {
547 constexpr auto ignore_mask = MemoryAttribute::Uncached | MemoryAttribute::DeviceMapped; 547 constexpr auto ignore_mask =
548 MemoryAttribute::Uncached | MemoryAttribute::DeviceMapped | MemoryAttribute::Locked;
548 constexpr auto attribute_mask = ~ignore_mask; 549 constexpr auto attribute_mask = ~ignore_mask;
549 550
550 const auto result = CheckRangeState( 551 const auto result = CheckRangeState(
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 850a7ebc3..90b4b006a 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -98,6 +98,8 @@ enum class MemoryAttribute : u32 {
98 DeviceMapped = 4, 98 DeviceMapped = 4,
99 /// Uncached memory 99 /// Uncached memory
100 Uncached = 8, 100 Uncached = 8,
101
102 IpcAndDeviceMapped = LockedForIPC | DeviceMapped,
101}; 103};
102 104
103constexpr MemoryAttribute operator|(MemoryAttribute lhs, MemoryAttribute rhs) { 105constexpr MemoryAttribute operator|(MemoryAttribute lhs, MemoryAttribute rhs) {
@@ -654,6 +656,35 @@ public:
654 /// is scheduled. 656 /// is scheduled.
655 Common::PageTable page_table{Memory::PAGE_BITS}; 657 Common::PageTable page_table{Memory::PAGE_BITS};
656 658
659 using CheckResults = ResultVal<std::tuple<MemoryState, VMAPermission, MemoryAttribute>>;
660
661 /// Checks if an address range adheres to the specified states provided.
662 ///
663 /// @param address The starting address of the address range.
664 /// @param size The size of the address range.
665 /// @param state_mask The memory state mask.
666 /// @param state The state to compare the individual VMA states against,
667 /// which is done in the form of: (vma.state & state_mask) != state.
668 /// @param permission_mask The memory permissions mask.
669 /// @param permissions The permission to compare the individual VMA permissions against,
670 /// which is done in the form of:
671 /// (vma.permission & permission_mask) != permission.
672 /// @param attribute_mask The memory attribute mask.
673 /// @param attribute The memory attributes to compare the individual VMA attributes
674 /// against, which is done in the form of:
675 /// (vma.attributes & attribute_mask) != attribute.
676 /// @param ignore_mask The memory attributes to ignore during the check.
677 ///
678 /// @returns If successful, returns a tuple containing the memory attributes
679 /// (with ignored bits specified by ignore_mask unset), memory permissions, and
680 /// memory state across the memory range.
681 /// @returns If not successful, returns ERR_INVALID_ADDRESS_STATE.
682 ///
683 CheckResults CheckRangeState(VAddr address, u64 size, MemoryState state_mask, MemoryState state,
684 VMAPermission permission_mask, VMAPermission permissions,
685 MemoryAttribute attribute_mask, MemoryAttribute attribute,
686 MemoryAttribute ignore_mask) const;
687
657private: 688private:
658 using VMAIter = VMAMap::iterator; 689 using VMAIter = VMAMap::iterator;
659 690
@@ -707,35 +738,6 @@ private:
707 /// Clears out the page table 738 /// Clears out the page table
708 void ClearPageTable(); 739 void ClearPageTable();
709 740
710 using CheckResults = ResultVal<std::tuple<MemoryState, VMAPermission, MemoryAttribute>>;
711
712 /// Checks if an address range adheres to the specified states provided.
713 ///
714 /// @param address The starting address of the address range.
715 /// @param size The size of the address range.
716 /// @param state_mask The memory state mask.
717 /// @param state The state to compare the individual VMA states against,
718 /// which is done in the form of: (vma.state & state_mask) != state.
719 /// @param permission_mask The memory permissions mask.
720 /// @param permissions The permission to compare the individual VMA permissions against,
721 /// which is done in the form of:
722 /// (vma.permission & permission_mask) != permission.
723 /// @param attribute_mask The memory attribute mask.
724 /// @param attribute The memory attributes to compare the individual VMA attributes
725 /// against, which is done in the form of:
726 /// (vma.attributes & attribute_mask) != attribute.
727 /// @param ignore_mask The memory attributes to ignore during the check.
728 ///
729 /// @returns If successful, returns a tuple containing the memory attributes
730 /// (with ignored bits specified by ignore_mask unset), memory permissions, and
731 /// memory state across the memory range.
732 /// @returns If not successful, returns ERR_INVALID_ADDRESS_STATE.
733 ///
734 CheckResults CheckRangeState(VAddr address, u64 size, MemoryState state_mask, MemoryState state,
735 VMAPermission permission_mask, VMAPermission permissions,
736 MemoryAttribute attribute_mask, MemoryAttribute attribute,
737 MemoryAttribute ignore_mask) const;
738
739 /// Gets the amount of memory currently mapped (state != Unmapped) in a range. 741 /// Gets the amount of memory currently mapped (state != Unmapped) in a range.
740 ResultVal<std::size_t> SizeOfAllocatedVMAsInRange(VAddr address, std::size_t size) const; 742 ResultVal<std::size_t> SizeOfAllocatedVMAsInRange(VAddr address, std::size_t size) const;
741 743
diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp
index a0c806e8f..1838260fd 100644
--- a/src/core/hle/kernel/wait_object.cpp
+++ b/src/core/hle/kernel/wait_object.cpp
@@ -50,17 +50,8 @@ std::shared_ptr<Thread> WaitObject::GetHighestPriorityReadyThread() const {
50 if (ShouldWait(thread.get())) 50 if (ShouldWait(thread.get()))
51 continue; 51 continue;
52 52
53 // A thread is ready to run if it's either in ThreadStatus::WaitSynch 53 candidate = thread.get();
54 // and the rest of the objects it is waiting on are ready. 54 candidate_priority = thread->GetPriority();
55 bool ready_to_run = true;
56 if (thread_status == ThreadStatus::WaitSynch) {
57 ready_to_run = thread->AllWaitObjectsReady();
58 }
59
60 if (ready_to_run) {
61 candidate = thread.get();
62 candidate_priority = thread->GetPriority();
63 }
64 } 55 }
65 56
66 return SharedFrom(candidate); 57 return SharedFrom(candidate);
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 95aa5d23d..cc978713b 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -709,8 +709,34 @@ void ICommonStateGetter::SetCpuBoostMode(Kernel::HLERequestContext& ctx) {
709 apm_sys->SetCpuBoostMode(ctx); 709 apm_sys->SetCpuBoostMode(ctx);
710} 710}
711 711
712IStorage::IStorage(std::vector<u8> buffer) 712IStorageImpl::~IStorageImpl() = default;
713 : ServiceFramework("IStorage"), buffer(std::move(buffer)) { 713
714class StorageDataImpl final : public IStorageImpl {
715public:
716 explicit StorageDataImpl(std::vector<u8>&& buffer) : buffer{std::move(buffer)} {}
717
718 std::vector<u8>& GetData() override {
719 return buffer;
720 }
721
722 const std::vector<u8>& GetData() const override {
723 return buffer;
724 }
725
726 std::size_t GetSize() const override {
727 return buffer.size();
728 }
729
730private:
731 std::vector<u8> buffer;
732};
733
734IStorage::IStorage(std::vector<u8>&& buffer)
735 : ServiceFramework("IStorage"), impl{std::make_shared<StorageDataImpl>(std::move(buffer))} {
736 Register();
737}
738
739void IStorage::Register() {
714 // clang-format off 740 // clang-format off
715 static const FunctionInfo functions[] = { 741 static const FunctionInfo functions[] = {
716 {0, &IStorage::Open, "Open"}, 742 {0, &IStorage::Open, "Open"},
@@ -723,8 +749,13 @@ IStorage::IStorage(std::vector<u8> buffer)
723 749
724IStorage::~IStorage() = default; 750IStorage::~IStorage() = default;
725 751
726const std::vector<u8>& IStorage::GetData() const { 752void IStorage::Open(Kernel::HLERequestContext& ctx) {
727 return buffer; 753 LOG_DEBUG(Service_AM, "called");
754
755 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
756
757 rb.Push(RESULT_SUCCESS);
758 rb.PushIpcInterface<IStorageAccessor>(*this);
728} 759}
729 760
730void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) { 761void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) {
@@ -816,7 +847,7 @@ private:
816 LOG_DEBUG(Service_AM, "called"); 847 LOG_DEBUG(Service_AM, "called");
817 848
818 IPC::RequestParser rp{ctx}; 849 IPC::RequestParser rp{ctx};
819 applet->GetBroker().PushNormalDataFromGame(*rp.PopIpcInterface<IStorage>()); 850 applet->GetBroker().PushNormalDataFromGame(rp.PopIpcInterface<IStorage>());
820 851
821 IPC::ResponseBuilder rb{ctx, 2}; 852 IPC::ResponseBuilder rb{ctx, 2};
822 rb.Push(RESULT_SUCCESS); 853 rb.Push(RESULT_SUCCESS);
@@ -825,26 +856,25 @@ private:
825 void PopOutData(Kernel::HLERequestContext& ctx) { 856 void PopOutData(Kernel::HLERequestContext& ctx) {
826 LOG_DEBUG(Service_AM, "called"); 857 LOG_DEBUG(Service_AM, "called");
827 858
828 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
829
830 const auto storage = applet->GetBroker().PopNormalDataToGame(); 859 const auto storage = applet->GetBroker().PopNormalDataToGame();
831 if (storage == nullptr) { 860 if (storage == nullptr) {
832 LOG_ERROR(Service_AM, 861 LOG_ERROR(Service_AM,
833 "storage is a nullptr. There is no data in the current normal channel"); 862 "storage is a nullptr. There is no data in the current normal channel");
834 863 IPC::ResponseBuilder rb{ctx, 2};
835 rb.Push(ERR_NO_DATA_IN_CHANNEL); 864 rb.Push(ERR_NO_DATA_IN_CHANNEL);
836 return; 865 return;
837 } 866 }
838 867
868 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
839 rb.Push(RESULT_SUCCESS); 869 rb.Push(RESULT_SUCCESS);
840 rb.PushIpcInterface<IStorage>(std::move(*storage)); 870 rb.PushIpcInterface<IStorage>(std::move(storage));
841 } 871 }
842 872
843 void PushInteractiveInData(Kernel::HLERequestContext& ctx) { 873 void PushInteractiveInData(Kernel::HLERequestContext& ctx) {
844 LOG_DEBUG(Service_AM, "called"); 874 LOG_DEBUG(Service_AM, "called");
845 875
846 IPC::RequestParser rp{ctx}; 876 IPC::RequestParser rp{ctx};
847 applet->GetBroker().PushInteractiveDataFromGame(*rp.PopIpcInterface<IStorage>()); 877 applet->GetBroker().PushInteractiveDataFromGame(rp.PopIpcInterface<IStorage>());
848 878
849 ASSERT(applet->IsInitialized()); 879 ASSERT(applet->IsInitialized());
850 applet->ExecuteInteractive(); 880 applet->ExecuteInteractive();
@@ -857,19 +887,18 @@ private:
857 void PopInteractiveOutData(Kernel::HLERequestContext& ctx) { 887 void PopInteractiveOutData(Kernel::HLERequestContext& ctx) {
858 LOG_DEBUG(Service_AM, "called"); 888 LOG_DEBUG(Service_AM, "called");
859 889
860 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
861
862 const auto storage = applet->GetBroker().PopInteractiveDataToGame(); 890 const auto storage = applet->GetBroker().PopInteractiveDataToGame();
863 if (storage == nullptr) { 891 if (storage == nullptr) {
864 LOG_ERROR(Service_AM, 892 LOG_ERROR(Service_AM,
865 "storage is a nullptr. There is no data in the current interactive channel"); 893 "storage is a nullptr. There is no data in the current interactive channel");
866 894 IPC::ResponseBuilder rb{ctx, 2};
867 rb.Push(ERR_NO_DATA_IN_CHANNEL); 895 rb.Push(ERR_NO_DATA_IN_CHANNEL);
868 return; 896 return;
869 } 897 }
870 898
899 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
871 rb.Push(RESULT_SUCCESS); 900 rb.Push(RESULT_SUCCESS);
872 rb.PushIpcInterface<IStorage>(std::move(*storage)); 901 rb.PushIpcInterface<IStorage>(std::move(storage));
873 } 902 }
874 903
875 void GetPopOutDataEvent(Kernel::HLERequestContext& ctx) { 904 void GetPopOutDataEvent(Kernel::HLERequestContext& ctx) {
@@ -891,15 +920,6 @@ private:
891 std::shared_ptr<Applets::Applet> applet; 920 std::shared_ptr<Applets::Applet> applet;
892}; 921};
893 922
894void IStorage::Open(Kernel::HLERequestContext& ctx) {
895 LOG_DEBUG(Service_AM, "called");
896
897 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
898
899 rb.Push(RESULT_SUCCESS);
900 rb.PushIpcInterface<IStorageAccessor>(*this);
901}
902
903IStorageAccessor::IStorageAccessor(IStorage& storage) 923IStorageAccessor::IStorageAccessor(IStorage& storage)
904 : ServiceFramework("IStorageAccessor"), backing(storage) { 924 : ServiceFramework("IStorageAccessor"), backing(storage) {
905 // clang-format off 925 // clang-format off
@@ -921,7 +941,7 @@ void IStorageAccessor::GetSize(Kernel::HLERequestContext& ctx) {
921 IPC::ResponseBuilder rb{ctx, 4}; 941 IPC::ResponseBuilder rb{ctx, 4};
922 942
923 rb.Push(RESULT_SUCCESS); 943 rb.Push(RESULT_SUCCESS);
924 rb.Push(static_cast<u64>(backing.buffer.size())); 944 rb.Push(static_cast<u64>(backing.GetSize()));
925} 945}
926 946
927void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) { 947void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) {
@@ -932,17 +952,17 @@ void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) {
932 952
933 LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, data.size()); 953 LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, data.size());
934 954
935 if (data.size() > backing.buffer.size() - offset) { 955 if (data.size() > backing.GetSize() - offset) {
936 LOG_ERROR(Service_AM, 956 LOG_ERROR(Service_AM,
937 "offset is out of bounds, backing_buffer_sz={}, data_size={}, offset={}", 957 "offset is out of bounds, backing_buffer_sz={}, data_size={}, offset={}",
938 backing.buffer.size(), data.size(), offset); 958 backing.GetSize(), data.size(), offset);
939 959
940 IPC::ResponseBuilder rb{ctx, 2}; 960 IPC::ResponseBuilder rb{ctx, 2};
941 rb.Push(ERR_SIZE_OUT_OF_BOUNDS); 961 rb.Push(ERR_SIZE_OUT_OF_BOUNDS);
942 return; 962 return;
943 } 963 }
944 964
945 std::memcpy(backing.buffer.data() + offset, data.data(), data.size()); 965 std::memcpy(backing.GetData().data() + offset, data.data(), data.size());
946 966
947 IPC::ResponseBuilder rb{ctx, 2}; 967 IPC::ResponseBuilder rb{ctx, 2};
948 rb.Push(RESULT_SUCCESS); 968 rb.Push(RESULT_SUCCESS);
@@ -956,16 +976,16 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) {
956 976
957 LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size); 977 LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size);
958 978
959 if (size > backing.buffer.size() - offset) { 979 if (size > backing.GetSize() - offset) {
960 LOG_ERROR(Service_AM, "offset is out of bounds, backing_buffer_sz={}, size={}, offset={}", 980 LOG_ERROR(Service_AM, "offset is out of bounds, backing_buffer_sz={}, size={}, offset={}",
961 backing.buffer.size(), size, offset); 981 backing.GetSize(), size, offset);
962 982
963 IPC::ResponseBuilder rb{ctx, 2}; 983 IPC::ResponseBuilder rb{ctx, 2};
964 rb.Push(ERR_SIZE_OUT_OF_BOUNDS); 984 rb.Push(ERR_SIZE_OUT_OF_BOUNDS);
965 return; 985 return;
966 } 986 }
967 987
968 ctx.WriteBuffer(backing.buffer.data() + offset, size); 988 ctx.WriteBuffer(backing.GetData().data() + offset, size);
969 989
970 IPC::ResponseBuilder rb{ctx, 2}; 990 IPC::ResponseBuilder rb{ctx, 2};
971 rb.Push(RESULT_SUCCESS); 991 rb.Push(RESULT_SUCCESS);
@@ -1031,7 +1051,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex
1031 rp.SetCurrentOffset(3); 1051 rp.SetCurrentOffset(3);
1032 const auto handle{rp.Pop<Kernel::Handle>()}; 1052 const auto handle{rp.Pop<Kernel::Handle>()};
1033 1053
1034 const auto transfer_mem = 1054 auto transfer_mem =
1035 system.CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>(handle); 1055 system.CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>(handle);
1036 1056
1037 if (transfer_mem == nullptr) { 1057 if (transfer_mem == nullptr) {
@@ -1047,7 +1067,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex
1047 1067
1048 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 1068 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1049 rb.Push(RESULT_SUCCESS); 1069 rb.Push(RESULT_SUCCESS);
1050 rb.PushIpcInterface(std::make_shared<IStorage>(std::move(memory))); 1070 rb.PushIpcInterface<IStorage>(std::move(memory));
1051} 1071}
1052 1072
1053IApplicationFunctions::IApplicationFunctions(Core::System& system_) 1073IApplicationFunctions::IApplicationFunctions(Core::System& system_)
@@ -1189,13 +1209,11 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
1189 u64 build_id{}; 1209 u64 build_id{};
1190 std::memcpy(&build_id, build_id_full.data(), sizeof(u64)); 1210 std::memcpy(&build_id, build_id_full.data(), sizeof(u64));
1191 1211
1192 const auto data = 1212 auto data = backend->GetLaunchParameter({system.CurrentProcess()->GetTitleID(), build_id});
1193 backend->GetLaunchParameter({system.CurrentProcess()->GetTitleID(), build_id});
1194
1195 if (data.has_value()) { 1213 if (data.has_value()) {
1196 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 1214 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1197 rb.Push(RESULT_SUCCESS); 1215 rb.Push(RESULT_SUCCESS);
1198 rb.PushIpcInterface<AM::IStorage>(*data); 1216 rb.PushIpcInterface<IStorage>(std::move(*data));
1199 launch_popped_application_specific = true; 1217 launch_popped_application_specific = true;
1200 return; 1218 return;
1201 } 1219 }
@@ -1218,7 +1236,7 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
1218 std::vector<u8> buffer(sizeof(LaunchParameterAccountPreselectedUser)); 1236 std::vector<u8> buffer(sizeof(LaunchParameterAccountPreselectedUser));
1219 std::memcpy(buffer.data(), &params, buffer.size()); 1237 std::memcpy(buffer.data(), &params, buffer.size());
1220 1238
1221 rb.PushIpcInterface<AM::IStorage>(buffer); 1239 rb.PushIpcInterface<IStorage>(std::move(buffer));
1222 launch_popped_account_preselect = true; 1240 launch_popped_account_preselect = true;
1223 return; 1241 return;
1224 } 1242 }
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 448817be9..0b9a4332d 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -12,7 +12,8 @@
12 12
13namespace Kernel { 13namespace Kernel {
14class KernelCore; 14class KernelCore;
15} 15class TransferMemory;
16} // namespace Kernel
16 17
17namespace Service::NVFlinger { 18namespace Service::NVFlinger {
18class NVFlinger; 19class NVFlinger;
@@ -188,19 +189,36 @@ private:
188 std::shared_ptr<AppletMessageQueue> msg_queue; 189 std::shared_ptr<AppletMessageQueue> msg_queue;
189}; 190};
190 191
192class IStorageImpl {
193public:
194 virtual ~IStorageImpl();
195 virtual std::vector<u8>& GetData() = 0;
196 virtual const std::vector<u8>& GetData() const = 0;
197 virtual std::size_t GetSize() const = 0;
198};
199
191class IStorage final : public ServiceFramework<IStorage> { 200class IStorage final : public ServiceFramework<IStorage> {
192public: 201public:
193 explicit IStorage(std::vector<u8> buffer); 202 explicit IStorage(std::vector<u8>&& buffer);
194 ~IStorage() override; 203 ~IStorage() override;
195 204
196 const std::vector<u8>& GetData() const; 205 std::vector<u8>& GetData() {
206 return impl->GetData();
207 }
208
209 const std::vector<u8>& GetData() const {
210 return impl->GetData();
211 }
212
213 std::size_t GetSize() const {
214 return impl->GetSize();
215 }
197 216
198private: 217private:
218 void Register();
199 void Open(Kernel::HLERequestContext& ctx); 219 void Open(Kernel::HLERequestContext& ctx);
200 220
201 std::vector<u8> buffer; 221 std::shared_ptr<IStorageImpl> impl;
202
203 friend class IStorageAccessor;
204}; 222};
205 223
206class IStorageAccessor final : public ServiceFramework<IStorageAccessor> { 224class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index 92f995f8f..c3261f3e6 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -50,16 +50,17 @@ AppletDataBroker::RawChannelData AppletDataBroker::PeekDataToAppletForDebug() co
50 return {std::move(out_normal), std::move(out_interactive)}; 50 return {std::move(out_normal), std::move(out_interactive)};
51} 51}
52 52
53std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() { 53std::shared_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() {
54 if (out_channel.empty()) 54 if (out_channel.empty())
55 return nullptr; 55 return nullptr;
56 56
57 auto out = std::move(out_channel.front()); 57 auto out = std::move(out_channel.front());
58 out_channel.pop_front(); 58 out_channel.pop_front();
59 pop_out_data_event.writable->Clear();
59 return out; 60 return out;
60} 61}
61 62
62std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() { 63std::shared_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() {
63 if (in_channel.empty()) 64 if (in_channel.empty())
64 return nullptr; 65 return nullptr;
65 66
@@ -68,16 +69,17 @@ std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() {
68 return out; 69 return out;
69} 70}
70 71
71std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() { 72std::shared_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() {
72 if (out_interactive_channel.empty()) 73 if (out_interactive_channel.empty())
73 return nullptr; 74 return nullptr;
74 75
75 auto out = std::move(out_interactive_channel.front()); 76 auto out = std::move(out_interactive_channel.front());
76 out_interactive_channel.pop_front(); 77 out_interactive_channel.pop_front();
78 pop_interactive_out_data_event.writable->Clear();
77 return out; 79 return out;
78} 80}
79 81
80std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() { 82std::shared_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() {
81 if (in_interactive_channel.empty()) 83 if (in_interactive_channel.empty())
82 return nullptr; 84 return nullptr;
83 85
@@ -86,21 +88,21 @@ std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() {
86 return out; 88 return out;
87} 89}
88 90
89void AppletDataBroker::PushNormalDataFromGame(IStorage storage) { 91void AppletDataBroker::PushNormalDataFromGame(std::shared_ptr<IStorage>&& storage) {
90 in_channel.push_back(std::make_unique<IStorage>(storage)); 92 in_channel.emplace_back(std::move(storage));
91} 93}
92 94
93void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) { 95void AppletDataBroker::PushNormalDataFromApplet(std::shared_ptr<IStorage>&& storage) {
94 out_channel.push_back(std::make_unique<IStorage>(storage)); 96 out_channel.emplace_back(std::move(storage));
95 pop_out_data_event.writable->Signal(); 97 pop_out_data_event.writable->Signal();
96} 98}
97 99
98void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) { 100void AppletDataBroker::PushInteractiveDataFromGame(std::shared_ptr<IStorage>&& storage) {
99 in_interactive_channel.push_back(std::make_unique<IStorage>(storage)); 101 in_interactive_channel.emplace_back(std::move(storage));
100} 102}
101 103
102void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) { 104void AppletDataBroker::PushInteractiveDataFromApplet(std::shared_ptr<IStorage>&& storage) {
103 out_interactive_channel.push_back(std::make_unique<IStorage>(storage)); 105 out_interactive_channel.emplace_back(std::move(storage));
104 pop_interactive_out_data_event.writable->Signal(); 106 pop_interactive_out_data_event.writable->Signal();
105} 107}
106 108
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index 16e61fc6f..e75be86a2 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -72,17 +72,17 @@ public:
72 // Retrieves but does not pop the data sent to applet. 72 // Retrieves but does not pop the data sent to applet.
73 RawChannelData PeekDataToAppletForDebug() const; 73 RawChannelData PeekDataToAppletForDebug() const;
74 74
75 std::unique_ptr<IStorage> PopNormalDataToGame(); 75 std::shared_ptr<IStorage> PopNormalDataToGame();
76 std::unique_ptr<IStorage> PopNormalDataToApplet(); 76 std::shared_ptr<IStorage> PopNormalDataToApplet();
77 77
78 std::unique_ptr<IStorage> PopInteractiveDataToGame(); 78 std::shared_ptr<IStorage> PopInteractiveDataToGame();
79 std::unique_ptr<IStorage> PopInteractiveDataToApplet(); 79 std::shared_ptr<IStorage> PopInteractiveDataToApplet();
80 80
81 void PushNormalDataFromGame(IStorage storage); 81 void PushNormalDataFromGame(std::shared_ptr<IStorage>&& storage);
82 void PushNormalDataFromApplet(IStorage storage); 82 void PushNormalDataFromApplet(std::shared_ptr<IStorage>&& storage);
83 83
84 void PushInteractiveDataFromGame(IStorage storage); 84 void PushInteractiveDataFromGame(std::shared_ptr<IStorage>&& storage);
85 void PushInteractiveDataFromApplet(IStorage storage); 85 void PushInteractiveDataFromApplet(std::shared_ptr<IStorage>&& storage);
86 86
87 void SignalStateChanged() const; 87 void SignalStateChanged() const;
88 88
@@ -94,16 +94,16 @@ private:
94 // Queues are named from applet's perspective 94 // Queues are named from applet's perspective
95 95
96 // PopNormalDataToApplet and PushNormalDataFromGame 96 // PopNormalDataToApplet and PushNormalDataFromGame
97 std::deque<std::unique_ptr<IStorage>> in_channel; 97 std::deque<std::shared_ptr<IStorage>> in_channel;
98 98
99 // PopNormalDataToGame and PushNormalDataFromApplet 99 // PopNormalDataToGame and PushNormalDataFromApplet
100 std::deque<std::unique_ptr<IStorage>> out_channel; 100 std::deque<std::shared_ptr<IStorage>> out_channel;
101 101
102 // PopInteractiveDataToApplet and PushInteractiveDataFromGame 102 // PopInteractiveDataToApplet and PushInteractiveDataFromGame
103 std::deque<std::unique_ptr<IStorage>> in_interactive_channel; 103 std::deque<std::shared_ptr<IStorage>> in_interactive_channel;
104 104
105 // PopInteractiveDataToGame and PushInteractiveDataFromApplet 105 // PopInteractiveDataToGame and PushInteractiveDataFromApplet
106 std::deque<std::unique_ptr<IStorage>> out_interactive_channel; 106 std::deque<std::shared_ptr<IStorage>> out_interactive_channel;
107 107
108 Kernel::EventPair state_changed_event; 108 Kernel::EventPair state_changed_event;
109 109
diff --git a/src/core/hle/service/am/applets/error.cpp b/src/core/hle/service/am/applets/error.cpp
index eab0d42c9..f12fd7f89 100644
--- a/src/core/hle/service/am/applets/error.cpp
+++ b/src/core/hle/service/am/applets/error.cpp
@@ -186,7 +186,7 @@ void Error::Execute() {
186 186
187void Error::DisplayCompleted() { 187void Error::DisplayCompleted() {
188 complete = true; 188 complete = true;
189 broker.PushNormalDataFromApplet(IStorage{{}}); 189 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::vector<u8>{}));
190 broker.SignalStateChanged(); 190 broker.SignalStateChanged();
191} 191}
192 192
diff --git a/src/core/hle/service/am/applets/general_backend.cpp b/src/core/hle/service/am/applets/general_backend.cpp
index 328438a1d..104501ac5 100644
--- a/src/core/hle/service/am/applets/general_backend.cpp
+++ b/src/core/hle/service/am/applets/general_backend.cpp
@@ -20,7 +20,7 @@ namespace Service::AM::Applets {
20constexpr ResultCode ERROR_INVALID_PIN{ErrorModule::PCTL, 221}; 20constexpr ResultCode ERROR_INVALID_PIN{ErrorModule::PCTL, 221};
21 21
22static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix) { 22static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix) {
23 std::unique_ptr<IStorage> storage = broker.PopNormalDataToApplet(); 23 std::shared_ptr<IStorage> storage = broker.PopNormalDataToApplet();
24 for (; storage != nullptr; storage = broker.PopNormalDataToApplet()) { 24 for (; storage != nullptr; storage = broker.PopNormalDataToApplet()) {
25 const auto data = storage->GetData(); 25 const auto data = storage->GetData();
26 LOG_INFO(Service_AM, 26 LOG_INFO(Service_AM,
@@ -148,7 +148,7 @@ void Auth::AuthFinished(bool successful) {
148 std::vector<u8> out(sizeof(Return)); 148 std::vector<u8> out(sizeof(Return));
149 std::memcpy(out.data(), &return_, sizeof(Return)); 149 std::memcpy(out.data(), &return_, sizeof(Return));
150 150
151 broker.PushNormalDataFromApplet(IStorage{out}); 151 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(out)));
152 broker.SignalStateChanged(); 152 broker.SignalStateChanged();
153} 153}
154 154
@@ -198,7 +198,7 @@ void PhotoViewer::Execute() {
198} 198}
199 199
200void PhotoViewer::ViewFinished() { 200void PhotoViewer::ViewFinished() {
201 broker.PushNormalDataFromApplet(IStorage{{}}); 201 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::vector<u8>{}));
202 broker.SignalStateChanged(); 202 broker.SignalStateChanged();
203} 203}
204 204
@@ -234,8 +234,8 @@ void StubApplet::ExecuteInteractive() {
234 LOG_WARNING(Service_AM, "called (STUBBED)"); 234 LOG_WARNING(Service_AM, "called (STUBBED)");
235 LogCurrentStorage(broker, "ExecuteInteractive"); 235 LogCurrentStorage(broker, "ExecuteInteractive");
236 236
237 broker.PushNormalDataFromApplet(IStorage{std::vector<u8>(0x1000)}); 237 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::vector<u8>(0x1000)));
238 broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)}); 238 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(std::vector<u8>(0x1000)));
239 broker.SignalStateChanged(); 239 broker.SignalStateChanged();
240} 240}
241 241
@@ -243,8 +243,8 @@ void StubApplet::Execute() {
243 LOG_WARNING(Service_AM, "called (STUBBED)"); 243 LOG_WARNING(Service_AM, "called (STUBBED)");
244 LogCurrentStorage(broker, "Execute"); 244 LogCurrentStorage(broker, "Execute");
245 245
246 broker.PushNormalDataFromApplet(IStorage{std::vector<u8>(0x1000)}); 246 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::vector<u8>(0x1000)));
247 broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)}); 247 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(std::vector<u8>(0x1000)));
248 broker.SignalStateChanged(); 248 broker.SignalStateChanged();
249} 249}
250 250
diff --git a/src/core/hle/service/am/applets/profile_select.cpp b/src/core/hle/service/am/applets/profile_select.cpp
index 3eba696ca..70cc23552 100644
--- a/src/core/hle/service/am/applets/profile_select.cpp
+++ b/src/core/hle/service/am/applets/profile_select.cpp
@@ -50,7 +50,7 @@ void ProfileSelect::ExecuteInteractive() {
50 50
51void ProfileSelect::Execute() { 51void ProfileSelect::Execute() {
52 if (complete) { 52 if (complete) {
53 broker.PushNormalDataFromApplet(IStorage{final_data}); 53 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(final_data)));
54 return; 54 return;
55 } 55 }
56 56
@@ -71,7 +71,7 @@ void ProfileSelect::SelectionComplete(std::optional<Common::UUID> uuid) {
71 71
72 final_data = std::vector<u8>(sizeof(UserSelectionOutput)); 72 final_data = std::vector<u8>(sizeof(UserSelectionOutput));
73 std::memcpy(final_data.data(), &output, final_data.size()); 73 std::memcpy(final_data.data(), &output, final_data.size());
74 broker.PushNormalDataFromApplet(IStorage{final_data}); 74 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(final_data)));
75 broker.SignalStateChanged(); 75 broker.SignalStateChanged();
76} 76}
77 77
diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp
index 748559cd0..54e63c138 100644
--- a/src/core/hle/service/am/applets/software_keyboard.cpp
+++ b/src/core/hle/service/am/applets/software_keyboard.cpp
@@ -102,7 +102,8 @@ void SoftwareKeyboard::ExecuteInteractive() {
102 102
103void SoftwareKeyboard::Execute() { 103void SoftwareKeyboard::Execute() {
104 if (complete) { 104 if (complete) {
105 broker.PushNormalDataFromApplet(IStorage{final_data}); 105 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(final_data)));
106 broker.SignalStateChanged();
106 return; 107 return;
107 } 108 }
108 109
@@ -119,7 +120,7 @@ void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) {
119 std::vector<u8> output_sub(SWKBD_OUTPUT_BUFFER_SIZE); 120 std::vector<u8> output_sub(SWKBD_OUTPUT_BUFFER_SIZE);
120 121
121 if (config.utf_8) { 122 if (config.utf_8) {
122 const u64 size = text->size() + 8; 123 const u64 size = text->size() + sizeof(u64);
123 const auto new_text = Common::UTF16ToUTF8(*text); 124 const auto new_text = Common::UTF16ToUTF8(*text);
124 125
125 std::memcpy(output_sub.data(), &size, sizeof(u64)); 126 std::memcpy(output_sub.data(), &size, sizeof(u64));
@@ -130,7 +131,7 @@ void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) {
130 std::memcpy(output_main.data() + 4, new_text.data(), 131 std::memcpy(output_main.data() + 4, new_text.data(),
131 std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 4)); 132 std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 4));
132 } else { 133 } else {
133 const u64 size = text->size() * 2 + 8; 134 const u64 size = text->size() * 2 + sizeof(u64);
134 std::memcpy(output_sub.data(), &size, sizeof(u64)); 135 std::memcpy(output_sub.data(), &size, sizeof(u64));
135 std::memcpy(output_sub.data() + 8, text->data(), 136 std::memcpy(output_sub.data() + 8, text->data(),
136 std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 8)); 137 std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 8));
@@ -144,15 +145,15 @@ void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) {
144 final_data = output_main; 145 final_data = output_main;
145 146
146 if (complete) { 147 if (complete) {
147 broker.PushNormalDataFromApplet(IStorage{output_main}); 148 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(output_main)));
148 broker.SignalStateChanged(); 149 broker.SignalStateChanged();
149 } else { 150 } else {
150 broker.PushInteractiveDataFromApplet(IStorage{output_sub}); 151 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(std::move(output_sub)));
151 } 152 }
152 } else { 153 } else {
153 output_main[0] = 1; 154 output_main[0] = 1;
154 complete = true; 155 complete = true;
155 broker.PushNormalDataFromApplet(IStorage{output_main}); 156 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(output_main)));
156 broker.SignalStateChanged(); 157 broker.SignalStateChanged();
157 } 158 }
158} 159}
diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp
index 5546ef6e8..12443c910 100644
--- a/src/core/hle/service/am/applets/web_browser.cpp
+++ b/src/core/hle/service/am/applets/web_browser.cpp
@@ -284,7 +284,7 @@ void WebBrowser::Finalize() {
284 std::vector<u8> data(sizeof(WebCommonReturnValue)); 284 std::vector<u8> data(sizeof(WebCommonReturnValue));
285 std::memcpy(data.data(), &out, sizeof(WebCommonReturnValue)); 285 std::memcpy(data.data(), &out, sizeof(WebCommonReturnValue));
286 286
287 broker.PushNormalDataFromApplet(IStorage{data}); 287 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(data)));
288 broker.SignalStateChanged(); 288 broker.SignalStateChanged();
289 289
290 if (!temporary_dir.empty() && FileUtil::IsDirectory(temporary_dir)) { 290 if (!temporary_dir.empty() && FileUtil::IsDirectory(temporary_dir)) {
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index 55d62fc5e..e6811d5b5 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -420,7 +420,7 @@ public:
420 return; 420 return;
421 } 421 }
422 422
423 IFile file(result.Unwrap()); 423 auto file = std::make_shared<IFile>(result.Unwrap());
424 424
425 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 425 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
426 rb.Push(RESULT_SUCCESS); 426 rb.Push(RESULT_SUCCESS);
@@ -445,7 +445,7 @@ public:
445 return; 445 return;
446 } 446 }
447 447
448 IDirectory directory(result.Unwrap()); 448 auto directory = std::make_shared<IDirectory>(result.Unwrap());
449 449
450 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 450 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
451 rb.Push(RESULT_SUCCESS); 451 rb.Push(RESULT_SUCCESS);
@@ -794,8 +794,8 @@ void FSP_SRV::OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx) {
794void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) { 794void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) {
795 LOG_DEBUG(Service_FS, "called"); 795 LOG_DEBUG(Service_FS, "called");
796 796
797 IFileSystem filesystem(fsc.OpenSDMC().Unwrap(), 797 auto filesystem = std::make_shared<IFileSystem>(
798 SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard)); 798 fsc.OpenSDMC().Unwrap(), SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard));
799 799
800 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 800 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
801 rb.Push(RESULT_SUCCESS); 801 rb.Push(RESULT_SUCCESS);
@@ -846,7 +846,8 @@ void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
846 id = FileSys::StorageId::NandSystem; 846 id = FileSys::StorageId::NandSystem;
847 } 847 }
848 848
849 IFileSystem filesystem(std::move(dir.Unwrap()), SizeGetter::FromStorageId(fsc, id)); 849 auto filesystem =
850 std::make_shared<IFileSystem>(std::move(dir.Unwrap()), SizeGetter::FromStorageId(fsc, id));
850 851
851 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 852 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
852 rb.Push(RESULT_SUCCESS); 853 rb.Push(RESULT_SUCCESS);
@@ -898,7 +899,7 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
898 return; 899 return;
899 } 900 }
900 901
901 IStorage storage(std::move(romfs.Unwrap())); 902 auto storage = std::make_shared<IStorage>(std::move(romfs.Unwrap()));
902 903
903 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 904 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
904 rb.Push(RESULT_SUCCESS); 905 rb.Push(RESULT_SUCCESS);
@@ -937,7 +938,8 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
937 938
938 FileSys::PatchManager pm{title_id}; 939 FileSys::PatchManager pm{title_id};
939 940
940 IStorage storage(pm.PatchRomFS(std::move(data.Unwrap()), 0, FileSys::ContentRecordType::Data)); 941 auto storage = std::make_shared<IStorage>(
942 pm.PatchRomFS(std::move(data.Unwrap()), 0, FileSys::ContentRecordType::Data));
941 943
942 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 944 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
943 rb.Push(RESULT_SUCCESS); 945 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 4d952adc0..15c09f04c 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -250,6 +250,10 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
250 auto& rstick_entry = npad_pad_states[controller_idx].r_stick; 250 auto& rstick_entry = npad_pad_states[controller_idx].r_stick;
251 const auto& button_state = buttons[controller_idx]; 251 const auto& button_state = buttons[controller_idx];
252 const auto& analog_state = sticks[controller_idx]; 252 const auto& analog_state = sticks[controller_idx];
253 const auto [stick_l_x_f, stick_l_y_f] =
254 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
255 const auto [stick_r_x_f, stick_r_y_f] =
256 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
253 257
254 using namespace Settings::NativeButton; 258 using namespace Settings::NativeButton;
255 pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus()); 259 pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus());
@@ -270,23 +274,32 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
270 pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus()); 274 pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus());
271 pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus()); 275 pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus());
272 276
273 pad_state.l_stick_left.Assign(button_state[LStick_Left - BUTTON_HID_BEGIN]->GetStatus()); 277 pad_state.l_stick_right.Assign(
274 pad_state.l_stick_up.Assign(button_state[LStick_Up - BUTTON_HID_BEGIN]->GetStatus()); 278 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus(
275 pad_state.l_stick_right.Assign(button_state[LStick_Right - BUTTON_HID_BEGIN]->GetStatus()); 279 Input::AnalogDirection::RIGHT));
276 pad_state.l_stick_down.Assign(button_state[LStick_Down - BUTTON_HID_BEGIN]->GetStatus()); 280 pad_state.l_stick_left.Assign(
277 281 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus(
278 pad_state.r_stick_left.Assign(button_state[RStick_Left - BUTTON_HID_BEGIN]->GetStatus()); 282 Input::AnalogDirection::LEFT));
279 pad_state.r_stick_up.Assign(button_state[RStick_Up - BUTTON_HID_BEGIN]->GetStatus()); 283 pad_state.l_stick_up.Assign(
280 pad_state.r_stick_right.Assign(button_state[RStick_Right - BUTTON_HID_BEGIN]->GetStatus()); 284 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus(
281 pad_state.r_stick_down.Assign(button_state[RStick_Down - BUTTON_HID_BEGIN]->GetStatus()); 285 Input::AnalogDirection::UP));
286 pad_state.l_stick_down.Assign(
287 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus(
288 Input::AnalogDirection::DOWN));
289
290 pad_state.r_stick_up.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
291 ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT));
292 pad_state.r_stick_left.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
293 ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT));
294 pad_state.r_stick_right.Assign(
295 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
296 ->GetAnalogDirectionStatus(Input::AnalogDirection::UP));
297 pad_state.r_stick_down.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
298 ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN));
282 299
283 pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus()); 300 pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus());
284 pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus()); 301 pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus());
285 302
286 const auto [stick_l_x_f, stick_l_y_f] =
287 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
288 const auto [stick_r_x_f, stick_r_y_f] =
289 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
290 lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX); 303 lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
291 lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX); 304 lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
292 rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); 305 rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp
index 5eb26caf8..8f1be0e48 100644
--- a/src/core/hle/service/prepo/prepo.cpp
+++ b/src/core/hle/service/prepo/prepo.cpp
@@ -50,16 +50,16 @@ private:
50 IPC::RequestParser rp{ctx}; 50 IPC::RequestParser rp{ctx};
51 const auto process_id = rp.PopRaw<u64>(); 51 const auto process_id = rp.PopRaw<u64>();
52 52
53 const auto data1 = ctx.ReadBuffer(0); 53 std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)};
54 const auto data2 = ctx.ReadBuffer(1); 54 if (Type == Core::Reporter::PlayReportType::New) {
55 data.emplace_back(ctx.ReadBuffer(1));
56 }
55 57
56 LOG_DEBUG(Service_PREPO, 58 LOG_DEBUG(Service_PREPO, "called, type={:02X}, process_id={:016X}, data1_size={:016X}",
57 "called, type={:02X}, process_id={:016X}, data1_size={:016X}, data2_size={:016X}", 59 static_cast<u8>(Type), process_id, data[0].size());
58 static_cast<u8>(Type), process_id, data1.size(), data2.size());
59 60
60 const auto& reporter{system.GetReporter()}; 61 const auto& reporter{system.GetReporter()};
61 reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), {data1, data2}, 62 reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), data, process_id);
62 process_id);
63 63
64 IPC::ResponseBuilder rb{ctx, 2}; 64 IPC::ResponseBuilder rb{ctx, 2};
65 rb.Push(RESULT_SUCCESS); 65 rb.Push(RESULT_SUCCESS);
@@ -70,19 +70,19 @@ private:
70 IPC::RequestParser rp{ctx}; 70 IPC::RequestParser rp{ctx};
71 const auto user_id = rp.PopRaw<u128>(); 71 const auto user_id = rp.PopRaw<u128>();
72 const auto process_id = rp.PopRaw<u64>(); 72 const auto process_id = rp.PopRaw<u64>();
73 73 std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)};
74 const auto data1 = ctx.ReadBuffer(0); 74 if (Type == Core::Reporter::PlayReportType::New) {
75 const auto data2 = ctx.ReadBuffer(1); 75 data.emplace_back(ctx.ReadBuffer(1));
76 }
76 77
77 LOG_DEBUG( 78 LOG_DEBUG(
78 Service_PREPO, 79 Service_PREPO,
79 "called, type={:02X}, user_id={:016X}{:016X}, process_id={:016X}, data1_size={:016X}, " 80 "called, type={:02X}, user_id={:016X}{:016X}, process_id={:016X}, data1_size={:016X}",
80 "data2_size={:016X}", 81 static_cast<u8>(Type), user_id[1], user_id[0], process_id, data[0].size());
81 static_cast<u8>(Type), user_id[1], user_id[0], process_id, data1.size(), data2.size());
82 82
83 const auto& reporter{system.GetReporter()}; 83 const auto& reporter{system.GetReporter()};
84 reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), {data1, data2}, 84 reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), data, process_id,
85 process_id, user_id); 85 user_id);
86 86
87 IPC::ResponseBuilder rb{ctx, 2}; 87 IPC::ResponseBuilder rb{ctx, 2};
88 rb.Push(RESULT_SUCCESS); 88 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/settings.h b/src/core/settings.h
index 421e76f5f..e1a9a0ffa 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -371,6 +371,11 @@ enum class SDMCSize : u64 {
371 S1TB = 0x10000000000ULL, 371 S1TB = 0x10000000000ULL,
372}; 372};
373 373
374enum class RendererBackend {
375 OpenGL = 0,
376 Vulkan = 1,
377};
378
374struct Values { 379struct Values {
375 // System 380 // System
376 bool use_docked_mode; 381 bool use_docked_mode;
@@ -419,6 +424,10 @@ struct Values {
419 SDMCSize sdmc_size; 424 SDMCSize sdmc_size;
420 425
421 // Renderer 426 // Renderer
427 RendererBackend renderer_backend;
428 bool renderer_debug;
429 int vulkan_device;
430
422 float resolution_factor; 431 float resolution_factor;
423 bool use_frame_limit; 432 bool use_frame_limit;
424 u16 frame_limit; 433 u16 frame_limit;
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 320e8ad73..0e72d31cd 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -46,6 +46,16 @@ static u64 GenerateTelemetryId() {
46 return telemetry_id; 46 return telemetry_id;
47} 47}
48 48
49static const char* TranslateRenderer(Settings::RendererBackend backend) {
50 switch (backend) {
51 case Settings::RendererBackend::OpenGL:
52 return "OpenGL";
53 case Settings::RendererBackend::Vulkan:
54 return "Vulkan";
55 }
56 return "Unknown";
57}
58
49u64 GetTelemetryId() { 59u64 GetTelemetryId() {
50 u64 telemetry_id{}; 60 u64 telemetry_id{};
51 const std::string filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + 61 const std::string filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) +
@@ -169,7 +179,7 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
169 AddField(field_type, "Audio_SinkId", Settings::values.sink_id); 179 AddField(field_type, "Audio_SinkId", Settings::values.sink_id);
170 AddField(field_type, "Audio_EnableAudioStretching", Settings::values.enable_audio_stretching); 180 AddField(field_type, "Audio_EnableAudioStretching", Settings::values.enable_audio_stretching);
171 AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core); 181 AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core);
172 AddField(field_type, "Renderer_Backend", "OpenGL"); 182 AddField(field_type, "Renderer_Backend", TranslateRenderer(Settings::values.renderer_backend));
173 AddField(field_type, "Renderer_ResolutionFactor", Settings::values.resolution_factor); 183 AddField(field_type, "Renderer_ResolutionFactor", Settings::values.resolution_factor);
174 AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit); 184 AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit);
175 AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit); 185 AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit);
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index 9e028da89..c98c848cf 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -41,6 +41,7 @@ void Shutdown() {
41 Input::UnregisterFactory<Input::MotionDevice>("motion_emu"); 41 Input::UnregisterFactory<Input::MotionDevice>("motion_emu");
42 motion_emu.reset(); 42 motion_emu.reset();
43 sdl.reset(); 43 sdl.reset();
44 udp.reset();
44} 45}
45 46
46Keyboard* GetKeyboard() { 47Keyboard* GetKeyboard() {
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index d2e9d278f..a2e0c0bd2 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -342,6 +342,22 @@ public:
342 return std::make_tuple<float, float>(0.0f, 0.0f); 342 return std::make_tuple<float, float>(0.0f, 0.0f);
343 } 343 }
344 344
345 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
346 const auto [x, y] = GetStatus();
347 const float directional_deadzone = 0.4f;
348 switch (direction) {
349 case Input::AnalogDirection::RIGHT:
350 return x > directional_deadzone;
351 case Input::AnalogDirection::LEFT:
352 return x < -directional_deadzone;
353 case Input::AnalogDirection::UP:
354 return y > directional_deadzone;
355 case Input::AnalogDirection::DOWN:
356 return y < -directional_deadzone;
357 }
358 return false;
359 }
360
345private: 361private:
346 std::shared_ptr<SDLJoystick> joystick; 362 std::shared_ptr<SDLJoystick> joystick;
347 const int axis_x; 363 const int axis_x;
diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp
index 5f5a9989c..2228571a6 100644
--- a/src/input_common/udp/client.cpp
+++ b/src/input_common/udp/client.cpp
@@ -14,7 +14,6 @@
14#include "input_common/udp/client.h" 14#include "input_common/udp/client.h"
15#include "input_common/udp/protocol.h" 15#include "input_common/udp/protocol.h"
16 16
17using boost::asio::ip::address_v4;
18using boost::asio::ip::udp; 17using boost::asio::ip::udp;
19 18
20namespace InputCommon::CemuhookUDP { 19namespace InputCommon::CemuhookUDP {
@@ -31,10 +30,10 @@ public:
31 30
32 explicit Socket(const std::string& host, u16 port, u8 pad_index, u32 client_id, 31 explicit Socket(const std::string& host, u16 port, u8 pad_index, u32 client_id,
33 SocketCallback callback) 32 SocketCallback callback)
34 : client_id(client_id), timer(io_service), 33 : callback(std::move(callback)), timer(io_service),
35 send_endpoint(udp::endpoint(address_v4::from_string(host), port)), 34 socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(client_id),
36 socket(io_service, udp::endpoint(udp::v4(), 0)), pad_index(pad_index), 35 pad_index(pad_index),
37 callback(std::move(callback)) {} 36 send_endpoint(udp::endpoint(boost::asio::ip::make_address_v4(host), port)) {}
38 37
39 void Stop() { 38 void Stop() {
40 io_service.stop(); 39 io_service.stop();
@@ -126,7 +125,7 @@ static void SocketLoop(Socket* socket) {
126 125
127Client::Client(std::shared_ptr<DeviceStatus> status, const std::string& host, u16 port, 126Client::Client(std::shared_ptr<DeviceStatus> status, const std::string& host, u16 port,
128 u8 pad_index, u32 client_id) 127 u8 pad_index, u32 client_id)
129 : status(status) { 128 : status(std::move(status)) {
130 StartCommunication(host, port, pad_index, client_id); 129 StartCommunication(host, port, pad_index, client_id);
131} 130}
132 131
@@ -207,7 +206,7 @@ void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 clie
207 Common::Event success_event; 206 Common::Event success_event;
208 SocketCallback callback{[](Response::Version version) {}, [](Response::PortInfo info) {}, 207 SocketCallback callback{[](Response::Version version) {}, [](Response::PortInfo info) {},
209 [&](Response::PadData data) { success_event.Set(); }}; 208 [&](Response::PadData data) { success_event.Set(); }};
210 Socket socket{host, port, pad_index, client_id, callback}; 209 Socket socket{host, port, pad_index, client_id, std::move(callback)};
211 std::thread worker_thread{SocketLoop, &socket}; 210 std::thread worker_thread{SocketLoop, &socket};
212 bool result = success_event.WaitFor(std::chrono::seconds(8)); 211 bool result = success_event.WaitFor(std::chrono::seconds(8));
213 socket.Stop(); 212 socket.Stop();
@@ -267,7 +266,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
267 complete_event.Set(); 266 complete_event.Set();
268 } 267 }
269 }}; 268 }};
270 Socket socket{host, port, pad_index, client_id, callback}; 269 Socket socket{host, port, pad_index, client_id, std::move(callback)};
271 std::thread worker_thread{SocketLoop, &socket}; 270 std::thread worker_thread{SocketLoop, &socket};
272 complete_event.Wait(); 271 complete_event.Wait();
273 socket.Stop(); 272 socket.Stop();
diff --git a/src/input_common/udp/client.h b/src/input_common/udp/client.h
index 0b21f4da6..b8c654755 100644
--- a/src/input_common/udp/client.h
+++ b/src/input_common/udp/client.h
@@ -11,7 +11,6 @@
11#include <string> 11#include <string>
12#include <thread> 12#include <thread>
13#include <tuple> 13#include <tuple>
14#include <vector>
15#include "common/common_types.h" 14#include "common/common_types.h"
16#include "common/thread.h" 15#include "common/thread.h"
17#include "common/vector_math.h" 16#include "common/vector_math.h"
diff --git a/src/input_common/udp/protocol.h b/src/input_common/udp/protocol.h
index 1b521860a..3ba4d1fc8 100644
--- a/src/input_common/udp/protocol.h
+++ b/src/input_common/udp/protocol.h
@@ -7,7 +7,6 @@
7#include <array> 7#include <array>
8#include <optional> 8#include <optional>
9#include <type_traits> 9#include <type_traits>
10#include <vector>
11#include <boost/crc.hpp> 10#include <boost/crc.hpp>
12#include "common/bit_field.h" 11#include "common/bit_field.h"
13#include "common/swap.h" 12#include "common/swap.h"
diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp
index a80f38614..ca99cc22f 100644
--- a/src/input_common/udp/udp.cpp
+++ b/src/input_common/udp/udp.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/logging/log.h" 5#include <mutex>
6#include <tuple>
7
6#include "common/param_package.h" 8#include "common/param_package.h"
7#include "core/frontend/input.h" 9#include "core/frontend/input.h"
8#include "core/settings.h" 10#include "core/settings.h"
@@ -14,7 +16,7 @@ namespace InputCommon::CemuhookUDP {
14class UDPTouchDevice final : public Input::TouchDevice { 16class UDPTouchDevice final : public Input::TouchDevice {
15public: 17public:
16 explicit UDPTouchDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} 18 explicit UDPTouchDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {}
17 std::tuple<float, float, bool> GetStatus() const { 19 std::tuple<float, float, bool> GetStatus() const override {
18 std::lock_guard guard(status->update_mutex); 20 std::lock_guard guard(status->update_mutex);
19 return status->touch_status; 21 return status->touch_status;
20 } 22 }
@@ -26,7 +28,7 @@ private:
26class UDPMotionDevice final : public Input::MotionDevice { 28class UDPMotionDevice final : public Input::MotionDevice {
27public: 29public:
28 explicit UDPMotionDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} 30 explicit UDPMotionDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {}
29 std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() const { 31 std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() const override {
30 std::lock_guard guard(status->update_mutex); 32 std::lock_guard guard(status->update_mutex);
31 return status->motion_status; 33 return status->motion_status;
32 } 34 }
diff --git a/src/input_common/udp/udp.h b/src/input_common/udp/udp.h
index ea3de60bb..4f83f0441 100644
--- a/src/input_common/udp/udp.h
+++ b/src/input_common/udp/udp.h
@@ -2,15 +2,13 @@
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#pragma once
6
5#include <memory> 7#include <memory>
6#include <unordered_map>
7#include "input_common/main.h"
8#include "input_common/udp/client.h"
9 8
10namespace InputCommon::CemuhookUDP { 9namespace InputCommon::CemuhookUDP {
11 10
12class UDPTouchDevice; 11class Client;
13class UDPMotionDevice;
14 12
15class State { 13class State {
16public: 14public:
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index ccfed4f2e..db9332d00 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -29,6 +29,8 @@ add_library(video_core STATIC
29 gpu_synch.h 29 gpu_synch.h
30 gpu_thread.cpp 30 gpu_thread.cpp
31 gpu_thread.h 31 gpu_thread.h
32 guest_driver.cpp
33 guest_driver.h
32 macro_interpreter.cpp 34 macro_interpreter.cpp
33 macro_interpreter.h 35 macro_interpreter.h
34 memory_manager.cpp 36 memory_manager.cpp
@@ -154,6 +156,7 @@ if (ENABLE_VULKAN)
154 renderer_vulkan/maxwell_to_vk.cpp 156 renderer_vulkan/maxwell_to_vk.cpp
155 renderer_vulkan/maxwell_to_vk.h 157 renderer_vulkan/maxwell_to_vk.h
156 renderer_vulkan/renderer_vulkan.h 158 renderer_vulkan/renderer_vulkan.h
159 renderer_vulkan/renderer_vulkan.cpp
157 renderer_vulkan/vk_blit_screen.cpp 160 renderer_vulkan/vk_blit_screen.cpp
158 renderer_vulkan/vk_blit_screen.h 161 renderer_vulkan/vk_blit_screen.h
159 renderer_vulkan/vk_buffer_cache.cpp 162 renderer_vulkan/vk_buffer_cache.cpp
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 0510ed777..186aca61d 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -101,7 +101,10 @@ public:
101 void TickFrame() { 101 void TickFrame() {
102 ++epoch; 102 ++epoch;
103 while (!pending_destruction.empty()) { 103 while (!pending_destruction.empty()) {
104 if (pending_destruction.front()->GetEpoch() + 1 > epoch) { 104 // Delay at least 4 frames before destruction.
105 // This is due to triple buffering happening on some drivers.
106 static constexpr u64 epochs_to_destroy = 5;
107 if (pending_destruction.front()->GetEpoch() + epochs_to_destroy > epoch) {
105 break; 108 break;
106 } 109 }
107 pending_destruction.pop_front(); 110 pending_destruction.pop_front();
diff --git a/src/video_core/engines/const_buffer_engine_interface.h b/src/video_core/engines/const_buffer_engine_interface.h
index 44b8b8d22..d56a47710 100644
--- a/src/video_core/engines/const_buffer_engine_interface.h
+++ b/src/video_core/engines/const_buffer_engine_interface.h
@@ -9,6 +9,7 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "video_core/engines/shader_bytecode.h" 10#include "video_core/engines/shader_bytecode.h"
11#include "video_core/engines/shader_type.h" 11#include "video_core/engines/shader_type.h"
12#include "video_core/guest_driver.h"
12#include "video_core/textures/texture.h" 13#include "video_core/textures/texture.h"
13 14
14namespace Tegra::Engines { 15namespace Tegra::Engines {
@@ -106,6 +107,9 @@ public:
106 virtual SamplerDescriptor AccessBindlessSampler(ShaderType stage, u64 const_buffer, 107 virtual SamplerDescriptor AccessBindlessSampler(ShaderType stage, u64 const_buffer,
107 u64 offset) const = 0; 108 u64 offset) const = 0;
108 virtual u32 GetBoundBuffer() const = 0; 109 virtual u32 GetBoundBuffer() const = 0;
110
111 virtual VideoCore::GuestDriverProfile& AccessGuestDriverProfile() = 0;
112 virtual const VideoCore::GuestDriverProfile& AccessGuestDriverProfile() const = 0;
109}; 113};
110 114
111} // namespace Tegra::Engines 115} // namespace Tegra::Engines
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp
index 110406f2f..4b824aa4e 100644
--- a/src/video_core/engines/kepler_compute.cpp
+++ b/src/video_core/engines/kepler_compute.cpp
@@ -94,6 +94,14 @@ SamplerDescriptor KeplerCompute::AccessBindlessSampler(ShaderType stage, u64 con
94 return result; 94 return result;
95} 95}
96 96
97VideoCore::GuestDriverProfile& KeplerCompute::AccessGuestDriverProfile() {
98 return rasterizer.AccessGuestDriverProfile();
99}
100
101const VideoCore::GuestDriverProfile& KeplerCompute::AccessGuestDriverProfile() const {
102 return rasterizer.AccessGuestDriverProfile();
103}
104
97void KeplerCompute::ProcessLaunch() { 105void KeplerCompute::ProcessLaunch() {
98 const GPUVAddr launch_desc_loc = regs.launch_desc_loc.Address(); 106 const GPUVAddr launch_desc_loc = regs.launch_desc_loc.Address();
99 memory_manager.ReadBlockUnsafe(launch_desc_loc, &launch_description, 107 memory_manager.ReadBlockUnsafe(launch_desc_loc, &launch_description,
diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h
index 4ef3e0613..eeb79c56f 100644
--- a/src/video_core/engines/kepler_compute.h
+++ b/src/video_core/engines/kepler_compute.h
@@ -218,6 +218,10 @@ public:
218 return regs.tex_cb_index; 218 return regs.tex_cb_index;
219 } 219 }
220 220
221 VideoCore::GuestDriverProfile& AccessGuestDriverProfile() override;
222
223 const VideoCore::GuestDriverProfile& AccessGuestDriverProfile() const override;
224
221private: 225private:
222 Core::System& system; 226 Core::System& system;
223 VideoCore::RasterizerInterface& rasterizer; 227 VideoCore::RasterizerInterface& rasterizer;
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 58dfa8033..7cea146f0 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -784,4 +784,12 @@ SamplerDescriptor Maxwell3D::AccessBindlessSampler(ShaderType stage, u64 const_b
784 return result; 784 return result;
785} 785}
786 786
787VideoCore::GuestDriverProfile& Maxwell3D::AccessGuestDriverProfile() {
788 return rasterizer.AccessGuestDriverProfile();
789}
790
791const VideoCore::GuestDriverProfile& Maxwell3D::AccessGuestDriverProfile() const {
792 return rasterizer.AccessGuestDriverProfile();
793}
794
787} // namespace Tegra::Engines 795} // namespace Tegra::Engines
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index ee79260fc..8808bbf76 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -1306,6 +1306,10 @@ public:
1306 return regs.tex_cb_index; 1306 return regs.tex_cb_index;
1307 } 1307 }
1308 1308
1309 VideoCore::GuestDriverProfile& AccessGuestDriverProfile() override;
1310
1311 const VideoCore::GuestDriverProfile& AccessGuestDriverProfile() const override;
1312
1309 /// Memory for macro code - it's undetermined how big this is, however 1MB is much larger than 1313 /// Memory for macro code - it's undetermined how big this is, however 1MB is much larger than
1310 /// we've seen used. 1314 /// we've seen used.
1311 using MacroMemory = std::array<u32, 0x40000>; 1315 using MacroMemory = std::array<u32, 0x40000>;
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index f443ec0fe..402869fde 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -624,6 +624,19 @@ enum class ShuffleOperation : u64 {
624 Bfly = 3, // shuffleXorNV 624 Bfly = 3, // shuffleXorNV
625}; 625};
626 626
627enum class ShfType : u64 {
628 Bits32 = 0,
629 U64 = 2,
630 S64 = 3,
631};
632
633enum class ShfXmode : u64 {
634 None = 0,
635 HI = 1,
636 X = 2,
637 XHI = 3,
638};
639
627union Instruction { 640union Instruction {
628 constexpr Instruction& operator=(const Instruction& instr) { 641 constexpr Instruction& operator=(const Instruction& instr) {
629 value = instr.value; 642 value = instr.value;
@@ -776,6 +789,13 @@ union Instruction {
776 } shr; 789 } shr;
777 790
778 union { 791 union {
792 BitField<37, 2, ShfType> type;
793 BitField<48, 2, ShfXmode> xmode;
794 BitField<50, 1, u64> wrap;
795 BitField<20, 6, u64> immediate;
796 } shf;
797
798 union {
779 BitField<39, 5, u64> shift_amount; 799 BitField<39, 5, u64> shift_amount;
780 BitField<48, 1, u64> negate_b; 800 BitField<48, 1, u64> negate_b;
781 BitField<49, 1, u64> negate_a; 801 BitField<49, 1, u64> negate_a;
@@ -1124,6 +1144,11 @@ union Instruction {
1124 } fset; 1144 } fset;
1125 1145
1126 union { 1146 union {
1147 BitField<47, 1, u64> ftz;
1148 BitField<48, 4, PredCondition> cond;
1149 } fcmp;
1150
1151 union {
1127 BitField<49, 1, u64> bf; 1152 BitField<49, 1, u64> bf;
1128 BitField<35, 3, PredCondition> cond; 1153 BitField<35, 3, PredCondition> cond;
1129 BitField<50, 1, u64> ftz; 1154 BitField<50, 1, u64> ftz;
@@ -1703,6 +1728,7 @@ public:
1703 BFE_C, 1728 BFE_C,
1704 BFE_R, 1729 BFE_R,
1705 BFE_IMM, 1730 BFE_IMM,
1731 BFI_RC,
1706 BFI_IMM_R, 1732 BFI_IMM_R,
1707 BRA, 1733 BRA,
1708 BRX, 1734 BRX,
@@ -1800,6 +1826,7 @@ public:
1800 ICMP_R, 1826 ICMP_R,
1801 ICMP_CR, 1827 ICMP_CR,
1802 ICMP_IMM, 1828 ICMP_IMM,
1829 FCMP_R,
1803 MUFU, // Multi-Function Operator 1830 MUFU, // Multi-Function Operator
1804 RRO_C, // Range Reduction Operator 1831 RRO_C, // Range Reduction Operator
1805 RRO_R, 1832 RRO_R,
@@ -2104,6 +2131,7 @@ private:
2104 INST("0101110100100---", Id::HSETP2_R, Type::HalfSetPredicate, "HSETP2_R"), 2131 INST("0101110100100---", Id::HSETP2_R, Type::HalfSetPredicate, "HSETP2_R"),
2105 INST("0111111-0-------", Id::HSETP2_IMM, Type::HalfSetPredicate, "HSETP2_IMM"), 2132 INST("0111111-0-------", Id::HSETP2_IMM, Type::HalfSetPredicate, "HSETP2_IMM"),
2106 INST("0101110100011---", Id::HSET2_R, Type::HalfSet, "HSET2_R"), 2133 INST("0101110100011---", Id::HSET2_R, Type::HalfSet, "HSET2_R"),
2134 INST("010110111010----", Id::FCMP_R, Type::Arithmetic, "FCMP_R"),
2107 INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"), 2135 INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"),
2108 INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"), 2136 INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"),
2109 INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"), 2137 INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"),
@@ -2128,6 +2156,7 @@ private:
2128 INST("0100110000000---", Id::BFE_C, Type::Bfe, "BFE_C"), 2156 INST("0100110000000---", Id::BFE_C, Type::Bfe, "BFE_C"),
2129 INST("0101110000000---", Id::BFE_R, Type::Bfe, "BFE_R"), 2157 INST("0101110000000---", Id::BFE_R, Type::Bfe, "BFE_R"),
2130 INST("0011100-00000---", Id::BFE_IMM, Type::Bfe, "BFE_IMM"), 2158 INST("0011100-00000---", Id::BFE_IMM, Type::Bfe, "BFE_IMM"),
2159 INST("0101001111110---", Id::BFI_RC, Type::Bfi, "BFI_RC"),
2131 INST("0011011-11110---", Id::BFI_IMM_R, Type::Bfi, "BFI_IMM_R"), 2160 INST("0011011-11110---", Id::BFI_IMM_R, Type::Bfi, "BFI_IMM_R"),
2132 INST("0100110001000---", Id::LOP_C, Type::ArithmeticInteger, "LOP_C"), 2161 INST("0100110001000---", Id::LOP_C, Type::ArithmeticInteger, "LOP_C"),
2133 INST("0101110001000---", Id::LOP_R, Type::ArithmeticInteger, "LOP_R"), 2162 INST("0101110001000---", Id::LOP_R, Type::ArithmeticInteger, "LOP_R"),
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index b9c5c41a2..062ca83b8 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -23,7 +23,7 @@ MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192));
23GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer, bool is_async) 23GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer, bool is_async)
24 : system{system}, renderer{renderer}, is_async{is_async} { 24 : system{system}, renderer{renderer}, is_async{is_async} {
25 auto& rasterizer{renderer.Rasterizer()}; 25 auto& rasterizer{renderer.Rasterizer()};
26 memory_manager = std::make_unique<Tegra::MemoryManager>(system, rasterizer); 26 memory_manager = std::make_unique<Tegra::MemoryManager>(system);
27 dma_pusher = std::make_unique<Tegra::DmaPusher>(*this); 27 dma_pusher = std::make_unique<Tegra::DmaPusher>(*this);
28 maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager); 28 maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager);
29 fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer); 29 fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer);
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
index 08dc96bb3..882e2d9c7 100644
--- a/src/video_core/gpu_thread.h
+++ b/src/video_core/gpu_thread.h
@@ -86,7 +86,7 @@ struct CommandDataContainer {
86struct SynchState final { 86struct SynchState final {
87 std::atomic_bool is_running{true}; 87 std::atomic_bool is_running{true};
88 88
89 using CommandQueue = Common::SPSCQueue<CommandDataContainer>; 89 using CommandQueue = Common::MPSCQueue<CommandDataContainer>;
90 CommandQueue queue; 90 CommandQueue queue;
91 u64 last_fence{}; 91 u64 last_fence{};
92 std::atomic<u64> signaled_fence{}; 92 std::atomic<u64> signaled_fence{};
diff --git a/src/video_core/guest_driver.cpp b/src/video_core/guest_driver.cpp
new file mode 100644
index 000000000..6adef459e
--- /dev/null
+++ b/src/video_core/guest_driver.cpp
@@ -0,0 +1,36 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <limits>
7
8#include "video_core/guest_driver.h"
9
10namespace VideoCore {
11
12void GuestDriverProfile::DeduceTextureHandlerSize(std::vector<u32>&& bound_offsets) {
13 if (texture_handler_size_deduced) {
14 return;
15 }
16 const std::size_t size = bound_offsets.size();
17 if (size < 2) {
18 return;
19 }
20 std::sort(bound_offsets.begin(), bound_offsets.end(), std::less{});
21 u32 min_val = std::numeric_limits<u32>::max();
22 for (std::size_t i = 1; i < size; ++i) {
23 if (bound_offsets[i] == bound_offsets[i - 1]) {
24 continue;
25 }
26 const u32 new_min = bound_offsets[i] - bound_offsets[i - 1];
27 min_val = std::min(min_val, new_min);
28 }
29 if (min_val > 2) {
30 return;
31 }
32 texture_handler_size_deduced = true;
33 texture_handler_size = min_texture_handler_size * min_val;
34}
35
36} // namespace VideoCore
diff --git a/src/video_core/guest_driver.h b/src/video_core/guest_driver.h
new file mode 100644
index 000000000..fc1917347
--- /dev/null
+++ b/src/video_core/guest_driver.h
@@ -0,0 +1,41 @@
1// Copyright 2020 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 <vector>
8
9#include "common/common_types.h"
10
11namespace VideoCore {
12
13/**
14 * The GuestDriverProfile class is used to learn about the GPU drivers behavior and collect
15 * information necessary for impossible to avoid HLE methods like shader tracks as they are
16 * Entscheidungsproblems.
17 */
18class GuestDriverProfile {
19public:
20 void DeduceTextureHandlerSize(std::vector<u32>&& bound_offsets);
21
22 u32 GetTextureHandlerSize() const {
23 return texture_handler_size;
24 }
25
26 bool TextureHandlerSizeKnown() const {
27 return texture_handler_size_deduced;
28 }
29
30private:
31 // Minimum size of texture handler any driver can use.
32 static constexpr u32 min_texture_handler_size = 4;
33 // This goes with Vulkan and OpenGL standards but Nvidia GPUs can easily
34 // use 4 bytes instead. Thus, certain drivers may squish the size.
35 static constexpr u32 default_texture_handler_size = 8;
36
37 u32 texture_handler_size = default_texture_handler_size;
38 bool texture_handler_size_deduced = false;
39};
40
41} // namespace VideoCore
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 11848fbce..f1d50be3e 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -9,13 +9,12 @@
9#include "core/hle/kernel/process.h" 9#include "core/hle/kernel/process.h"
10#include "core/hle/kernel/vm_manager.h" 10#include "core/hle/kernel/vm_manager.h"
11#include "core/memory.h" 11#include "core/memory.h"
12#include "video_core/gpu.h"
12#include "video_core/memory_manager.h" 13#include "video_core/memory_manager.h"
13#include "video_core/rasterizer_interface.h"
14 14
15namespace Tegra { 15namespace Tegra {
16 16
17MemoryManager::MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer) 17MemoryManager::MemoryManager(Core::System& system) : system{system} {
18 : rasterizer{rasterizer}, system{system} {
19 std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr); 18 std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr);
20 std::fill(page_table.attributes.begin(), page_table.attributes.end(), 19 std::fill(page_table.attributes.begin(), page_table.attributes.end(),
21 Common::PageType::Unmapped); 20 Common::PageType::Unmapped);
@@ -84,7 +83,8 @@ GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) {
84 const auto cpu_addr = GpuToCpuAddress(gpu_addr); 83 const auto cpu_addr = GpuToCpuAddress(gpu_addr);
85 ASSERT(cpu_addr); 84 ASSERT(cpu_addr);
86 85
87 rasterizer.FlushAndInvalidateRegion(cache_addr, aligned_size); 86 system.GPU().FlushAndInvalidateRegion(cache_addr, aligned_size);
87
88 UnmapRange(gpu_addr, aligned_size); 88 UnmapRange(gpu_addr, aligned_size);
89 ASSERT(system.CurrentProcess() 89 ASSERT(system.CurrentProcess()
90 ->VMManager() 90 ->VMManager()
@@ -242,7 +242,7 @@ void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, const std::s
242 switch (page_table.attributes[page_index]) { 242 switch (page_table.attributes[page_index]) {
243 case Common::PageType::Memory: { 243 case Common::PageType::Memory: {
244 const u8* src_ptr{page_table.pointers[page_index] + page_offset}; 244 const u8* src_ptr{page_table.pointers[page_index] + page_offset};
245 rasterizer.FlushRegion(ToCacheAddr(src_ptr), copy_amount); 245 system.GPU().FlushRegion(ToCacheAddr(src_ptr), copy_amount);
246 std::memcpy(dest_buffer, src_ptr, copy_amount); 246 std::memcpy(dest_buffer, src_ptr, copy_amount);
247 break; 247 break;
248 } 248 }
@@ -292,7 +292,7 @@ void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, const
292 switch (page_table.attributes[page_index]) { 292 switch (page_table.attributes[page_index]) {
293 case Common::PageType::Memory: { 293 case Common::PageType::Memory: {
294 u8* dest_ptr{page_table.pointers[page_index] + page_offset}; 294 u8* dest_ptr{page_table.pointers[page_index] + page_offset};
295 rasterizer.InvalidateRegion(ToCacheAddr(dest_ptr), copy_amount); 295 system.GPU().InvalidateRegion(ToCacheAddr(dest_ptr), copy_amount);
296 std::memcpy(dest_ptr, src_buffer, copy_amount); 296 std::memcpy(dest_ptr, src_buffer, copy_amount);
297 break; 297 break;
298 } 298 }
@@ -340,7 +340,7 @@ void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, const std::
340 switch (page_table.attributes[page_index]) { 340 switch (page_table.attributes[page_index]) {
341 case Common::PageType::Memory: { 341 case Common::PageType::Memory: {
342 const u8* src_ptr{page_table.pointers[page_index] + page_offset}; 342 const u8* src_ptr{page_table.pointers[page_index] + page_offset};
343 rasterizer.FlushRegion(ToCacheAddr(src_ptr), copy_amount); 343 system.GPU().FlushRegion(ToCacheAddr(src_ptr), copy_amount);
344 WriteBlock(dest_addr, src_ptr, copy_amount); 344 WriteBlock(dest_addr, src_ptr, copy_amount);
345 break; 345 break;
346 } 346 }
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index aea010087..393447eb4 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -10,10 +10,6 @@
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/page_table.h" 11#include "common/page_table.h"
12 12
13namespace VideoCore {
14class RasterizerInterface;
15}
16
17namespace Core { 13namespace Core {
18class System; 14class System;
19} 15}
@@ -51,7 +47,7 @@ struct VirtualMemoryArea {
51 47
52class MemoryManager final { 48class MemoryManager final {
53public: 49public:
54 explicit MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer); 50 explicit MemoryManager(Core::System& system);
55 ~MemoryManager(); 51 ~MemoryManager();
56 52
57 GPUVAddr AllocateSpace(u64 size, u64 align); 53 GPUVAddr AllocateSpace(u64 size, u64 align);
@@ -176,7 +172,6 @@ private:
176 172
177 Common::PageTable page_table{page_bits}; 173 Common::PageTable page_table{page_bits};
178 VMAMap vma_map; 174 VMAMap vma_map;
179 VideoCore::RasterizerInterface& rasterizer;
180 175
181 Core::System& system; 176 Core::System& system;
182}; 177};
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index 5b0eca9e2..c586cd6fe 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -9,6 +9,7 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "video_core/engines/fermi_2d.h" 10#include "video_core/engines/fermi_2d.h"
11#include "video_core/gpu.h" 11#include "video_core/gpu.h"
12#include "video_core/guest_driver.h"
12 13
13namespace Tegra { 14namespace Tegra {
14class MemoryManager; 15class MemoryManager;
@@ -78,5 +79,18 @@ public:
78 /// Initialize disk cached resources for the game being emulated 79 /// Initialize disk cached resources for the game being emulated
79 virtual void LoadDiskResources(const std::atomic_bool& stop_loading = false, 80 virtual void LoadDiskResources(const std::atomic_bool& stop_loading = false,
80 const DiskResourceLoadCallback& callback = {}) {} 81 const DiskResourceLoadCallback& callback = {}) {}
82
83 /// Grant access to the Guest Driver Profile for recording/obtaining info on the guest driver.
84 GuestDriverProfile& AccessGuestDriverProfile() {
85 return guest_driver_profile;
86 }
87
88 /// Grant access to the Guest Driver Profile for recording/obtaining info on the guest driver.
89 const GuestDriverProfile& AccessGuestDriverProfile() const {
90 return guest_driver_profile;
91 }
92
93private:
94 GuestDriverProfile guest_driver_profile{};
81}; 95};
82} // namespace VideoCore 96} // namespace VideoCore
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index c428f06e4..46a7433ea 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -55,16 +55,20 @@ namespace {
55 55
56template <typename Engine, typename Entry> 56template <typename Engine, typename Entry>
57Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry& entry, 57Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry& entry,
58 Tegra::Engines::ShaderType shader_type) { 58 Tegra::Engines::ShaderType shader_type,
59 std::size_t index = 0) {
59 if (entry.IsBindless()) { 60 if (entry.IsBindless()) {
60 const Tegra::Texture::TextureHandle tex_handle = 61 const Tegra::Texture::TextureHandle tex_handle =
61 engine.AccessConstBuffer32(shader_type, entry.GetBuffer(), entry.GetOffset()); 62 engine.AccessConstBuffer32(shader_type, entry.GetBuffer(), entry.GetOffset());
62 return engine.GetTextureInfo(tex_handle); 63 return engine.GetTextureInfo(tex_handle);
63 } 64 }
65 const auto& gpu_profile = engine.AccessGuestDriverProfile();
66 const u32 offset =
67 entry.GetOffset() + static_cast<u32>(index * gpu_profile.GetTextureHandlerSize());
64 if constexpr (std::is_same_v<Engine, Tegra::Engines::Maxwell3D>) { 68 if constexpr (std::is_same_v<Engine, Tegra::Engines::Maxwell3D>) {
65 return engine.GetStageTexture(shader_type, entry.GetOffset()); 69 return engine.GetStageTexture(shader_type, offset);
66 } else { 70 } else {
67 return engine.GetTexture(entry.GetOffset()); 71 return engine.GetTexture(offset);
68 } 72 }
69} 73}
70 74
@@ -244,9 +248,6 @@ void RasterizerOpenGL::SetupVertexInstances(GLuint vao) {
244} 248}
245 249
246GLintptr RasterizerOpenGL::SetupIndexBuffer() { 250GLintptr RasterizerOpenGL::SetupIndexBuffer() {
247 if (accelerate_draw != AccelDraw::Indexed) {
248 return 0;
249 }
250 MICROPROFILE_SCOPE(OpenGL_Index); 251 MICROPROFILE_SCOPE(OpenGL_Index);
251 const auto& regs = system.GPU().Maxwell3D().regs; 252 const auto& regs = system.GPU().Maxwell3D().regs;
252 const std::size_t size = CalculateIndexBufferSize(); 253 const std::size_t size = CalculateIndexBufferSize();
@@ -542,7 +543,8 @@ void RasterizerOpenGL::Clear() {
542 } 543 }
543} 544}
544 545
545void RasterizerOpenGL::DrawPrelude() { 546void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
547 MICROPROFILE_SCOPE(OpenGL_Drawing);
546 auto& gpu = system.GPU().Maxwell3D(); 548 auto& gpu = system.GPU().Maxwell3D();
547 549
548 SyncRasterizeEnable(state); 550 SyncRasterizeEnable(state);
@@ -563,9 +565,6 @@ void RasterizerOpenGL::DrawPrelude() {
563 565
564 buffer_cache.Acquire(); 566 buffer_cache.Acquire();
565 567
566 // Draw the vertex batch
567 const bool is_indexed = accelerate_draw == AccelDraw::Indexed;
568
569 std::size_t buffer_size = CalculateVertexArraysSize(); 568 std::size_t buffer_size = CalculateVertexArraysSize();
570 569
571 // Add space for index buffer 570 // Add space for index buffer
@@ -592,7 +591,11 @@ void RasterizerOpenGL::DrawPrelude() {
592 // Upload vertex and index data. 591 // Upload vertex and index data.
593 SetupVertexBuffer(vao); 592 SetupVertexBuffer(vao);
594 SetupVertexInstances(vao); 593 SetupVertexInstances(vao);
595 index_buffer_offset = SetupIndexBuffer(); 594
595 GLintptr index_buffer_offset;
596 if (is_indexed) {
597 index_buffer_offset = SetupIndexBuffer();
598 }
596 599
597 // Prepare packed bindings. 600 // Prepare packed bindings.
598 bind_ubo_pushbuffer.Setup(); 601 bind_ubo_pushbuffer.Setup();
@@ -626,6 +629,7 @@ void RasterizerOpenGL::DrawPrelude() {
626 // As all cached buffers are invalidated, we need to recheck their state. 629 // As all cached buffers are invalidated, we need to recheck their state.
627 gpu.dirty.ResetVertexArrays(); 630 gpu.dirty.ResetVertexArrays();
628 } 631 }
632 gpu.dirty.memory_general = false;
629 633
630 shader_program_manager->ApplyTo(state); 634 shader_program_manager->ApplyTo(state);
631 state.Apply(); 635 state.Apply();
@@ -633,106 +637,33 @@ void RasterizerOpenGL::DrawPrelude() {
633 if (texture_cache.TextureBarrier()) { 637 if (texture_cache.TextureBarrier()) {
634 glTextureBarrier(); 638 glTextureBarrier();
635 } 639 }
636}
637
638struct DrawParams {
639 bool is_indexed{};
640 bool is_instanced{};
641 GLenum primitive_mode{};
642 GLint count{};
643 GLint base_vertex{};
644
645 // Indexed settings
646 GLenum index_format{};
647 GLintptr index_buffer_offset{};
648
649 // Instanced setting
650 GLint num_instances{};
651 GLint base_instance{};
652
653 void DispatchDraw() {
654 if (is_indexed) {
655 const auto index_buffer_ptr = reinterpret_cast<const void*>(index_buffer_offset);
656 if (is_instanced) {
657 glDrawElementsInstancedBaseVertexBaseInstance(primitive_mode, count, index_format,
658 index_buffer_ptr, num_instances,
659 base_vertex, base_instance);
660 } else {
661 glDrawElementsBaseVertex(primitive_mode, count, index_format, index_buffer_ptr,
662 base_vertex);
663 }
664 } else {
665 if (is_instanced) {
666 glDrawArraysInstancedBaseInstance(primitive_mode, base_vertex, count, num_instances,
667 base_instance);
668 } else {
669 glDrawArrays(primitive_mode, base_vertex, count);
670 }
671 }
672 }
673};
674
675bool RasterizerOpenGL::DrawBatch(bool is_indexed) {
676 accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays;
677
678 MICROPROFILE_SCOPE(OpenGL_Drawing);
679
680 DrawPrelude();
681 640
682 auto& maxwell3d = system.GPU().Maxwell3D(); 641 const GLuint base_instance = static_cast<GLuint>(gpu.regs.vb_base_instance);
683 const auto& regs = maxwell3d.regs; 642 const GLsizei num_instances =
684 const auto current_instance = maxwell3d.state.current_instance; 643 static_cast<GLsizei>(is_instanced ? gpu.mme_draw.instance_count : 1);
685 DrawParams draw_call{}; 644 if (is_indexed) {
686 draw_call.is_indexed = is_indexed; 645 const GLenum index_format = MaxwellToGL::IndexFormat(gpu.regs.index_array.format);
687 draw_call.num_instances = static_cast<GLint>(1); 646 const GLint base_vertex = static_cast<GLint>(gpu.regs.vb_element_base);
688 draw_call.base_instance = static_cast<GLint>(current_instance); 647 const GLsizei num_vertices = static_cast<GLsizei>(gpu.regs.index_array.count);
689 draw_call.is_instanced = current_instance > 0; 648 glDrawElementsInstancedBaseVertexBaseInstance(
690 draw_call.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology); 649 primitive_mode, num_vertices, index_format,
691 if (draw_call.is_indexed) { 650 reinterpret_cast<const void*>(index_buffer_offset), num_instances, base_vertex,
692 draw_call.count = static_cast<GLint>(regs.index_array.count); 651 base_instance);
693 draw_call.base_vertex = static_cast<GLint>(regs.vb_element_base);
694 draw_call.index_format = MaxwellToGL::IndexFormat(regs.index_array.format);
695 draw_call.index_buffer_offset = index_buffer_offset;
696 } else { 652 } else {
697 draw_call.count = static_cast<GLint>(regs.vertex_buffer.count); 653 const GLint base_vertex = static_cast<GLint>(gpu.regs.vertex_buffer.first);
698 draw_call.base_vertex = static_cast<GLint>(regs.vertex_buffer.first); 654 const GLsizei num_vertices = static_cast<GLsizei>(gpu.regs.vertex_buffer.count);
655 glDrawArraysInstancedBaseInstance(primitive_mode, base_vertex, num_vertices, num_instances,
656 base_instance);
699 } 657 }
700 draw_call.DispatchDraw(); 658}
701 659
702 maxwell3d.dirty.memory_general = false; 660bool RasterizerOpenGL::DrawBatch(bool is_indexed) {
703 accelerate_draw = AccelDraw::Disabled; 661 Draw(is_indexed, false);
704 return true; 662 return true;
705} 663}
706 664
707bool RasterizerOpenGL::DrawMultiBatch(bool is_indexed) { 665bool RasterizerOpenGL::DrawMultiBatch(bool is_indexed) {
708 accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays; 666 Draw(is_indexed, true);
709
710 MICROPROFILE_SCOPE(OpenGL_Drawing);
711
712 DrawPrelude();
713
714 auto& maxwell3d = system.GPU().Maxwell3D();
715 const auto& regs = maxwell3d.regs;
716 const auto& draw_setup = maxwell3d.mme_draw;
717 DrawParams draw_call{};
718 draw_call.is_indexed = is_indexed;
719 draw_call.num_instances = static_cast<GLint>(draw_setup.instance_count);
720 draw_call.base_instance = static_cast<GLint>(regs.vb_base_instance);
721 draw_call.is_instanced = draw_setup.instance_count > 1;
722 draw_call.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology);
723 if (draw_call.is_indexed) {
724 draw_call.count = static_cast<GLint>(regs.index_array.count);
725 draw_call.base_vertex = static_cast<GLint>(regs.vb_element_base);
726 draw_call.index_format = MaxwellToGL::IndexFormat(regs.index_array.format);
727 draw_call.index_buffer_offset = index_buffer_offset;
728 } else {
729 draw_call.count = static_cast<GLint>(regs.vertex_buffer.count);
730 draw_call.base_vertex = static_cast<GLint>(regs.vertex_buffer.first);
731 }
732 draw_call.DispatchDraw();
733
734 maxwell3d.dirty.memory_general = false;
735 accelerate_draw = AccelDraw::Disabled;
736 return true; 667 return true;
737} 668}
738 669
@@ -942,8 +873,15 @@ void RasterizerOpenGL::SetupDrawTextures(std::size_t stage_index, const Shader&
942 u32 binding = device.GetBaseBindings(stage_index).sampler; 873 u32 binding = device.GetBaseBindings(stage_index).sampler;
943 for (const auto& entry : shader->GetShaderEntries().samplers) { 874 for (const auto& entry : shader->GetShaderEntries().samplers) {
944 const auto shader_type = static_cast<Tegra::Engines::ShaderType>(stage_index); 875 const auto shader_type = static_cast<Tegra::Engines::ShaderType>(stage_index);
945 const auto texture = GetTextureInfo(maxwell3d, entry, shader_type); 876 if (!entry.IsIndexed()) {
946 SetupTexture(binding++, texture, entry); 877 const auto texture = GetTextureInfo(maxwell3d, entry, shader_type);
878 SetupTexture(binding++, texture, entry);
879 } else {
880 for (std::size_t i = 0; i < entry.Size(); ++i) {
881 const auto texture = GetTextureInfo(maxwell3d, entry, shader_type, i);
882 SetupTexture(binding++, texture, entry);
883 }
884 }
947 } 885 }
948} 886}
949 887
@@ -952,8 +890,17 @@ void RasterizerOpenGL::SetupComputeTextures(const Shader& kernel) {
952 const auto& compute = system.GPU().KeplerCompute(); 890 const auto& compute = system.GPU().KeplerCompute();
953 u32 binding = 0; 891 u32 binding = 0;
954 for (const auto& entry : kernel->GetShaderEntries().samplers) { 892 for (const auto& entry : kernel->GetShaderEntries().samplers) {
955 const auto texture = GetTextureInfo(compute, entry, Tegra::Engines::ShaderType::Compute); 893 if (!entry.IsIndexed()) {
956 SetupTexture(binding++, texture, entry); 894 const auto texture =
895 GetTextureInfo(compute, entry, Tegra::Engines::ShaderType::Compute);
896 SetupTexture(binding++, texture, entry);
897 } else {
898 for (std::size_t i = 0; i < entry.Size(); ++i) {
899 const auto texture =
900 GetTextureInfo(compute, entry, Tegra::Engines::ShaderType::Compute, i);
901 SetupTexture(binding++, texture, entry);
902 }
903 }
957 } 904 }
958} 905}
959 906
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 6a27cf497..0501f3828 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -103,7 +103,7 @@ private:
103 std::size_t size); 103 std::size_t size);
104 104
105 /// Syncs all the state, shaders, render targets and textures setting before a draw call. 105 /// Syncs all the state, shaders, render targets and textures setting before a draw call.
106 void DrawPrelude(); 106 void Draw(bool is_indexed, bool is_instanced);
107 107
108 /// Configures the current textures to use for the draw command. 108 /// Configures the current textures to use for the draw command.
109 void SetupDrawTextures(std::size_t stage_index, const Shader& shader); 109 void SetupDrawTextures(std::size_t stage_index, const Shader& shader);
@@ -220,12 +220,7 @@ private:
220 220
221 GLintptr SetupIndexBuffer(); 221 GLintptr SetupIndexBuffer();
222 222
223 GLintptr index_buffer_offset;
224
225 void SetupShaders(GLenum primitive_mode); 223 void SetupShaders(GLenum primitive_mode);
226
227 enum class AccelDraw { Disabled, Arrays, Indexed };
228 AccelDraw accelerate_draw = AccelDraw::Disabled;
229}; 224};
230 225
231} // namespace OpenGL 226} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 3c5bdd377..489eb143c 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -214,6 +214,7 @@ std::unique_ptr<ConstBufferLocker> MakeLocker(Core::System& system, ShaderType s
214} 214}
215 215
216void FillLocker(ConstBufferLocker& locker, const ShaderDiskCacheUsage& usage) { 216void FillLocker(ConstBufferLocker& locker, const ShaderDiskCacheUsage& usage) {
217 locker.SetBoundBuffer(usage.bound_buffer);
217 for (const auto& key : usage.keys) { 218 for (const auto& key : usage.keys) {
218 const auto [buffer, offset] = key.first; 219 const auto [buffer, offset] = key.first;
219 locker.InsertKey(buffer, offset, key.second); 220 locker.InsertKey(buffer, offset, key.second);
@@ -418,7 +419,8 @@ bool CachedShader::EnsureValidLockerVariant() {
418 419
419ShaderDiskCacheUsage CachedShader::GetUsage(const ProgramVariant& variant, 420ShaderDiskCacheUsage CachedShader::GetUsage(const ProgramVariant& variant,
420 const ConstBufferLocker& locker) const { 421 const ConstBufferLocker& locker) const {
421 return ShaderDiskCacheUsage{unique_identifier, variant, locker.GetKeys(), 422 return ShaderDiskCacheUsage{unique_identifier, variant,
423 locker.GetBoundBuffer(), locker.GetKeys(),
422 locker.GetBoundSamplers(), locker.GetBindlessSamplers()}; 424 locker.GetBoundSamplers(), locker.GetBindlessSamplers()};
423} 425}
424 426
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index a1ac3d7a9..4735000b5 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -391,6 +391,7 @@ public:
391 DeclareVertex(); 391 DeclareVertex();
392 DeclareGeometry(); 392 DeclareGeometry();
393 DeclareRegisters(); 393 DeclareRegisters();
394 DeclareCustomVariables();
394 DeclarePredicates(); 395 DeclarePredicates();
395 DeclareLocalMemory(); 396 DeclareLocalMemory();
396 DeclareInternalFlags(); 397 DeclareInternalFlags();
@@ -503,6 +504,16 @@ private:
503 } 504 }
504 } 505 }
505 506
507 void DeclareCustomVariables() {
508 const u32 num_custom_variables = ir.GetNumCustomVariables();
509 for (u32 i = 0; i < num_custom_variables; ++i) {
510 code.AddLine("float {} = 0.0f;", GetCustomVariable(i));
511 }
512 if (num_custom_variables > 0) {
513 code.AddNewLine();
514 }
515 }
516
506 void DeclarePredicates() { 517 void DeclarePredicates() {
507 const auto& predicates = ir.GetPredicates(); 518 const auto& predicates = ir.GetPredicates();
508 for (const auto pred : predicates) { 519 for (const auto pred : predicates) {
@@ -655,7 +666,8 @@ private:
655 u32 binding = device.GetBaseBindings(stage).sampler; 666 u32 binding = device.GetBaseBindings(stage).sampler;
656 for (const auto& sampler : ir.GetSamplers()) { 667 for (const auto& sampler : ir.GetSamplers()) {
657 const std::string name = GetSampler(sampler); 668 const std::string name = GetSampler(sampler);
658 const std::string description = fmt::format("layout (binding = {}) uniform", binding++); 669 const std::string description = fmt::format("layout (binding = {}) uniform", binding);
670 binding += sampler.IsIndexed() ? sampler.Size() : 1;
659 671
660 std::string sampler_type = [&]() { 672 std::string sampler_type = [&]() {
661 if (sampler.IsBuffer()) { 673 if (sampler.IsBuffer()) {
@@ -682,7 +694,11 @@ private:
682 sampler_type += "Shadow"; 694 sampler_type += "Shadow";
683 } 695 }
684 696
685 code.AddLine("{} {} {};", description, sampler_type, name); 697 if (!sampler.IsIndexed()) {
698 code.AddLine("{} {} {};", description, sampler_type, name);
699 } else {
700 code.AddLine("{} {} {}[{}];", description, sampler_type, name, sampler.Size());
701 }
686 } 702 }
687 if (!ir.GetSamplers().empty()) { 703 if (!ir.GetSamplers().empty()) {
688 code.AddNewLine(); 704 code.AddNewLine();
@@ -775,6 +791,11 @@ private:
775 return {GetRegister(index), Type::Float}; 791 return {GetRegister(index), Type::Float};
776 } 792 }
777 793
794 if (const auto cv = std::get_if<CustomVarNode>(&*node)) {
795 const u32 index = cv->GetIndex();
796 return {GetCustomVariable(index), Type::Float};
797 }
798
778 if (const auto immediate = std::get_if<ImmediateNode>(&*node)) { 799 if (const auto immediate = std::get_if<ImmediateNode>(&*node)) {
779 const u32 value = immediate->GetValue(); 800 const u32 value = immediate->GetValue();
780 if (value < 10) { 801 if (value < 10) {
@@ -1098,7 +1119,11 @@ private:
1098 } else if (!meta->ptp.empty()) { 1119 } else if (!meta->ptp.empty()) {
1099 expr += "Offsets"; 1120 expr += "Offsets";
1100 } 1121 }
1101 expr += '(' + GetSampler(meta->sampler) + ", "; 1122 if (!meta->sampler.IsIndexed()) {
1123 expr += '(' + GetSampler(meta->sampler) + ", ";
1124 } else {
1125 expr += '(' + GetSampler(meta->sampler) + '[' + Visit(meta->index).AsUint() + "], ";
1126 }
1102 expr += coord_constructors.at(count + (has_array ? 1 : 0) + 1127 expr += coord_constructors.at(count + (has_array ? 1 : 0) +
1103 (has_shadow && !separate_dc ? 1 : 0) - 1); 1128 (has_shadow && !separate_dc ? 1 : 0) - 1);
1104 expr += '('; 1129 expr += '(';
@@ -1310,6 +1335,8 @@ private:
1310 const std::string final_offset = fmt::format("({} - {}) >> 2", real, base); 1335 const std::string final_offset = fmt::format("({} - {}) >> 2", real, base);
1311 target = {fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset), 1336 target = {fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset),
1312 Type::Uint}; 1337 Type::Uint};
1338 } else if (const auto cv = std::get_if<CustomVarNode>(&*dest)) {
1339 target = {GetCustomVariable(cv->GetIndex()), Type::Float};
1313 } else { 1340 } else {
1314 UNREACHABLE_MSG("Assign called without a proper target"); 1341 UNREACHABLE_MSG("Assign called without a proper target");
1315 } 1342 }
@@ -2237,6 +2264,10 @@ private:
2237 return GetDeclarationWithSuffix(index, "gpr"); 2264 return GetDeclarationWithSuffix(index, "gpr");
2238 } 2265 }
2239 2266
2267 std::string GetCustomVariable(u32 index) const {
2268 return GetDeclarationWithSuffix(index, "custom_var");
2269 }
2270
2240 std::string GetPredicate(Tegra::Shader::Pred pred) const { 2271 std::string GetPredicate(Tegra::Shader::Pred pred) const {
2241 return GetDeclarationWithSuffix(static_cast<u32>(pred), "pred"); 2272 return GetDeclarationWithSuffix(static_cast<u32>(pred), "pred");
2242 } 2273 }
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
index cf874a09a..1fc204f6f 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
@@ -53,7 +53,7 @@ struct BindlessSamplerKey {
53 Tegra::Engines::SamplerDescriptor sampler{}; 53 Tegra::Engines::SamplerDescriptor sampler{};
54}; 54};
55 55
56constexpr u32 NativeVersion = 11; 56constexpr u32 NativeVersion = 12;
57 57
58// Making sure sizes doesn't change by accident 58// Making sure sizes doesn't change by accident
59static_assert(sizeof(ProgramVariant) == 20); 59static_assert(sizeof(ProgramVariant) == 20);
@@ -186,7 +186,8 @@ ShaderDiskCacheOpenGL::LoadTransferable() {
186 u32 num_bound_samplers{}; 186 u32 num_bound_samplers{};
187 u32 num_bindless_samplers{}; 187 u32 num_bindless_samplers{};
188 if (file.ReadArray(&usage.unique_identifier, 1) != 1 || 188 if (file.ReadArray(&usage.unique_identifier, 1) != 1 ||
189 file.ReadArray(&usage.variant, 1) != 1 || file.ReadArray(&num_keys, 1) != 1 || 189 file.ReadArray(&usage.variant, 1) != 1 ||
190 file.ReadArray(&usage.bound_buffer, 1) != 1 || file.ReadArray(&num_keys, 1) != 1 ||
190 file.ReadArray(&num_bound_samplers, 1) != 1 || 191 file.ReadArray(&num_bound_samplers, 1) != 1 ||
191 file.ReadArray(&num_bindless_samplers, 1) != 1) { 192 file.ReadArray(&num_bindless_samplers, 1) != 1) {
192 LOG_ERROR(Render_OpenGL, error_loading); 193 LOG_ERROR(Render_OpenGL, error_loading);
@@ -281,7 +282,9 @@ ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) {
281 u32 num_bindless_samplers{}; 282 u32 num_bindless_samplers{};
282 ShaderDiskCacheUsage usage; 283 ShaderDiskCacheUsage usage;
283 if (!LoadObjectFromPrecompiled(usage.unique_identifier) || 284 if (!LoadObjectFromPrecompiled(usage.unique_identifier) ||
284 !LoadObjectFromPrecompiled(usage.variant) || !LoadObjectFromPrecompiled(num_keys) || 285 !LoadObjectFromPrecompiled(usage.variant) ||
286 !LoadObjectFromPrecompiled(usage.bound_buffer) ||
287 !LoadObjectFromPrecompiled(num_keys) ||
285 !LoadObjectFromPrecompiled(num_bound_samplers) || 288 !LoadObjectFromPrecompiled(num_bound_samplers) ||
286 !LoadObjectFromPrecompiled(num_bindless_samplers)) { 289 !LoadObjectFromPrecompiled(num_bindless_samplers)) {
287 return {}; 290 return {};
@@ -393,6 +396,7 @@ void ShaderDiskCacheOpenGL::SaveUsage(const ShaderDiskCacheUsage& usage) {
393 396
394 if (file.WriteObject(TransferableEntryKind::Usage) != 1 || 397 if (file.WriteObject(TransferableEntryKind::Usage) != 1 ||
395 file.WriteObject(usage.unique_identifier) != 1 || file.WriteObject(usage.variant) != 1 || 398 file.WriteObject(usage.unique_identifier) != 1 || file.WriteObject(usage.variant) != 1 ||
399 file.WriteObject(usage.bound_buffer) != 1 ||
396 file.WriteObject(static_cast<u32>(usage.keys.size())) != 1 || 400 file.WriteObject(static_cast<u32>(usage.keys.size())) != 1 ||
397 file.WriteObject(static_cast<u32>(usage.bound_samplers.size())) != 1 || 401 file.WriteObject(static_cast<u32>(usage.bound_samplers.size())) != 1 ||
398 file.WriteObject(static_cast<u32>(usage.bindless_samplers.size())) != 1) { 402 file.WriteObject(static_cast<u32>(usage.bindless_samplers.size())) != 1) {
@@ -447,7 +451,7 @@ void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint p
447 }; 451 };
448 452
449 if (!SaveObjectToPrecompiled(usage.unique_identifier) || 453 if (!SaveObjectToPrecompiled(usage.unique_identifier) ||
450 !SaveObjectToPrecompiled(usage.variant) || 454 !SaveObjectToPrecompiled(usage.variant) || !SaveObjectToPrecompiled(usage.bound_buffer) ||
451 !SaveObjectToPrecompiled(static_cast<u32>(usage.keys.size())) || 455 !SaveObjectToPrecompiled(static_cast<u32>(usage.keys.size())) ||
452 !SaveObjectToPrecompiled(static_cast<u32>(usage.bound_samplers.size())) || 456 !SaveObjectToPrecompiled(static_cast<u32>(usage.bound_samplers.size())) ||
453 !SaveObjectToPrecompiled(static_cast<u32>(usage.bindless_samplers.size()))) { 457 !SaveObjectToPrecompiled(static_cast<u32>(usage.bindless_samplers.size()))) {
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.h b/src/video_core/renderer_opengl/gl_shader_disk_cache.h
index 69a2fbdda..ef2371f6d 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.h
@@ -79,6 +79,7 @@ static_assert(std::is_trivially_copyable_v<ProgramVariant>);
79struct ShaderDiskCacheUsage { 79struct ShaderDiskCacheUsage {
80 u64 unique_identifier{}; 80 u64 unique_identifier{};
81 ProgramVariant variant; 81 ProgramVariant variant;
82 u32 bound_buffer{};
82 VideoCommon::Shader::KeyMap keys; 83 VideoCommon::Shader::KeyMap keys;
83 VideoCommon::Shader::BoundSamplerMap bound_samplers; 84 VideoCommon::Shader::BoundSamplerMap bound_samplers;
84 VideoCommon::Shader::BindlessSamplerMap bindless_samplers; 85 VideoCommon::Shader::BindlessSamplerMap bindless_samplers;
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index ea4f35663..7ed505628 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -47,8 +47,7 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
47 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 47 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
48 return GL_UNSIGNED_INT_2_10_10_10_REV; 48 return GL_UNSIGNED_INT_2_10_10_10_REV;
49 default: 49 default:
50 LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); 50 LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
51 UNREACHABLE();
52 return {}; 51 return {};
53 } 52 }
54 case Maxwell::VertexAttribute::Type::SignedInt: 53 case Maxwell::VertexAttribute::Type::SignedInt:
@@ -72,8 +71,7 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
72 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 71 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
73 return GL_INT_2_10_10_10_REV; 72 return GL_INT_2_10_10_10_REV;
74 default: 73 default:
75 LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); 74 LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
76 UNREACHABLE();
77 return {}; 75 return {};
78 } 76 }
79 case Maxwell::VertexAttribute::Type::Float: 77 case Maxwell::VertexAttribute::Type::Float:
@@ -89,13 +87,19 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
89 case Maxwell::VertexAttribute::Size::Size_32_32_32_32: 87 case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
90 return GL_FLOAT; 88 return GL_FLOAT;
91 default: 89 default:
92 LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); 90 LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
93 UNREACHABLE(); 91 return {};
92 }
93 case Maxwell::VertexAttribute::Type::UnsignedScaled:
94 switch (attrib.size) {
95 case Maxwell::VertexAttribute::Size::Size_8_8:
96 return GL_UNSIGNED_BYTE;
97 default:
98 LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
94 return {}; 99 return {};
95 } 100 }
96 default: 101 default:
97 LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString()); 102 LOG_ERROR(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString());
98 UNREACHABLE();
99 return {}; 103 return {};
100 } 104 }
101} 105}
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
new file mode 100644
index 000000000..d5032b432
--- /dev/null
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -0,0 +1,265 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6#include <optional>
7#include <vector>
8
9#include <fmt/format.h>
10
11#include "common/assert.h"
12#include "common/logging/log.h"
13#include "common/telemetry.h"
14#include "core/core.h"
15#include "core/core_timing.h"
16#include "core/frontend/emu_window.h"
17#include "core/memory.h"
18#include "core/perf_stats.h"
19#include "core/settings.h"
20#include "core/telemetry_session.h"
21#include "video_core/gpu.h"
22#include "video_core/renderer_vulkan/declarations.h"
23#include "video_core/renderer_vulkan/renderer_vulkan.h"
24#include "video_core/renderer_vulkan/vk_blit_screen.h"
25#include "video_core/renderer_vulkan/vk_device.h"
26#include "video_core/renderer_vulkan/vk_memory_manager.h"
27#include "video_core/renderer_vulkan/vk_rasterizer.h"
28#include "video_core/renderer_vulkan/vk_resource_manager.h"
29#include "video_core/renderer_vulkan/vk_scheduler.h"
30#include "video_core/renderer_vulkan/vk_swapchain.h"
31
32namespace Vulkan {
33
34namespace {
35
36VkBool32 DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity_,
37 VkDebugUtilsMessageTypeFlagsEXT type,
38 const VkDebugUtilsMessengerCallbackDataEXT* data,
39 [[maybe_unused]] void* user_data) {
40 const vk::DebugUtilsMessageSeverityFlagBitsEXT severity{severity_};
41 const char* message{data->pMessage};
42
43 if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eError) {
44 LOG_CRITICAL(Render_Vulkan, "{}", message);
45 } else if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning) {
46 LOG_WARNING(Render_Vulkan, "{}", message);
47 } else if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo) {
48 LOG_INFO(Render_Vulkan, "{}", message);
49 } else if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose) {
50 LOG_DEBUG(Render_Vulkan, "{}", message);
51 }
52 return VK_FALSE;
53}
54
55std::string GetReadableVersion(u32 version) {
56 return fmt::format("{}.{}.{}", VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version),
57 VK_VERSION_PATCH(version));
58}
59
60std::string GetDriverVersion(const VKDevice& device) {
61 // Extracted from
62 // https://github.com/SaschaWillems/vulkan.gpuinfo.org/blob/5dddea46ea1120b0df14eef8f15ff8e318e35462/functions.php#L308-L314
63 const u32 version = device.GetDriverVersion();
64
65 if (device.GetDriverID() == vk::DriverIdKHR::eNvidiaProprietary) {
66 const u32 major = (version >> 22) & 0x3ff;
67 const u32 minor = (version >> 14) & 0x0ff;
68 const u32 secondary = (version >> 6) & 0x0ff;
69 const u32 tertiary = version & 0x003f;
70 return fmt::format("{}.{}.{}.{}", major, minor, secondary, tertiary);
71 }
72 if (device.GetDriverID() == vk::DriverIdKHR::eIntelProprietaryWindows) {
73 const u32 major = version >> 14;
74 const u32 minor = version & 0x3fff;
75 return fmt::format("{}.{}", major, minor);
76 }
77
78 return GetReadableVersion(version);
79}
80
81std::string BuildCommaSeparatedExtensions(std::vector<std::string> available_extensions) {
82 std::sort(std::begin(available_extensions), std::end(available_extensions));
83
84 static constexpr std::size_t AverageExtensionSize = 64;
85 std::string separated_extensions;
86 separated_extensions.reserve(available_extensions.size() * AverageExtensionSize);
87
88 const auto end = std::end(available_extensions);
89 for (auto extension = std::begin(available_extensions); extension != end; ++extension) {
90 if (const bool is_last = extension + 1 == end; is_last) {
91 separated_extensions += *extension;
92 } else {
93 separated_extensions += fmt::format("{},", *extension);
94 }
95 }
96 return separated_extensions;
97}
98
99} // Anonymous namespace
100
101RendererVulkan::RendererVulkan(Core::Frontend::EmuWindow& window, Core::System& system)
102 : RendererBase(window), system{system} {}
103
104RendererVulkan::~RendererVulkan() {
105 ShutDown();
106}
107
108void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
109 const auto& layout = render_window.GetFramebufferLayout();
110 if (framebuffer && layout.width > 0 && layout.height > 0 && render_window.IsShown()) {
111 const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset;
112 const bool use_accelerated =
113 rasterizer->AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride);
114 const bool is_srgb = use_accelerated && screen_info.is_srgb;
115 if (swapchain->HasFramebufferChanged(layout) || swapchain->GetSrgbState() != is_srgb) {
116 swapchain->Create(layout.width, layout.height, is_srgb);
117 blit_screen->Recreate();
118 }
119
120 scheduler->WaitWorker();
121
122 swapchain->AcquireNextImage();
123 const auto [fence, render_semaphore] = blit_screen->Draw(*framebuffer, use_accelerated);
124
125 scheduler->Flush(false, render_semaphore);
126
127 if (swapchain->Present(render_semaphore, fence)) {
128 blit_screen->Recreate();
129 }
130
131 render_window.SwapBuffers();
132 rasterizer->TickFrame();
133 }
134
135 render_window.PollEvents();
136}
137
138bool RendererVulkan::Init() {
139 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{};
140 render_window.RetrieveVulkanHandlers(&vkGetInstanceProcAddr, &instance, &surface);
141 const vk::DispatchLoaderDynamic dldi(instance, vkGetInstanceProcAddr);
142
143 std::optional<vk::DebugUtilsMessengerEXT> callback;
144 if (Settings::values.renderer_debug && dldi.vkCreateDebugUtilsMessengerEXT) {
145 callback = CreateDebugCallback(dldi);
146 if (!callback) {
147 return false;
148 }
149 }
150
151 if (!PickDevices(dldi)) {
152 if (callback) {
153 instance.destroy(*callback, nullptr, dldi);
154 }
155 return false;
156 }
157 debug_callback = UniqueDebugUtilsMessengerEXT(
158 *callback, vk::ObjectDestroy<vk::Instance, vk::DispatchLoaderDynamic>(
159 instance, nullptr, device->GetDispatchLoader()));
160
161 Report();
162
163 memory_manager = std::make_unique<VKMemoryManager>(*device);
164
165 resource_manager = std::make_unique<VKResourceManager>(*device);
166
167 const auto& framebuffer = render_window.GetFramebufferLayout();
168 swapchain = std::make_unique<VKSwapchain>(surface, *device);
169 swapchain->Create(framebuffer.width, framebuffer.height, false);
170
171 scheduler = std::make_unique<VKScheduler>(*device, *resource_manager);
172
173 rasterizer = std::make_unique<RasterizerVulkan>(system, render_window, screen_info, *device,
174 *resource_manager, *memory_manager, *scheduler);
175
176 blit_screen = std::make_unique<VKBlitScreen>(system, render_window, *rasterizer, *device,
177 *resource_manager, *memory_manager, *swapchain,
178 *scheduler, screen_info);
179
180 return true;
181}
182
183void RendererVulkan::ShutDown() {
184 if (!device) {
185 return;
186 }
187 const auto dev = device->GetLogical();
188 const auto& dld = device->GetDispatchLoader();
189 if (dev && dld.vkDeviceWaitIdle) {
190 dev.waitIdle(dld);
191 }
192
193 rasterizer.reset();
194 blit_screen.reset();
195 scheduler.reset();
196 swapchain.reset();
197 memory_manager.reset();
198 resource_manager.reset();
199 device.reset();
200}
201
202std::optional<vk::DebugUtilsMessengerEXT> RendererVulkan::CreateDebugCallback(
203 const vk::DispatchLoaderDynamic& dldi) {
204 const vk::DebugUtilsMessengerCreateInfoEXT callback_ci(
205 {},
206 vk::DebugUtilsMessageSeverityFlagBitsEXT::eError |
207 vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning |
208 vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo |
209 vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose,
210 vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral |
211 vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation |
212 vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance,
213 &DebugCallback, nullptr);
214 vk::DebugUtilsMessengerEXT callback;
215 if (instance.createDebugUtilsMessengerEXT(&callback_ci, nullptr, &callback, dldi) !=
216 vk::Result::eSuccess) {
217 LOG_ERROR(Render_Vulkan, "Failed to create debug callback");
218 return {};
219 }
220 return callback;
221}
222
223bool RendererVulkan::PickDevices(const vk::DispatchLoaderDynamic& dldi) {
224 const auto devices = instance.enumeratePhysicalDevices(dldi);
225
226 // TODO(Rodrigo): Choose device from config file
227 const s32 device_index = Settings::values.vulkan_device;
228 if (device_index < 0 || device_index >= static_cast<s32>(devices.size())) {
229 LOG_ERROR(Render_Vulkan, "Invalid device index {}!", device_index);
230 return false;
231 }
232 const vk::PhysicalDevice physical_device = devices[device_index];
233
234 if (!VKDevice::IsSuitable(dldi, physical_device, surface)) {
235 return false;
236 }
237
238 device = std::make_unique<VKDevice>(dldi, physical_device, surface);
239 return device->Create(dldi, instance);
240}
241
242void RendererVulkan::Report() const {
243 const std::string vendor_name{device->GetVendorName()};
244 const std::string model_name{device->GetModelName()};
245 const std::string driver_version = GetDriverVersion(*device);
246 const std::string driver_name = fmt::format("{} {}", vendor_name, driver_version);
247
248 const std::string api_version = GetReadableVersion(device->GetApiVersion());
249
250 const std::string extensions = BuildCommaSeparatedExtensions(device->GetAvailableExtensions());
251
252 LOG_INFO(Render_Vulkan, "Driver: {}", driver_name);
253 LOG_INFO(Render_Vulkan, "Device: {}", model_name);
254 LOG_INFO(Render_Vulkan, "Vulkan: {}", api_version);
255
256 auto& telemetry_session = system.TelemetrySession();
257 constexpr auto field = Telemetry::FieldType::UserSystem;
258 telemetry_session.AddField(field, "GPU_Vendor", vendor_name);
259 telemetry_session.AddField(field, "GPU_Model", model_name);
260 telemetry_session.AddField(field, "GPU_Vulkan_Driver", driver_name);
261 telemetry_session.AddField(field, "GPU_Vulkan_Version", api_version);
262 telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions);
263}
264
265} // namespace Vulkan \ No newline at end of file
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp
index 939eebe83..9840f26e5 100644
--- a/src/video_core/renderer_vulkan/vk_device.cpp
+++ b/src/video_core/renderer_vulkan/vk_device.cpp
@@ -400,8 +400,10 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami
400 VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME, true); 400 VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME, true);
401 Test(extension, ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, 401 Test(extension, ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME,
402 false); 402 false);
403 Test(extension, nv_device_diagnostic_checkpoints, 403 if (Settings::values.renderer_debug) {
404 VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME, true); 404 Test(extension, nv_device_diagnostic_checkpoints,
405 VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME, true);
406 }
405 } 407 }
406 408
407 if (khr_shader_float16_int8) { 409 if (khr_shader_float16_int8) {
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index d2c6b1189..aada38702 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -571,7 +571,7 @@ RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments() {
571 color_attachments[rt] = texture_cache.GetColorBufferSurface(rt, true); 571 color_attachments[rt] = texture_cache.GetColorBufferSurface(rt, true);
572 } 572 }
573 if (color_attachments[rt] && WalkAttachmentOverlaps(*color_attachments[rt])) { 573 if (color_attachments[rt] && WalkAttachmentOverlaps(*color_attachments[rt])) {
574 texceptions.set(rt); 574 texceptions[rt] = true;
575 } 575 }
576 } 576 }
577 577
@@ -579,7 +579,7 @@ RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments() {
579 zeta_attachment = texture_cache.GetDepthBufferSurface(true); 579 zeta_attachment = texture_cache.GetDepthBufferSurface(true);
580 } 580 }
581 if (zeta_attachment && WalkAttachmentOverlaps(*zeta_attachment)) { 581 if (zeta_attachment && WalkAttachmentOverlaps(*zeta_attachment)) {
582 texceptions.set(ZETA_TEXCEPTION_INDEX); 582 texceptions[ZETA_TEXCEPTION_INDEX] = true;
583 } 583 }
584 584
585 texture_cache.GuardRenderTargets(false); 585 texture_cache.GuardRenderTargets(false);
@@ -1122,11 +1122,12 @@ RenderPassParams RasterizerVulkan::GetRenderPassParams(Texceptions texceptions)
1122 1122
1123 for (std::size_t rt = 0; rt < static_cast<std::size_t>(regs.rt_control.count); ++rt) { 1123 for (std::size_t rt = 0; rt < static_cast<std::size_t>(regs.rt_control.count); ++rt) {
1124 const auto& rendertarget = regs.rt[rt]; 1124 const auto& rendertarget = regs.rt[rt];
1125 if (rendertarget.Address() == 0 || rendertarget.format == Tegra::RenderTargetFormat::NONE) 1125 if (rendertarget.Address() == 0 || rendertarget.format == Tegra::RenderTargetFormat::NONE) {
1126 continue; 1126 continue;
1127 }
1127 renderpass_params.color_attachments.push_back(RenderPassParams::ColorAttachment{ 1128 renderpass_params.color_attachments.push_back(RenderPassParams::ColorAttachment{
1128 static_cast<u32>(rt), PixelFormatFromRenderTargetFormat(rendertarget.format), 1129 static_cast<u32>(rt), PixelFormatFromRenderTargetFormat(rendertarget.format),
1129 texceptions.test(rt)}); 1130 texceptions[rt]});
1130 } 1131 }
1131 1132
1132 renderpass_params.has_zeta = regs.zeta_enable; 1133 renderpass_params.has_zeta = regs.zeta_enable;
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
index 1ab22251e..24a658dce 100644
--- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
+++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
@@ -353,6 +353,7 @@ private:
353 DeclareFragment(); 353 DeclareFragment();
354 DeclareCompute(); 354 DeclareCompute();
355 DeclareRegisters(); 355 DeclareRegisters();
356 DeclareCustomVariables();
356 DeclarePredicates(); 357 DeclarePredicates();
357 DeclareLocalMemory(); 358 DeclareLocalMemory();
358 DeclareSharedMemory(); 359 DeclareSharedMemory();
@@ -586,6 +587,15 @@ private:
586 } 587 }
587 } 588 }
588 589
590 void DeclareCustomVariables() {
591 const u32 num_custom_variables = ir.GetNumCustomVariables();
592 for (u32 i = 0; i < num_custom_variables; ++i) {
593 const Id id = OpVariable(t_prv_float, spv::StorageClass::Private, v_float_zero);
594 Name(id, fmt::format("custom_var_{}", i));
595 custom_variables.emplace(i, AddGlobalVariable(id));
596 }
597 }
598
589 void DeclarePredicates() { 599 void DeclarePredicates() {
590 for (const auto pred : ir.GetPredicates()) { 600 for (const auto pred : ir.GetPredicates()) {
591 const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false); 601 const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false);
@@ -982,6 +992,11 @@ private:
982 return {OpLoad(t_float, registers.at(index)), Type::Float}; 992 return {OpLoad(t_float, registers.at(index)), Type::Float};
983 } 993 }
984 994
995 if (const auto cv = std::get_if<CustomVarNode>(&*node)) {
996 const u32 index = cv->GetIndex();
997 return {OpLoad(t_float, custom_variables.at(index)), Type::Float};
998 }
999
985 if (const auto immediate = std::get_if<ImmediateNode>(&*node)) { 1000 if (const auto immediate = std::get_if<ImmediateNode>(&*node)) {
986 return {Constant(t_uint, immediate->GetValue()), Type::Uint}; 1001 return {Constant(t_uint, immediate->GetValue()), Type::Uint};
987 } 1002 }
@@ -1333,6 +1348,9 @@ private:
1333 } else if (const auto gmem = std::get_if<GmemNode>(&*dest)) { 1348 } else if (const auto gmem = std::get_if<GmemNode>(&*dest)) {
1334 target = {GetGlobalMemoryPointer(*gmem), Type::Uint}; 1349 target = {GetGlobalMemoryPointer(*gmem), Type::Uint};
1335 1350
1351 } else if (const auto cv = std::get_if<CustomVarNode>(&*dest)) {
1352 target = {custom_variables.at(cv->GetIndex()), Type::Float};
1353
1336 } else { 1354 } else {
1337 UNIMPLEMENTED(); 1355 UNIMPLEMENTED();
1338 } 1356 }
@@ -2508,6 +2526,7 @@ private:
2508 Id out_vertex{}; 2526 Id out_vertex{};
2509 Id in_vertex{}; 2527 Id in_vertex{};
2510 std::map<u32, Id> registers; 2528 std::map<u32, Id> registers;
2529 std::map<u32, Id> custom_variables;
2511 std::map<Tegra::Shader::Pred, Id> predicates; 2530 std::map<Tegra::Shader::Pred, Id> predicates;
2512 std::map<u32, Id> flow_variables; 2531 std::map<u32, Id> flow_variables;
2513 Id local_memory{}; 2532 Id local_memory{};
diff --git a/src/video_core/shader/ast.h b/src/video_core/shader/ast.h
index a2f0044ba..cca13bcde 100644
--- a/src/video_core/shader/ast.h
+++ b/src/video_core/shader/ast.h
@@ -65,8 +65,8 @@ public:
65 void DetachSegment(ASTNode start, ASTNode end); 65 void DetachSegment(ASTNode start, ASTNode end);
66 void Remove(ASTNode node); 66 void Remove(ASTNode node);
67 67
68 ASTNode first{}; 68 ASTNode first;
69 ASTNode last{}; 69 ASTNode last;
70}; 70};
71 71
72class ASTProgram { 72class ASTProgram {
@@ -299,9 +299,9 @@ private:
299 friend class ASTZipper; 299 friend class ASTZipper;
300 300
301 ASTData data; 301 ASTData data;
302 ASTNode parent{}; 302 ASTNode parent;
303 ASTNode next{}; 303 ASTNode next;
304 ASTNode previous{}; 304 ASTNode previous;
305 ASTZipper* manager{}; 305 ASTZipper* manager{};
306}; 306};
307 307
diff --git a/src/video_core/shader/const_buffer_locker.cpp b/src/video_core/shader/const_buffer_locker.cpp
index a4a0319eb..0638be8cb 100644
--- a/src/video_core/shader/const_buffer_locker.cpp
+++ b/src/video_core/shader/const_buffer_locker.cpp
@@ -66,6 +66,18 @@ std::optional<Tegra::Engines::SamplerDescriptor> ConstBufferLocker::ObtainBindle
66 return value; 66 return value;
67} 67}
68 68
69std::optional<u32> ConstBufferLocker::ObtainBoundBuffer() {
70 if (bound_buffer_saved) {
71 return bound_buffer;
72 }
73 if (!engine) {
74 return std::nullopt;
75 }
76 bound_buffer_saved = true;
77 bound_buffer = engine->GetBoundBuffer();
78 return bound_buffer;
79}
80
69void ConstBufferLocker::InsertKey(u32 buffer, u32 offset, u32 value) { 81void ConstBufferLocker::InsertKey(u32 buffer, u32 offset, u32 value) {
70 keys.insert_or_assign({buffer, offset}, value); 82 keys.insert_or_assign({buffer, offset}, value);
71} 83}
@@ -78,6 +90,11 @@ void ConstBufferLocker::InsertBindlessSampler(u32 buffer, u32 offset, SamplerDes
78 bindless_samplers.insert_or_assign({buffer, offset}, sampler); 90 bindless_samplers.insert_or_assign({buffer, offset}, sampler);
79} 91}
80 92
93void ConstBufferLocker::SetBoundBuffer(u32 buffer) {
94 bound_buffer_saved = true;
95 bound_buffer = buffer;
96}
97
81bool ConstBufferLocker::IsConsistent() const { 98bool ConstBufferLocker::IsConsistent() const {
82 if (!engine) { 99 if (!engine) {
83 return false; 100 return false;
diff --git a/src/video_core/shader/const_buffer_locker.h b/src/video_core/shader/const_buffer_locker.h
index d32e2d657..d3ea11087 100644
--- a/src/video_core/shader/const_buffer_locker.h
+++ b/src/video_core/shader/const_buffer_locker.h
@@ -10,6 +10,7 @@
10#include "common/hash.h" 10#include "common/hash.h"
11#include "video_core/engines/const_buffer_engine_interface.h" 11#include "video_core/engines/const_buffer_engine_interface.h"
12#include "video_core/engines/shader_type.h" 12#include "video_core/engines/shader_type.h"
13#include "video_core/guest_driver.h"
13 14
14namespace VideoCommon::Shader { 15namespace VideoCommon::Shader {
15 16
@@ -40,6 +41,8 @@ public:
40 41
41 std::optional<Tegra::Engines::SamplerDescriptor> ObtainBindlessSampler(u32 buffer, u32 offset); 42 std::optional<Tegra::Engines::SamplerDescriptor> ObtainBindlessSampler(u32 buffer, u32 offset);
42 43
44 std::optional<u32> ObtainBoundBuffer();
45
43 /// Inserts a key. 46 /// Inserts a key.
44 void InsertKey(u32 buffer, u32 offset, u32 value); 47 void InsertKey(u32 buffer, u32 offset, u32 value);
45 48
@@ -49,6 +52,9 @@ public:
49 /// Inserts a bindless sampler key. 52 /// Inserts a bindless sampler key.
50 void InsertBindlessSampler(u32 buffer, u32 offset, Tegra::Engines::SamplerDescriptor sampler); 53 void InsertBindlessSampler(u32 buffer, u32 offset, Tegra::Engines::SamplerDescriptor sampler);
51 54
55 /// Set the bound buffer for this locker.
56 void SetBoundBuffer(u32 buffer);
57
52 /// Checks keys and samplers against engine's current const buffers. Returns true if they are 58 /// Checks keys and samplers against engine's current const buffers. Returns true if they are
53 /// the same value, false otherwise; 59 /// the same value, false otherwise;
54 bool IsConsistent() const; 60 bool IsConsistent() const;
@@ -71,12 +77,27 @@ public:
71 return bindless_samplers; 77 return bindless_samplers;
72 } 78 }
73 79
80 /// Gets bound buffer used on this shader
81 u32 GetBoundBuffer() const {
82 return bound_buffer;
83 }
84
85 /// Obtains access to the guest driver's profile.
86 VideoCore::GuestDriverProfile* AccessGuestDriverProfile() const {
87 if (engine) {
88 return &engine->AccessGuestDriverProfile();
89 }
90 return nullptr;
91 }
92
74private: 93private:
75 const Tegra::Engines::ShaderType stage; 94 const Tegra::Engines::ShaderType stage;
76 Tegra::Engines::ConstBufferEngineInterface* engine = nullptr; 95 Tegra::Engines::ConstBufferEngineInterface* engine = nullptr;
77 KeyMap keys; 96 KeyMap keys;
78 BoundSamplerMap bound_samplers; 97 BoundSamplerMap bound_samplers;
79 BindlessSamplerMap bindless_samplers; 98 BindlessSamplerMap bindless_samplers;
99 bool bound_buffer_saved{};
100 u32 bound_buffer{};
80}; 101};
81 102
82} // namespace VideoCommon::Shader 103} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/decode.cpp b/src/video_core/shader/decode.cpp
index 22c3e5120..6b697ed5d 100644
--- a/src/video_core/shader/decode.cpp
+++ b/src/video_core/shader/decode.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cstring> 5#include <cstring>
6#include <limits>
6#include <set> 7#include <set>
7 8
8#include <fmt/format.h> 9#include <fmt/format.h>
@@ -33,6 +34,52 @@ constexpr bool IsSchedInstruction(u32 offset, u32 main_offset) {
33 return (absolute_offset % SchedPeriod) == 0; 34 return (absolute_offset % SchedPeriod) == 0;
34} 35}
35 36
37void DeduceTextureHandlerSize(VideoCore::GuestDriverProfile* gpu_driver,
38 const std::list<Sampler>& used_samplers) {
39 if (gpu_driver == nullptr) {
40 LOG_CRITICAL(HW_GPU, "GPU driver profile has not been created yet");
41 return;
42 }
43 if (gpu_driver->TextureHandlerSizeKnown() || used_samplers.size() <= 1) {
44 return;
45 }
46 u32 count{};
47 std::vector<u32> bound_offsets;
48 for (const auto& sampler : used_samplers) {
49 if (sampler.IsBindless()) {
50 continue;
51 }
52 ++count;
53 bound_offsets.emplace_back(sampler.GetOffset());
54 }
55 if (count > 1) {
56 gpu_driver->DeduceTextureHandlerSize(std::move(bound_offsets));
57 }
58}
59
60std::optional<u32> TryDeduceSamplerSize(const Sampler& sampler_to_deduce,
61 VideoCore::GuestDriverProfile* gpu_driver,
62 const std::list<Sampler>& used_samplers) {
63 if (gpu_driver == nullptr) {
64 LOG_CRITICAL(HW_GPU, "GPU Driver profile has not been created yet");
65 return std::nullopt;
66 }
67 const u32 base_offset = sampler_to_deduce.GetOffset();
68 u32 max_offset{std::numeric_limits<u32>::max()};
69 for (const auto& sampler : used_samplers) {
70 if (sampler.IsBindless()) {
71 continue;
72 }
73 if (sampler.GetOffset() > base_offset) {
74 max_offset = std::min(sampler.GetOffset(), max_offset);
75 }
76 }
77 if (max_offset == std::numeric_limits<u32>::max()) {
78 return std::nullopt;
79 }
80 return ((max_offset - base_offset) * 4) / gpu_driver->GetTextureHandlerSize();
81}
82
36} // Anonymous namespace 83} // Anonymous namespace
37 84
38class ASTDecoder { 85class ASTDecoder {
@@ -315,4 +362,25 @@ u32 ShaderIR::DecodeInstr(NodeBlock& bb, u32 pc) {
315 return pc + 1; 362 return pc + 1;
316} 363}
317 364
365void ShaderIR::PostDecode() {
366 // Deduce texture handler size if needed
367 auto gpu_driver = locker.AccessGuestDriverProfile();
368 DeduceTextureHandlerSize(gpu_driver, used_samplers);
369 // Deduce Indexed Samplers
370 if (!uses_indexed_samplers) {
371 return;
372 }
373 for (auto& sampler : used_samplers) {
374 if (!sampler.IsIndexed()) {
375 continue;
376 }
377 if (const auto size = TryDeduceSamplerSize(sampler, gpu_driver, used_samplers)) {
378 sampler.SetSize(*size);
379 } else {
380 LOG_CRITICAL(HW_GPU, "Failed to deduce size of indexed sampler");
381 sampler.SetSize(1);
382 }
383 }
384}
385
318} // namespace VideoCommon::Shader 386} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/decode/arithmetic.cpp b/src/video_core/shader/decode/arithmetic.cpp
index fcedd2af6..90240c765 100644
--- a/src/video_core/shader/decode/arithmetic.cpp
+++ b/src/video_core/shader/decode/arithmetic.cpp
@@ -21,7 +21,7 @@ u32 ShaderIR::DecodeArithmetic(NodeBlock& bb, u32 pc) {
21 21
22 Node op_a = GetRegister(instr.gpr8); 22 Node op_a = GetRegister(instr.gpr8);
23 23
24 Node op_b = [&]() -> Node { 24 Node op_b = [&] {
25 if (instr.is_b_imm) { 25 if (instr.is_b_imm) {
26 return GetImmediate19(instr); 26 return GetImmediate19(instr);
27 } else if (instr.is_b_gpr) { 27 } else if (instr.is_b_gpr) {
@@ -141,6 +141,15 @@ u32 ShaderIR::DecodeArithmetic(NodeBlock& bb, u32 pc) {
141 SetRegister(bb, instr.gpr0, value); 141 SetRegister(bb, instr.gpr0, value);
142 break; 142 break;
143 } 143 }
144 case OpCode::Id::FCMP_R: {
145 UNIMPLEMENTED_IF(instr.fcmp.ftz == 0);
146 Node op_c = GetRegister(instr.gpr39);
147 Node comp = GetPredicateComparisonFloat(instr.fcmp.cond, std::move(op_c), Immediate(0.0f));
148 SetRegister(
149 bb, instr.gpr0,
150 Operation(OperationCode::Select, std::move(comp), std::move(op_a), std::move(op_b)));
151 break;
152 }
144 case OpCode::Id::RRO_C: 153 case OpCode::Id::RRO_C:
145 case OpCode::Id::RRO_R: 154 case OpCode::Id::RRO_R:
146 case OpCode::Id::RRO_IMM: { 155 case OpCode::Id::RRO_IMM: {
diff --git a/src/video_core/shader/decode/arithmetic_integer.cpp b/src/video_core/shader/decode/arithmetic_integer.cpp
index 371fae127..e60875cc4 100644
--- a/src/video_core/shader/decode/arithmetic_integer.cpp
+++ b/src/video_core/shader/decode/arithmetic_integer.cpp
@@ -297,7 +297,7 @@ void ShaderIR::WriteLop3Instruction(NodeBlock& bb, Register dest, Node op_a, Nod
297 const Node one = Immediate(1); 297 const Node one = Immediate(1);
298 const Node two = Immediate(2); 298 const Node two = Immediate(2);
299 299
300 Node value{}; 300 Node value;
301 for (u32 i = 0; i < lop_iterations; ++i) { 301 for (u32 i = 0; i < lop_iterations; ++i) {
302 const Node shift_amount = Immediate(i); 302 const Node shift_amount = Immediate(i);
303 303
diff --git a/src/video_core/shader/decode/bfi.cpp b/src/video_core/shader/decode/bfi.cpp
index 8be1119df..f992bbe2a 100644
--- a/src/video_core/shader/decode/bfi.cpp
+++ b/src/video_core/shader/decode/bfi.cpp
@@ -17,10 +17,13 @@ u32 ShaderIR::DecodeBfi(NodeBlock& bb, u32 pc) {
17 const Instruction instr = {program_code[pc]}; 17 const Instruction instr = {program_code[pc]};
18 const auto opcode = OpCode::Decode(instr); 18 const auto opcode = OpCode::Decode(instr);
19 19
20 const auto [base, packed_shift] = [&]() -> std::tuple<Node, Node> { 20 const auto [packed_shift, base] = [&]() -> std::pair<Node, Node> {
21 switch (opcode->get().GetId()) { 21 switch (opcode->get().GetId()) {
22 case OpCode::Id::BFI_RC:
23 return {GetRegister(instr.gpr39),
24 GetConstBuffer(instr.cbuf34.index, instr.cbuf34.offset)};
22 case OpCode::Id::BFI_IMM_R: 25 case OpCode::Id::BFI_IMM_R:
23 return {GetRegister(instr.gpr39), Immediate(instr.alu.GetSignedImm20_20())}; 26 return {Immediate(instr.alu.GetSignedImm20_20()), GetRegister(instr.gpr39)};
24 default: 27 default:
25 UNREACHABLE(); 28 UNREACHABLE();
26 return {Immediate(0), Immediate(0)}; 29 return {Immediate(0), Immediate(0)};
diff --git a/src/video_core/shader/decode/other.cpp b/src/video_core/shader/decode/other.cpp
index 7321698b2..4944e9d69 100644
--- a/src/video_core/shader/decode/other.cpp
+++ b/src/video_core/shader/decode/other.cpp
@@ -69,13 +69,16 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
69 case OpCode::Id::MOV_SYS: { 69 case OpCode::Id::MOV_SYS: {
70 const Node value = [this, instr] { 70 const Node value = [this, instr] {
71 switch (instr.sys20) { 71 switch (instr.sys20) {
72 case SystemVariable::LaneId:
73 LOG_WARNING(HW_GPU, "MOV_SYS instruction with LaneId is incomplete");
74 return Immediate(0U);
72 case SystemVariable::InvocationId: 75 case SystemVariable::InvocationId:
73 return Operation(OperationCode::InvocationId); 76 return Operation(OperationCode::InvocationId);
74 case SystemVariable::Ydirection: 77 case SystemVariable::Ydirection:
75 return Operation(OperationCode::YNegate); 78 return Operation(OperationCode::YNegate);
76 case SystemVariable::InvocationInfo: 79 case SystemVariable::InvocationInfo:
77 LOG_WARNING(HW_GPU, "MOV_SYS instruction with InvocationInfo is incomplete"); 80 LOG_WARNING(HW_GPU, "MOV_SYS instruction with InvocationInfo is incomplete");
78 return Immediate(0u); 81 return Immediate(0U);
79 case SystemVariable::Tid: { 82 case SystemVariable::Tid: {
80 Node value = Immediate(0); 83 Node value = Immediate(0);
81 value = BitfieldInsert(value, Operation(OperationCode::LocalInvocationIdX), 0, 9); 84 value = BitfieldInsert(value, Operation(OperationCode::LocalInvocationIdX), 0, 9);
@@ -188,7 +191,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
188 UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "SYNC condition code used: {}", 191 UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "SYNC condition code used: {}",
189 static_cast<u32>(cc)); 192 static_cast<u32>(cc));
190 193
191 if (disable_flow_stack) { 194 if (decompiled) {
192 break; 195 break;
193 } 196 }
194 197
@@ -200,7 +203,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
200 const Tegra::Shader::ConditionCode cc = instr.flow_condition_code; 203 const Tegra::Shader::ConditionCode cc = instr.flow_condition_code;
201 UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "BRK condition code used: {}", 204 UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "BRK condition code used: {}",
202 static_cast<u32>(cc)); 205 static_cast<u32>(cc));
203 if (disable_flow_stack) { 206 if (decompiled) {
204 break; 207 break;
205 } 208 }
206 209
diff --git a/src/video_core/shader/decode/shift.cpp b/src/video_core/shader/decode/shift.cpp
index d419e9c45..3b391d3e6 100644
--- a/src/video_core/shader/decode/shift.cpp
+++ b/src/video_core/shader/decode/shift.cpp
@@ -10,8 +10,80 @@
10 10
11namespace VideoCommon::Shader { 11namespace VideoCommon::Shader {
12 12
13using std::move;
13using Tegra::Shader::Instruction; 14using Tegra::Shader::Instruction;
14using Tegra::Shader::OpCode; 15using Tegra::Shader::OpCode;
16using Tegra::Shader::ShfType;
17using Tegra::Shader::ShfXmode;
18
19namespace {
20
21Node IsFull(Node shift) {
22 return Operation(OperationCode::LogicalIEqual, move(shift), Immediate(32));
23}
24
25Node Shift(OperationCode opcode, Node value, Node shift) {
26 Node is_full = Operation(OperationCode::LogicalIEqual, shift, Immediate(32));
27 Node shifted = Operation(opcode, move(value), shift);
28 return Operation(OperationCode::Select, IsFull(move(shift)), Immediate(0), move(shifted));
29}
30
31Node ClampShift(Node shift, s32 size = 32) {
32 shift = Operation(OperationCode::IMax, move(shift), Immediate(0));
33 return Operation(OperationCode::IMin, move(shift), Immediate(size));
34}
35
36Node WrapShift(Node shift, s32 size = 32) {
37 return Operation(OperationCode::UBitwiseAnd, move(shift), Immediate(size - 1));
38}
39
40Node ShiftRight(Node low, Node high, Node shift, Node low_shift, ShfType type) {
41 // These values are used when the shift value is less than 32
42 Node less_low = Shift(OperationCode::ILogicalShiftRight, low, shift);
43 Node less_high = Shift(OperationCode::ILogicalShiftLeft, high, low_shift);
44 Node less = Operation(OperationCode::IBitwiseOr, move(less_high), move(less_low));
45
46 if (type == ShfType::Bits32) {
47 // On 32 bit shifts we are either full (shifting 32) or shifting less than 32 bits
48 return Operation(OperationCode::Select, IsFull(move(shift)), move(high), move(less));
49 }
50
51 // And these when it's larger than or 32
52 const bool is_signed = type == ShfType::S64;
53 const auto opcode = SignedToUnsignedCode(OperationCode::IArithmeticShiftRight, is_signed);
54 Node reduced = Operation(OperationCode::IAdd, shift, Immediate(-32));
55 Node greater = Shift(opcode, high, move(reduced));
56
57 Node is_less = Operation(OperationCode::LogicalILessThan, shift, Immediate(32));
58 Node is_zero = Operation(OperationCode::LogicalIEqual, move(shift), Immediate(0));
59
60 Node value = Operation(OperationCode::Select, move(is_less), move(less), move(greater));
61 return Operation(OperationCode::Select, move(is_zero), move(high), move(value));
62}
63
64Node ShiftLeft(Node low, Node high, Node shift, Node low_shift, ShfType type) {
65 // These values are used when the shift value is less than 32
66 Node less_low = Operation(OperationCode::ILogicalShiftRight, low, low_shift);
67 Node less_high = Operation(OperationCode::ILogicalShiftLeft, high, shift);
68 Node less = Operation(OperationCode::IBitwiseOr, move(less_low), move(less_high));
69
70 if (type == ShfType::Bits32) {
71 // On 32 bit shifts we are either full (shifting 32) or shifting less than 32 bits
72 return Operation(OperationCode::Select, IsFull(move(shift)), move(low), move(less));
73 }
74
75 // And these when it's larger than or 32
76 Node reduced = Operation(OperationCode::IAdd, shift, Immediate(-32));
77 Node greater = Shift(OperationCode::ILogicalShiftLeft, move(low), move(reduced));
78
79 Node is_less = Operation(OperationCode::LogicalILessThan, shift, Immediate(32));
80 Node is_zero = Operation(OperationCode::LogicalIEqual, move(shift), Immediate(0));
81
82 Node value = Operation(OperationCode::Select, move(is_less), move(less), move(greater));
83 return Operation(OperationCode::Select, move(is_zero), move(high), move(value));
84}
85
86} // Anonymous namespace
15 87
16u32 ShaderIR::DecodeShift(NodeBlock& bb, u32 pc) { 88u32 ShaderIR::DecodeShift(NodeBlock& bb, u32 pc) {
17 const Instruction instr = {program_code[pc]}; 89 const Instruction instr = {program_code[pc]};
@@ -28,29 +100,48 @@ u32 ShaderIR::DecodeShift(NodeBlock& bb, u32 pc) {
28 } 100 }
29 }(); 101 }();
30 102
31 switch (opcode->get().GetId()) { 103 switch (const auto opid = opcode->get().GetId(); opid) {
32 case OpCode::Id::SHR_C: 104 case OpCode::Id::SHR_C:
33 case OpCode::Id::SHR_R: 105 case OpCode::Id::SHR_R:
34 case OpCode::Id::SHR_IMM: { 106 case OpCode::Id::SHR_IMM: {
35 if (instr.shr.wrap) { 107 op_b = instr.shr.wrap ? WrapShift(move(op_b)) : ClampShift(move(op_b));
36 op_b = Operation(OperationCode::UBitwiseAnd, std::move(op_b), Immediate(0x1f));
37 } else {
38 op_b = Operation(OperationCode::IMax, std::move(op_b), Immediate(0));
39 op_b = Operation(OperationCode::IMin, std::move(op_b), Immediate(31));
40 }
41 108
42 Node value = SignedOperation(OperationCode::IArithmeticShiftRight, instr.shift.is_signed, 109 Node value = SignedOperation(OperationCode::IArithmeticShiftRight, instr.shift.is_signed,
43 std::move(op_a), std::move(op_b)); 110 move(op_a), move(op_b));
44 SetInternalFlagsFromInteger(bb, value, instr.generates_cc); 111 SetInternalFlagsFromInteger(bb, value, instr.generates_cc);
45 SetRegister(bb, instr.gpr0, std::move(value)); 112 SetRegister(bb, instr.gpr0, move(value));
46 break; 113 break;
47 } 114 }
48 case OpCode::Id::SHL_C: 115 case OpCode::Id::SHL_C:
49 case OpCode::Id::SHL_R: 116 case OpCode::Id::SHL_R:
50 case OpCode::Id::SHL_IMM: { 117 case OpCode::Id::SHL_IMM: {
51 const Node value = Operation(OperationCode::ILogicalShiftLeft, op_a, op_b); 118 Node value = Operation(OperationCode::ILogicalShiftLeft, op_a, op_b);
52 SetInternalFlagsFromInteger(bb, value, instr.generates_cc); 119 SetInternalFlagsFromInteger(bb, value, instr.generates_cc);
53 SetRegister(bb, instr.gpr0, value); 120 SetRegister(bb, instr.gpr0, move(value));
121 break;
122 }
123 case OpCode::Id::SHF_RIGHT_R:
124 case OpCode::Id::SHF_RIGHT_IMM:
125 case OpCode::Id::SHF_LEFT_R:
126 case OpCode::Id::SHF_LEFT_IMM: {
127 UNIMPLEMENTED_IF(instr.generates_cc);
128 UNIMPLEMENTED_IF_MSG(instr.shf.xmode != ShfXmode::None, "xmode={}",
129 static_cast<int>(instr.shf.xmode.Value()));
130
131 if (instr.is_b_imm) {
132 op_b = Immediate(static_cast<u32>(instr.shf.immediate));
133 }
134 const s32 size = instr.shf.type == ShfType::Bits32 ? 32 : 64;
135 Node shift = instr.shf.wrap ? WrapShift(move(op_b), size) : ClampShift(move(op_b), size);
136
137 Node negated_shift = Operation(OperationCode::INegate, shift);
138 Node low_shift = Operation(OperationCode::IAdd, move(negated_shift), Immediate(32));
139
140 const bool is_right = opid == OpCode::Id::SHF_RIGHT_R || opid == OpCode::Id::SHF_RIGHT_IMM;
141 Node value = (is_right ? ShiftRight : ShiftLeft)(
142 move(op_a), GetRegister(instr.gpr39), move(shift), move(low_shift), instr.shf.type);
143
144 SetRegister(bb, instr.gpr0, move(value));
54 break; 145 break;
55 } 146 }
56 default: 147 default:
diff --git a/src/video_core/shader/decode/texture.cpp b/src/video_core/shader/decode/texture.cpp
index 0b567e39d..351c8c2f1 100644
--- a/src/video_core/shader/decode/texture.cpp
+++ b/src/video_core/shader/decode/texture.cpp
@@ -144,7 +144,8 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
144 Node4 values; 144 Node4 values;
145 for (u32 element = 0; element < values.size(); ++element) { 145 for (u32 element = 0; element < values.size(); ++element) {
146 auto coords_copy = coords; 146 auto coords_copy = coords;
147 MetaTexture meta{sampler, {}, depth_compare, aoffi, {}, {}, {}, {}, component, element}; 147 MetaTexture meta{sampler, {}, depth_compare, aoffi, {}, {},
148 {}, {}, component, element, {}};
148 values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy)); 149 values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy));
149 } 150 }
150 151
@@ -167,9 +168,9 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
167 const auto derivate_reg = instr.gpr20.Value(); 168 const auto derivate_reg = instr.gpr20.Value();
168 const auto texture_type = instr.txd.texture_type.Value(); 169 const auto texture_type = instr.txd.texture_type.Value();
169 const auto coord_count = GetCoordCount(texture_type); 170 const auto coord_count = GetCoordCount(texture_type);
170 171 Node index_var{};
171 const Sampler* sampler = 172 const Sampler* sampler =
172 is_bindless ? GetBindlessSampler(base_reg, {{texture_type, is_array, false}}) 173 is_bindless ? GetBindlessSampler(base_reg, index_var, {{texture_type, is_array, false}})
173 : GetSampler(instr.sampler, {{texture_type, is_array, false}}); 174 : GetSampler(instr.sampler, {{texture_type, is_array, false}});
174 Node4 values; 175 Node4 values;
175 if (sampler == nullptr) { 176 if (sampler == nullptr) {
@@ -200,7 +201,8 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
200 } 201 }
201 202
202 for (u32 element = 0; element < values.size(); ++element) { 203 for (u32 element = 0; element < values.size(); ++element) {
203 MetaTexture meta{*sampler, array_node, {}, {}, {}, derivates, {}, {}, {}, element}; 204 MetaTexture meta{*sampler, array_node, {}, {}, {}, derivates,
205 {}, {}, {}, element, index_var};
204 values[element] = Operation(OperationCode::TextureGradient, std::move(meta), coords); 206 values[element] = Operation(OperationCode::TextureGradient, std::move(meta), coords);
205 } 207 }
206 208
@@ -215,8 +217,9 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
215 // TODO: The new commits on the texture refactor, change the way samplers work. 217 // TODO: The new commits on the texture refactor, change the way samplers work.
216 // Sadly, not all texture instructions specify the type of texture their sampler 218 // Sadly, not all texture instructions specify the type of texture their sampler
217 // uses. This must be fixed at a later instance. 219 // uses. This must be fixed at a later instance.
220 Node index_var{};
218 const Sampler* sampler = 221 const Sampler* sampler =
219 is_bindless ? GetBindlessSampler(instr.gpr8) : GetSampler(instr.sampler); 222 is_bindless ? GetBindlessSampler(instr.gpr8, index_var) : GetSampler(instr.sampler);
220 223
221 if (sampler == nullptr) { 224 if (sampler == nullptr) {
222 u32 indexer = 0; 225 u32 indexer = 0;
@@ -240,7 +243,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
240 if (!instr.txq.IsComponentEnabled(element)) { 243 if (!instr.txq.IsComponentEnabled(element)) {
241 continue; 244 continue;
242 } 245 }
243 MetaTexture meta{*sampler, {}, {}, {}, {}, {}, {}, {}, {}, element}; 246 MetaTexture meta{*sampler, {}, {}, {}, {}, {}, {}, {}, {}, element, index_var};
244 const Node value = 247 const Node value =
245 Operation(OperationCode::TextureQueryDimensions, meta, 248 Operation(OperationCode::TextureQueryDimensions, meta,
246 GetRegister(instr.gpr8.Value() + (is_bindless ? 1 : 0))); 249 GetRegister(instr.gpr8.Value() + (is_bindless ? 1 : 0)));
@@ -266,8 +269,9 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
266 269
267 auto texture_type = instr.tmml.texture_type.Value(); 270 auto texture_type = instr.tmml.texture_type.Value();
268 const bool is_array = instr.tmml.array != 0; 271 const bool is_array = instr.tmml.array != 0;
272 Node index_var{};
269 const Sampler* sampler = 273 const Sampler* sampler =
270 is_bindless ? GetBindlessSampler(instr.gpr20) : GetSampler(instr.sampler); 274 is_bindless ? GetBindlessSampler(instr.gpr20, index_var) : GetSampler(instr.sampler);
271 275
272 if (sampler == nullptr) { 276 if (sampler == nullptr) {
273 u32 indexer = 0; 277 u32 indexer = 0;
@@ -309,7 +313,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
309 continue; 313 continue;
310 } 314 }
311 auto params = coords; 315 auto params = coords;
312 MetaTexture meta{*sampler, {}, {}, {}, {}, {}, {}, {}, {}, element}; 316 MetaTexture meta{*sampler, {}, {}, {}, {}, {}, {}, {}, {}, element, index_var};
313 const Node value = Operation(OperationCode::TextureQueryLod, meta, std::move(params)); 317 const Node value = Operation(OperationCode::TextureQueryLod, meta, std::move(params));
314 SetTemporary(bb, indexer++, value); 318 SetTemporary(bb, indexer++, value);
315 } 319 }
@@ -383,37 +387,65 @@ const Sampler* ShaderIR::GetSampler(const Tegra::Shader::Sampler& sampler,
383 // Otherwise create a new mapping for this sampler 387 // Otherwise create a new mapping for this sampler
384 const auto next_index = static_cast<u32>(used_samplers.size()); 388 const auto next_index = static_cast<u32>(used_samplers.size());
385 return &used_samplers.emplace_back(next_index, offset, info.type, info.is_array, info.is_shadow, 389 return &used_samplers.emplace_back(next_index, offset, info.type, info.is_array, info.is_shadow,
386 info.is_buffer); 390 info.is_buffer, false);
387} 391}
388 392
389const Sampler* ShaderIR::GetBindlessSampler(Tegra::Shader::Register reg, 393const Sampler* ShaderIR::GetBindlessSampler(Tegra::Shader::Register reg, Node& index_var,
390 std::optional<SamplerInfo> sampler_info) { 394 std::optional<SamplerInfo> sampler_info) {
391 const Node sampler_register = GetRegister(reg); 395 const Node sampler_register = GetRegister(reg);
392 const auto [base_sampler, buffer, offset] = 396 const auto [base_node, tracked_sampler_info] =
393 TrackCbuf(sampler_register, global_code, static_cast<s64>(global_code.size())); 397 TrackBindlessSampler(sampler_register, global_code, static_cast<s64>(global_code.size()));
394 ASSERT(base_sampler != nullptr); 398 ASSERT(base_node != nullptr);
395 if (base_sampler == nullptr) { 399 if (base_node == nullptr) {
396 return nullptr; 400 return nullptr;
397 } 401 }
398 402
399 const auto info = GetSamplerInfo(sampler_info, offset, buffer); 403 if (const auto bindless_sampler_info =
404 std::get_if<BindlessSamplerNode>(&*tracked_sampler_info)) {
405 const u32 buffer = bindless_sampler_info->GetIndex();
406 const u32 offset = bindless_sampler_info->GetOffset();
407 const auto info = GetSamplerInfo(sampler_info, offset, buffer);
408
409 // If this sampler has already been used, return the existing mapping.
410 const auto it =
411 std::find_if(used_samplers.begin(), used_samplers.end(),
412 [buffer = buffer, offset = offset](const Sampler& entry) {
413 return entry.GetBuffer() == buffer && entry.GetOffset() == offset;
414 });
415 if (it != used_samplers.end()) {
416 ASSERT(it->IsBindless() && it->GetType() == info.type &&
417 it->IsArray() == info.is_array && it->IsShadow() == info.is_shadow);
418 return &*it;
419 }
400 420
401 // If this sampler has already been used, return the existing mapping. 421 // Otherwise create a new mapping for this sampler
402 const auto it = 422 const auto next_index = static_cast<u32>(used_samplers.size());
403 std::find_if(used_samplers.begin(), used_samplers.end(), 423 return &used_samplers.emplace_back(next_index, offset, buffer, info.type, info.is_array,
404 [buffer = buffer, offset = offset](const Sampler& entry) { 424 info.is_shadow, info.is_buffer, false);
405 return entry.GetBuffer() == buffer && entry.GetOffset() == offset; 425 } else if (const auto array_sampler_info =
406 }); 426 std::get_if<ArraySamplerNode>(&*tracked_sampler_info)) {
407 if (it != used_samplers.end()) { 427 const u32 base_offset = array_sampler_info->GetBaseOffset() / 4;
408 ASSERT(it->IsBindless() && it->GetType() == info.type && it->IsArray() == info.is_array && 428 index_var = GetCustomVariable(array_sampler_info->GetIndexVar());
409 it->IsShadow() == info.is_shadow); 429 const auto info = GetSamplerInfo(sampler_info, base_offset);
410 return &*it; 430
411 } 431 // If this sampler has already been used, return the existing mapping.
432 const auto it = std::find_if(
433 used_samplers.begin(), used_samplers.end(),
434 [base_offset](const Sampler& entry) { return entry.GetOffset() == base_offset; });
435 if (it != used_samplers.end()) {
436 ASSERT(!it->IsBindless() && it->GetType() == info.type &&
437 it->IsArray() == info.is_array && it->IsShadow() == info.is_shadow &&
438 it->IsBuffer() == info.is_buffer && it->IsIndexed());
439 return &*it;
440 }
412 441
413 // Otherwise create a new mapping for this sampler 442 uses_indexed_samplers = true;
414 const auto next_index = static_cast<u32>(used_samplers.size()); 443 // Otherwise create a new mapping for this sampler
415 return &used_samplers.emplace_back(next_index, offset, buffer, info.type, info.is_array, 444 const auto next_index = static_cast<u32>(used_samplers.size());
416 info.is_shadow, info.is_buffer); 445 return &used_samplers.emplace_back(next_index, base_offset, info.type, info.is_array,
446 info.is_shadow, info.is_buffer, true);
447 }
448 return nullptr;
417} 449}
418 450
419void ShaderIR::WriteTexInstructionFloat(NodeBlock& bb, Instruction instr, const Node4& components) { 451void ShaderIR::WriteTexInstructionFloat(NodeBlock& bb, Instruction instr, const Node4& components) {
@@ -499,8 +531,9 @@ Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type,
499 "This method is not supported."); 531 "This method is not supported.");
500 532
501 const SamplerInfo info{texture_type, is_array, is_shadow, false}; 533 const SamplerInfo info{texture_type, is_array, is_shadow, false};
502 const Sampler* sampler = 534 Node index_var{};
503 is_bindless ? GetBindlessSampler(*bindless_reg, info) : GetSampler(instr.sampler, info); 535 const Sampler* sampler = is_bindless ? GetBindlessSampler(*bindless_reg, index_var, info)
536 : GetSampler(instr.sampler, info);
504 Node4 values; 537 Node4 values;
505 if (sampler == nullptr) { 538 if (sampler == nullptr) {
506 for (u32 element = 0; element < values.size(); ++element) { 539 for (u32 element = 0; element < values.size(); ++element) {
@@ -548,7 +581,8 @@ Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type,
548 581
549 for (u32 element = 0; element < values.size(); ++element) { 582 for (u32 element = 0; element < values.size(); ++element) {
550 auto copy_coords = coords; 583 auto copy_coords = coords;
551 MetaTexture meta{*sampler, array, depth_compare, aoffi, {}, {}, bias, lod, {}, element}; 584 MetaTexture meta{*sampler, array, depth_compare, aoffi, {}, {}, bias,
585 lod, {}, element, index_var};
552 values[element] = Operation(read_method, meta, std::move(copy_coords)); 586 values[element] = Operation(read_method, meta, std::move(copy_coords));
553 } 587 }
554 588
@@ -596,7 +630,7 @@ Node4 ShaderIR::GetTexCode(Instruction instr, TextureType texture_type,
596 aoffi = GetAoffiCoordinates(GetRegister(parameter_register++), coord_count, false); 630 aoffi = GetAoffiCoordinates(GetRegister(parameter_register++), coord_count, false);
597 } 631 }
598 632
599 Node dc{}; 633 Node dc;
600 if (depth_compare) { 634 if (depth_compare) {
601 // Depth is always stored in the register signaled by gpr20 or in the next register if lod 635 // Depth is always stored in the register signaled by gpr20 or in the next register if lod
602 // or bias are used 636 // or bias are used
@@ -632,7 +666,7 @@ Node4 ShaderIR::GetTexsCode(Instruction instr, TextureType texture_type,
632 666
633 const Node array = is_array ? GetRegister(array_register) : nullptr; 667 const Node array = is_array ? GetRegister(array_register) : nullptr;
634 668
635 Node dc{}; 669 Node dc;
636 if (depth_compare) { 670 if (depth_compare) {
637 // Depth is always stored in the register signaled by gpr20 or in the next register if lod 671 // Depth is always stored in the register signaled by gpr20 or in the next register if lod
638 // or bias are used 672 // or bias are used
@@ -663,7 +697,8 @@ Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool de
663 u64 parameter_register = instr.gpr20.Value(); 697 u64 parameter_register = instr.gpr20.Value();
664 698
665 const SamplerInfo info{texture_type, is_array, depth_compare, false}; 699 const SamplerInfo info{texture_type, is_array, depth_compare, false};
666 const Sampler* sampler = is_bindless ? GetBindlessSampler(parameter_register++, info) 700 Node index_var{};
701 const Sampler* sampler = is_bindless ? GetBindlessSampler(parameter_register++, index_var, info)
667 : GetSampler(instr.sampler, info); 702 : GetSampler(instr.sampler, info);
668 Node4 values; 703 Node4 values;
669 if (sampler == nullptr) { 704 if (sampler == nullptr) {
@@ -692,7 +727,8 @@ Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool de
692 for (u32 element = 0; element < values.size(); ++element) { 727 for (u32 element = 0; element < values.size(); ++element) {
693 auto coords_copy = coords; 728 auto coords_copy = coords;
694 MetaTexture meta{ 729 MetaTexture meta{
695 *sampler, GetRegister(array_register), dc, aoffi, ptp, {}, {}, {}, component, element}; 730 *sampler, GetRegister(array_register), dc, aoffi, ptp, {}, {}, {}, component, element,
731 index_var};
696 values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy)); 732 values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy));
697 } 733 }
698 734
@@ -725,7 +761,7 @@ Node4 ShaderIR::GetTldCode(Tegra::Shader::Instruction instr) {
725 Node4 values; 761 Node4 values;
726 for (u32 element = 0; element < values.size(); ++element) { 762 for (u32 element = 0; element < values.size(); ++element) {
727 auto coords_copy = coords; 763 auto coords_copy = coords;
728 MetaTexture meta{sampler, array_register, {}, {}, {}, {}, {}, lod, {}, element}; 764 MetaTexture meta{sampler, array_register, {}, {}, {}, {}, {}, lod, {}, element, {}};
729 values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy)); 765 values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy));
730 } 766 }
731 767
@@ -775,7 +811,7 @@ Node4 ShaderIR::GetTldsCode(Instruction instr, TextureType texture_type, bool is
775 Node4 values; 811 Node4 values;
776 for (u32 element = 0; element < values.size(); ++element) { 812 for (u32 element = 0; element < values.size(); ++element) {
777 auto coords_copy = coords; 813 auto coords_copy = coords;
778 MetaTexture meta{sampler, array, {}, {}, {}, {}, {}, lod, {}, element}; 814 MetaTexture meta{sampler, array, {}, {}, {}, {}, {}, lod, {}, element, {}};
779 values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy)); 815 values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy));
780 } 816 }
781 return values; 817 return values;
diff --git a/src/video_core/shader/node.h b/src/video_core/shader/node.h
index 9af1f0228..a0a7b9111 100644
--- a/src/video_core/shader/node.h
+++ b/src/video_core/shader/node.h
@@ -212,6 +212,7 @@ enum class MetaStackClass {
212class OperationNode; 212class OperationNode;
213class ConditionalNode; 213class ConditionalNode;
214class GprNode; 214class GprNode;
215class CustomVarNode;
215class ImmediateNode; 216class ImmediateNode;
216class InternalFlagNode; 217class InternalFlagNode;
217class PredicateNode; 218class PredicateNode;
@@ -223,26 +224,32 @@ class SmemNode;
223class GmemNode; 224class GmemNode;
224class CommentNode; 225class CommentNode;
225 226
226using NodeData = std::variant<OperationNode, ConditionalNode, GprNode, ImmediateNode, 227using NodeData = std::variant<OperationNode, ConditionalNode, GprNode, CustomVarNode, ImmediateNode,
227 InternalFlagNode, PredicateNode, AbufNode, PatchNode, CbufNode, 228 InternalFlagNode, PredicateNode, AbufNode, PatchNode, CbufNode,
228 LmemNode, SmemNode, GmemNode, CommentNode>; 229 LmemNode, SmemNode, GmemNode, CommentNode>;
229using Node = std::shared_ptr<NodeData>; 230using Node = std::shared_ptr<NodeData>;
230using Node4 = std::array<Node, 4>; 231using Node4 = std::array<Node, 4>;
231using NodeBlock = std::vector<Node>; 232using NodeBlock = std::vector<Node>;
232 233
234class BindlessSamplerNode;
235class ArraySamplerNode;
236
237using TrackSamplerData = std::variant<BindlessSamplerNode, ArraySamplerNode>;
238using TrackSampler = std::shared_ptr<TrackSamplerData>;
239
233class Sampler { 240class Sampler {
234public: 241public:
235 /// This constructor is for bound samplers 242 /// This constructor is for bound samplers
236 constexpr explicit Sampler(u32 index, u32 offset, Tegra::Shader::TextureType type, 243 constexpr explicit Sampler(u32 index, u32 offset, Tegra::Shader::TextureType type,
237 bool is_array, bool is_shadow, bool is_buffer) 244 bool is_array, bool is_shadow, bool is_buffer, bool is_indexed)
238 : index{index}, offset{offset}, type{type}, is_array{is_array}, is_shadow{is_shadow}, 245 : index{index}, offset{offset}, type{type}, is_array{is_array}, is_shadow{is_shadow},
239 is_buffer{is_buffer} {} 246 is_buffer{is_buffer}, is_indexed{is_indexed} {}
240 247
241 /// This constructor is for bindless samplers 248 /// This constructor is for bindless samplers
242 constexpr explicit Sampler(u32 index, u32 offset, u32 buffer, Tegra::Shader::TextureType type, 249 constexpr explicit Sampler(u32 index, u32 offset, u32 buffer, Tegra::Shader::TextureType type,
243 bool is_array, bool is_shadow, bool is_buffer) 250 bool is_array, bool is_shadow, bool is_buffer, bool is_indexed)
244 : index{index}, offset{offset}, buffer{buffer}, type{type}, is_array{is_array}, 251 : index{index}, offset{offset}, buffer{buffer}, type{type}, is_array{is_array},
245 is_shadow{is_shadow}, is_buffer{is_buffer}, is_bindless{true} {} 252 is_shadow{is_shadow}, is_buffer{is_buffer}, is_bindless{true}, is_indexed{is_indexed} {}
246 253
247 constexpr u32 GetIndex() const { 254 constexpr u32 GetIndex() const {
248 return index; 255 return index;
@@ -276,16 +283,72 @@ public:
276 return is_bindless; 283 return is_bindless;
277 } 284 }
278 285
286 constexpr bool IsIndexed() const {
287 return is_indexed;
288 }
289
290 constexpr u32 Size() const {
291 return size;
292 }
293
294 constexpr void SetSize(u32 new_size) {
295 size = new_size;
296 }
297
279private: 298private:
280 u32 index{}; ///< Emulated index given for the this sampler. 299 u32 index{}; ///< Emulated index given for the this sampler.
281 u32 offset{}; ///< Offset in the const buffer from where the sampler is being read. 300 u32 offset{}; ///< Offset in the const buffer from where the sampler is being read.
282 u32 buffer{}; ///< Buffer where the bindless sampler is being read (unused on bound samplers). 301 u32 buffer{}; ///< Buffer where the bindless sampler is being read (unused on bound samplers).
302 u32 size{}; ///< Size of the sampler if indexed.
283 303
284 Tegra::Shader::TextureType type{}; ///< The type used to sample this texture (Texture2D, etc) 304 Tegra::Shader::TextureType type{}; ///< The type used to sample this texture (Texture2D, etc)
285 bool is_array{}; ///< Whether the texture is being sampled as an array texture or not. 305 bool is_array{}; ///< Whether the texture is being sampled as an array texture or not.
286 bool is_shadow{}; ///< Whether the texture is being sampled as a depth texture or not. 306 bool is_shadow{}; ///< Whether the texture is being sampled as a depth texture or not.
287 bool is_buffer{}; ///< Whether the texture is a texture buffer without sampler. 307 bool is_buffer{}; ///< Whether the texture is a texture buffer without sampler.
288 bool is_bindless{}; ///< Whether this sampler belongs to a bindless texture or not. 308 bool is_bindless{}; ///< Whether this sampler belongs to a bindless texture or not.
309 bool is_indexed{}; ///< Whether this sampler is an indexed array of textures.
310};
311
312/// Represents a tracked bindless sampler into a direct const buffer
313class ArraySamplerNode final {
314public:
315 explicit ArraySamplerNode(u32 index, u32 base_offset, u32 bindless_var)
316 : index{index}, base_offset{base_offset}, bindless_var{bindless_var} {}
317
318 constexpr u32 GetIndex() const {
319 return index;
320 }
321
322 constexpr u32 GetBaseOffset() const {
323 return base_offset;
324 }
325
326 constexpr u32 GetIndexVar() const {
327 return bindless_var;
328 }
329
330private:
331 u32 index;
332 u32 base_offset;
333 u32 bindless_var;
334};
335
336/// Represents a tracked bindless sampler into a direct const buffer
337class BindlessSamplerNode final {
338public:
339 explicit BindlessSamplerNode(u32 index, u32 offset) : index{index}, offset{offset} {}
340
341 constexpr u32 GetIndex() const {
342 return index;
343 }
344
345 constexpr u32 GetOffset() const {
346 return offset;
347 }
348
349private:
350 u32 index;
351 u32 offset;
289}; 352};
290 353
291class Image final { 354class Image final {
@@ -380,8 +443,9 @@ struct MetaTexture {
380 std::vector<Node> derivates; 443 std::vector<Node> derivates;
381 Node bias; 444 Node bias;
382 Node lod; 445 Node lod;
383 Node component{}; 446 Node component;
384 u32 element{}; 447 u32 element{};
448 Node index;
385}; 449};
386 450
387struct MetaImage { 451struct MetaImage {
@@ -488,6 +552,19 @@ private:
488 Tegra::Shader::Register index{}; 552 Tegra::Shader::Register index{};
489}; 553};
490 554
555/// A custom variable
556class CustomVarNode final {
557public:
558 explicit constexpr CustomVarNode(u32 index) : index{index} {}
559
560 constexpr u32 GetIndex() const {
561 return index;
562 }
563
564private:
565 u32 index{};
566};
567
491/// A 32-bits value that represents an immediate value 568/// A 32-bits value that represents an immediate value
492class ImmediateNode final { 569class ImmediateNode final {
493public: 570public:
diff --git a/src/video_core/shader/node_helper.h b/src/video_core/shader/node_helper.h
index 0c2aa749b..11231bbea 100644
--- a/src/video_core/shader/node_helper.h
+++ b/src/video_core/shader/node_helper.h
@@ -45,6 +45,12 @@ Node MakeNode(Args&&... args) {
45 return std::make_shared<NodeData>(T(std::forward<Args>(args)...)); 45 return std::make_shared<NodeData>(T(std::forward<Args>(args)...));
46} 46}
47 47
48template <typename T, typename... Args>
49TrackSampler MakeTrackSampler(Args&&... args) {
50 static_assert(std::is_convertible_v<T, TrackSamplerData>);
51 return std::make_shared<TrackSamplerData>(T(std::forward<Args>(args)...));
52}
53
48template <typename... Args> 54template <typename... Args>
49Node Operation(OperationCode code, Args&&... args) { 55Node Operation(OperationCode code, Args&&... args) {
50 if constexpr (sizeof...(args) == 0) { 56 if constexpr (sizeof...(args) == 0) {
diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp
index 31eecb3f4..3a5d280a9 100644
--- a/src/video_core/shader/shader_ir.cpp
+++ b/src/video_core/shader/shader_ir.cpp
@@ -27,6 +27,7 @@ ShaderIR::ShaderIR(const ProgramCode& program_code, u32 main_offset, CompilerSet
27 ConstBufferLocker& locker) 27 ConstBufferLocker& locker)
28 : program_code{program_code}, main_offset{main_offset}, settings{settings}, locker{locker} { 28 : program_code{program_code}, main_offset{main_offset}, settings{settings}, locker{locker} {
29 Decode(); 29 Decode();
30 PostDecode();
30} 31}
31 32
32ShaderIR::~ShaderIR() = default; 33ShaderIR::~ShaderIR() = default;
@@ -38,6 +39,10 @@ Node ShaderIR::GetRegister(Register reg) {
38 return MakeNode<GprNode>(reg); 39 return MakeNode<GprNode>(reg);
39} 40}
40 41
42Node ShaderIR::GetCustomVariable(u32 id) {
43 return MakeNode<CustomVarNode>(id);
44}
45
41Node ShaderIR::GetImmediate19(Instruction instr) { 46Node ShaderIR::GetImmediate19(Instruction instr) {
42 return Immediate(instr.alu.GetImm20_19()); 47 return Immediate(instr.alu.GetImm20_19());
43} 48}
@@ -452,4 +457,8 @@ std::size_t ShaderIR::DeclareAmend(Node new_amend) {
452 return id; 457 return id;
453} 458}
454 459
460u32 ShaderIR::NewCustomVariable() {
461 return num_custom_variables++;
462}
463
455} // namespace VideoCommon::Shader 464} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h
index ba1db4c11..b0851c3be 100644
--- a/src/video_core/shader/shader_ir.h
+++ b/src/video_core/shader/shader_ir.h
@@ -180,6 +180,10 @@ public:
180 return amend_code[index]; 180 return amend_code[index];
181 } 181 }
182 182
183 u32 GetNumCustomVariables() const {
184 return num_custom_variables;
185 }
186
183private: 187private:
184 friend class ASTDecoder; 188 friend class ASTDecoder;
185 189
@@ -191,6 +195,7 @@ private:
191 }; 195 };
192 196
193 void Decode(); 197 void Decode();
198 void PostDecode();
194 199
195 NodeBlock DecodeRange(u32 begin, u32 end); 200 NodeBlock DecodeRange(u32 begin, u32 end);
196 void DecodeRangeInner(NodeBlock& bb, u32 begin, u32 end); 201 void DecodeRangeInner(NodeBlock& bb, u32 begin, u32 end);
@@ -235,6 +240,8 @@ private:
235 240
236 /// Generates a node for a passed register. 241 /// Generates a node for a passed register.
237 Node GetRegister(Tegra::Shader::Register reg); 242 Node GetRegister(Tegra::Shader::Register reg);
243 /// Generates a node for a custom variable
244 Node GetCustomVariable(u32 id);
238 /// Generates a node representing a 19-bit immediate value 245 /// Generates a node representing a 19-bit immediate value
239 Node GetImmediate19(Tegra::Shader::Instruction instr); 246 Node GetImmediate19(Tegra::Shader::Instruction instr);
240 /// Generates a node representing a 32-bit immediate value 247 /// Generates a node representing a 32-bit immediate value
@@ -321,7 +328,7 @@ private:
321 std::optional<SamplerInfo> sampler_info = std::nullopt); 328 std::optional<SamplerInfo> sampler_info = std::nullopt);
322 329
323 /// Accesses a texture sampler for a bindless texture. 330 /// Accesses a texture sampler for a bindless texture.
324 const Sampler* GetBindlessSampler(Tegra::Shader::Register reg, 331 const Sampler* GetBindlessSampler(Tegra::Shader::Register reg, Node& index_var,
325 std::optional<SamplerInfo> sampler_info = std::nullopt); 332 std::optional<SamplerInfo> sampler_info = std::nullopt);
326 333
327 /// Accesses an image. 334 /// Accesses an image.
@@ -387,6 +394,9 @@ private:
387 394
388 std::tuple<Node, u32, u32> TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) const; 395 std::tuple<Node, u32, u32> TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) const;
389 396
397 std::tuple<Node, TrackSampler> TrackBindlessSampler(Node tracked, const NodeBlock& code,
398 s64 cursor);
399
390 std::optional<u32> TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor) const; 400 std::optional<u32> TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor) const;
391 401
392 std::pair<Node, s64> TrackRegister(const GprNode* tracked, const NodeBlock& code, 402 std::pair<Node, s64> TrackRegister(const GprNode* tracked, const NodeBlock& code,
@@ -399,6 +409,8 @@ private:
399 /// Register new amending code and obtain the reference id. 409 /// Register new amending code and obtain the reference id.
400 std::size_t DeclareAmend(Node new_amend); 410 std::size_t DeclareAmend(Node new_amend);
401 411
412 u32 NewCustomVariable();
413
402 const ProgramCode& program_code; 414 const ProgramCode& program_code;
403 const u32 main_offset; 415 const u32 main_offset;
404 const CompilerSettings settings; 416 const CompilerSettings settings;
@@ -414,6 +426,7 @@ private:
414 NodeBlock global_code; 426 NodeBlock global_code;
415 ASTManager program_manager{true, true}; 427 ASTManager program_manager{true, true};
416 std::vector<Node> amend_code; 428 std::vector<Node> amend_code;
429 u32 num_custom_variables{};
417 430
418 std::set<u32> used_registers; 431 std::set<u32> used_registers;
419 std::set<Tegra::Shader::Pred> used_predicates; 432 std::set<Tegra::Shader::Pred> used_predicates;
@@ -431,6 +444,7 @@ private:
431 bool uses_instance_id{}; 444 bool uses_instance_id{};
432 bool uses_vertex_id{}; 445 bool uses_vertex_id{};
433 bool uses_warps{}; 446 bool uses_warps{};
447 bool uses_indexed_samplers{};
434 448
435 Tegra::Shader::Header header; 449 Tegra::Shader::Header header;
436}; 450};
diff --git a/src/video_core/shader/track.cpp b/src/video_core/shader/track.cpp
index 165c79330..face8c943 100644
--- a/src/video_core/shader/track.cpp
+++ b/src/video_core/shader/track.cpp
@@ -8,6 +8,7 @@
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "video_core/shader/node.h" 10#include "video_core/shader/node.h"
11#include "video_core/shader/node_helper.h"
11#include "video_core/shader/shader_ir.h" 12#include "video_core/shader/shader_ir.h"
12 13
13namespace VideoCommon::Shader { 14namespace VideoCommon::Shader {
@@ -35,8 +36,113 @@ std::pair<Node, s64> FindOperation(const NodeBlock& code, s64 cursor,
35 } 36 }
36 return {}; 37 return {};
37} 38}
39
40std::optional<std::pair<Node, Node>> DecoupleIndirectRead(const OperationNode& operation) {
41 if (operation.GetCode() != OperationCode::UAdd) {
42 return std::nullopt;
43 }
44 Node gpr;
45 Node offset;
46 ASSERT(operation.GetOperandsCount() == 2);
47 for (std::size_t i = 0; i < operation.GetOperandsCount(); i++) {
48 Node operand = operation[i];
49 if (std::holds_alternative<ImmediateNode>(*operand)) {
50 offset = operation[i];
51 } else if (std::holds_alternative<GprNode>(*operand)) {
52 gpr = operation[i];
53 }
54 }
55 if (offset && gpr) {
56 return std::make_pair(gpr, offset);
57 }
58 return std::nullopt;
59}
60
61bool AmendNodeCv(std::size_t amend_index, Node node) {
62 if (const auto operation = std::get_if<OperationNode>(&*node)) {
63 operation->SetAmendIndex(amend_index);
64 return true;
65 } else if (const auto conditional = std::get_if<ConditionalNode>(&*node)) {
66 conditional->SetAmendIndex(amend_index);
67 return true;
68 }
69 return false;
70}
71
38} // Anonymous namespace 72} // Anonymous namespace
39 73
74std::tuple<Node, TrackSampler> ShaderIR::TrackBindlessSampler(Node tracked, const NodeBlock& code,
75 s64 cursor) {
76 if (const auto cbuf = std::get_if<CbufNode>(&*tracked)) {
77 // Constant buffer found, test if it's an immediate
78 const auto offset = cbuf->GetOffset();
79 if (const auto immediate = std::get_if<ImmediateNode>(&*offset)) {
80 auto track =
81 MakeTrackSampler<BindlessSamplerNode>(cbuf->GetIndex(), immediate->GetValue());
82 return {tracked, track};
83 } else if (const auto operation = std::get_if<OperationNode>(&*offset)) {
84 auto bound_buffer = locker.ObtainBoundBuffer();
85 if (!bound_buffer) {
86 return {};
87 }
88 if (*bound_buffer != cbuf->GetIndex()) {
89 return {};
90 }
91 auto pair = DecoupleIndirectRead(*operation);
92 if (!pair) {
93 return {};
94 }
95 auto [gpr, base_offset] = *pair;
96 const auto offset_inm = std::get_if<ImmediateNode>(&*base_offset);
97 auto gpu_driver = locker.AccessGuestDriverProfile();
98 if (gpu_driver == nullptr) {
99 return {};
100 }
101 const u32 bindless_cv = NewCustomVariable();
102 const Node op = Operation(OperationCode::UDiv, NO_PRECISE, gpr,
103 Immediate(gpu_driver->GetTextureHandlerSize()));
104
105 const Node cv_node = GetCustomVariable(bindless_cv);
106 Node amend_op = Operation(OperationCode::Assign, cv_node, std::move(op));
107 const std::size_t amend_index = DeclareAmend(amend_op);
108 AmendNodeCv(amend_index, code[cursor]);
109 // TODO Implement Bindless Index custom variable
110 auto track = MakeTrackSampler<ArraySamplerNode>(cbuf->GetIndex(),
111 offset_inm->GetValue(), bindless_cv);
112 return {tracked, track};
113 }
114 return {};
115 }
116 if (const auto gpr = std::get_if<GprNode>(&*tracked)) {
117 if (gpr->GetIndex() == Tegra::Shader::Register::ZeroIndex) {
118 return {};
119 }
120 // Reduce the cursor in one to avoid infinite loops when the instruction sets the same
121 // register that it uses as operand
122 const auto [source, new_cursor] = TrackRegister(gpr, code, cursor - 1);
123 if (!source) {
124 return {};
125 }
126 return TrackBindlessSampler(source, code, new_cursor);
127 }
128 if (const auto operation = std::get_if<OperationNode>(&*tracked)) {
129 for (std::size_t i = operation->GetOperandsCount(); i > 0; --i) {
130 if (auto found = TrackBindlessSampler((*operation)[i - 1], code, cursor);
131 std::get<0>(found)) {
132 // Cbuf found in operand.
133 return found;
134 }
135 }
136 return {};
137 }
138 if (const auto conditional = std::get_if<ConditionalNode>(&*tracked)) {
139 const auto& conditional_code = conditional->GetCode();
140 return TrackBindlessSampler(tracked, conditional_code,
141 static_cast<s64>(conditional_code.size()));
142 }
143 return {};
144}
145
40std::tuple<Node, u32, u32> ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code, 146std::tuple<Node, u32, u32> ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code,
41 s64 cursor) const { 147 s64 cursor) const {
42 if (const auto cbuf = std::get_if<CbufNode>(&*tracked)) { 148 if (const auto cbuf = std::get_if<CbufNode>(&*tracked)) {
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index 8e947394c..a5f81a8a0 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -3,19 +3,32 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <memory> 5#include <memory>
6#include "common/logging/log.h"
6#include "core/core.h" 7#include "core/core.h"
7#include "core/settings.h" 8#include "core/settings.h"
8#include "video_core/gpu_asynch.h" 9#include "video_core/gpu_asynch.h"
9#include "video_core/gpu_synch.h" 10#include "video_core/gpu_synch.h"
10#include "video_core/renderer_base.h" 11#include "video_core/renderer_base.h"
11#include "video_core/renderer_opengl/renderer_opengl.h" 12#include "video_core/renderer_opengl/renderer_opengl.h"
13#ifdef HAS_VULKAN
14#include "video_core/renderer_vulkan/renderer_vulkan.h"
15#endif
12#include "video_core/video_core.h" 16#include "video_core/video_core.h"
13 17
14namespace VideoCore { 18namespace VideoCore {
15 19
16std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window, 20std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
17 Core::System& system) { 21 Core::System& system) {
18 return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system); 22 switch (Settings::values.renderer_backend) {
23 case Settings::RendererBackend::OpenGL:
24 return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system);
25#ifdef HAS_VULKAN
26 case Settings::RendererBackend::Vulkan:
27 return std::make_unique<Vulkan::RendererVulkan>(emu_window, system);
28#endif
29 default:
30 return nullptr;
31 }
19} 32}
20 33
21std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system) { 34std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system) {
diff --git a/src/web_service/telemetry_json.cpp b/src/web_service/telemetry_json.cpp
index 9156ce802..7538389bf 100644
--- a/src/web_service/telemetry_json.cpp
+++ b/src/web_service/telemetry_json.cpp
@@ -117,6 +117,7 @@ bool TelemetryJson::SubmitTestcase() {
117 impl->SerializeSection(Telemetry::FieldType::Session, "Session"); 117 impl->SerializeSection(Telemetry::FieldType::Session, "Session");
118 impl->SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback"); 118 impl->SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback");
119 impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem"); 119 impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem");
120 impl->SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig");
120 121
121 auto content = impl->TopSection().dump(); 122 auto content = impl->TopSection().dump();
122 Client client(impl->host, impl->username, impl->token); 123 Client client(impl->host, impl->username, impl->token);
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index a3fb91d29..b841e63fa 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -200,3 +200,8 @@ if (MSVC)
200 copy_yuzu_SDL_deps(yuzu) 200 copy_yuzu_SDL_deps(yuzu)
201 copy_yuzu_unicorn_deps(yuzu) 201 copy_yuzu_unicorn_deps(yuzu)
202endif() 202endif()
203
204if (ENABLE_VULKAN)
205 target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include)
206 target_compile_definitions(yuzu PRIVATE HAS_VULKAN)
207endif()
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 7490fb718..55a37fffa 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -2,19 +2,30 @@
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 <glad/glad.h>
6
5#include <QApplication> 7#include <QApplication>
6#include <QHBoxLayout> 8#include <QHBoxLayout>
7#include <QKeyEvent> 9#include <QKeyEvent>
10#include <QMessageBox>
8#include <QOffscreenSurface> 11#include <QOffscreenSurface>
9#include <QOpenGLWindow> 12#include <QOpenGLWindow>
10#include <QPainter> 13#include <QPainter>
11#include <QScreen> 14#include <QScreen>
15#include <QStringList>
12#include <QWindow> 16#include <QWindow>
17#ifdef HAS_VULKAN
18#include <QVulkanWindow>
19#endif
20
13#include <fmt/format.h> 21#include <fmt/format.h>
22
23#include "common/assert.h"
14#include "common/microprofile.h" 24#include "common/microprofile.h"
15#include "common/scm_rev.h" 25#include "common/scm_rev.h"
16#include "core/core.h" 26#include "core/core.h"
17#include "core/frontend/framebuffer_layout.h" 27#include "core/frontend/framebuffer_layout.h"
28#include "core/frontend/scope_acquire_window_context.h"
18#include "core/settings.h" 29#include "core/settings.h"
19#include "input_common/keyboard.h" 30#include "input_common/keyboard.h"
20#include "input_common/main.h" 31#include "input_common/main.h"
@@ -114,19 +125,10 @@ private:
114 QOpenGLContext context; 125 QOpenGLContext context;
115}; 126};
116 127
117// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL 128class GWidgetInternal : public QWindow {
118// context.
119// The corresponding functionality is handled in EmuThread instead
120class GGLWidgetInternal : public QOpenGLWindow {
121public: 129public:
122 GGLWidgetInternal(GRenderWindow* parent, QOpenGLContext* shared_context) 130 GWidgetInternal(GRenderWindow* parent) : parent(parent) {}
123 : QOpenGLWindow(shared_context), parent(parent) {} 131 virtual ~GWidgetInternal() = default;
124
125 void paintEvent(QPaintEvent* ev) override {
126 if (do_painting) {
127 QPainter painter(this);
128 }
129 }
130 132
131 void resizeEvent(QResizeEvent* ev) override { 133 void resizeEvent(QResizeEvent* ev) override {
132 parent->OnClientAreaResized(ev->size().width(), ev->size().height()); 134 parent->OnClientAreaResized(ev->size().width(), ev->size().height());
@@ -182,11 +184,47 @@ public:
182 do_painting = true; 184 do_painting = true;
183 } 185 }
184 186
187 std::pair<unsigned, unsigned> GetSize() const {
188 return std::make_pair(width(), height());
189 }
190
191protected:
192 bool IsPaintingEnabled() const {
193 return do_painting;
194 }
195
185private: 196private:
186 GRenderWindow* parent; 197 GRenderWindow* parent;
187 bool do_painting; 198 bool do_painting = false;
199};
200
201// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL
202// context.
203// The corresponding functionality is handled in EmuThread instead
204class GGLWidgetInternal final : public GWidgetInternal, public QOpenGLWindow {
205public:
206 GGLWidgetInternal(GRenderWindow* parent, QOpenGLContext* shared_context)
207 : GWidgetInternal(parent), QOpenGLWindow(shared_context) {}
208 ~GGLWidgetInternal() override = default;
209
210 void paintEvent(QPaintEvent* ev) override {
211 if (IsPaintingEnabled()) {
212 QPainter painter(this);
213 }
214 }
188}; 215};
189 216
217#ifdef HAS_VULKAN
218class GVKWidgetInternal final : public GWidgetInternal {
219public:
220 GVKWidgetInternal(GRenderWindow* parent, QVulkanInstance* instance) : GWidgetInternal(parent) {
221 setSurfaceType(QSurface::SurfaceType::VulkanSurface);
222 setVulkanInstance(instance);
223 }
224 ~GVKWidgetInternal() override = default;
225};
226#endif
227
190GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread) 228GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread)
191 : QWidget(parent), emu_thread(emu_thread) { 229 : QWidget(parent), emu_thread(emu_thread) {
192 setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") 230 setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
@@ -201,9 +239,15 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread)
201 239
202GRenderWindow::~GRenderWindow() { 240GRenderWindow::~GRenderWindow() {
203 InputCommon::Shutdown(); 241 InputCommon::Shutdown();
242
243 // Avoid an unordered destruction that generates a segfault
244 delete child;
204} 245}
205 246
206void GRenderWindow::moveContext() { 247void GRenderWindow::moveContext() {
248 if (!context) {
249 return;
250 }
207 DoneCurrent(); 251 DoneCurrent();
208 252
209 // If the thread started running, move the GL Context to the new thread. Otherwise, move it 253 // If the thread started running, move the GL Context to the new thread. Otherwise, move it
@@ -215,8 +259,9 @@ void GRenderWindow::moveContext() {
215} 259}
216 260
217void GRenderWindow::SwapBuffers() { 261void GRenderWindow::SwapBuffers() {
218 context->swapBuffers(child); 262 if (context) {
219 263 context->swapBuffers(child);
264 }
220 if (!first_frame) { 265 if (!first_frame) {
221 first_frame = true; 266 first_frame = true;
222 emit FirstFrameDisplayed(); 267 emit FirstFrameDisplayed();
@@ -224,15 +269,38 @@ void GRenderWindow::SwapBuffers() {
224} 269}
225 270
226void GRenderWindow::MakeCurrent() { 271void GRenderWindow::MakeCurrent() {
227 context->makeCurrent(child); 272 if (context) {
273 context->makeCurrent(child);
274 }
228} 275}
229 276
230void GRenderWindow::DoneCurrent() { 277void GRenderWindow::DoneCurrent() {
231 context->doneCurrent(); 278 if (context) {
279 context->doneCurrent();
280 }
232} 281}
233 282
234void GRenderWindow::PollEvents() {} 283void GRenderWindow::PollEvents() {}
235 284
285bool GRenderWindow::IsShown() const {
286 return !isMinimized();
287}
288
289void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
290 void* surface) const {
291#ifdef HAS_VULKAN
292 const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr");
293 const VkInstance instance_copy = vk_instance->vkInstance();
294 const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child);
295
296 std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr));
297 std::memcpy(instance, &instance_copy, sizeof(instance_copy));
298 std::memcpy(surface, &surface_copy, sizeof(surface_copy));
299#else
300 UNREACHABLE_MSG("Executing Vulkan code without compiling Vulkan");
301#endif
302}
303
236// On Qt 5.0+, this correctly gets the size of the framebuffer (pixels). 304// On Qt 5.0+, this correctly gets the size of the framebuffer (pixels).
237// 305//
238// Older versions get the window size (density independent pixels), 306// Older versions get the window size (density independent pixels),
@@ -241,10 +309,9 @@ void GRenderWindow::PollEvents() {}
241void GRenderWindow::OnFramebufferSizeChanged() { 309void GRenderWindow::OnFramebufferSizeChanged() {
242 // Screen changes potentially incur a change in screen DPI, hence we should update the 310 // Screen changes potentially incur a change in screen DPI, hence we should update the
243 // framebuffer size 311 // framebuffer size
244 const qreal pixel_ratio = GetWindowPixelRatio(); 312 const qreal pixelRatio{GetWindowPixelRatio()};
245 const u32 width = child->QPaintDevice::width() * pixel_ratio; 313 const auto size{child->GetSize()};
246 const u32 height = child->QPaintDevice::height() * pixel_ratio; 314 UpdateCurrentFramebufferLayout(size.first * pixelRatio, size.second * pixelRatio);
247 UpdateCurrentFramebufferLayout(width, height);
248} 315}
249 316
250void GRenderWindow::ForwardKeyPressEvent(QKeyEvent* event) { 317void GRenderWindow::ForwardKeyPressEvent(QKeyEvent* event) {
@@ -290,7 +357,7 @@ qreal GRenderWindow::GetWindowPixelRatio() const {
290} 357}
291 358
292std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const { 359std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const {
293 const qreal pixel_ratio = GetWindowPixelRatio(); 360 const qreal pixel_ratio{GetWindowPixelRatio()};
294 return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), 361 return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})),
295 static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; 362 static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))};
296} 363}
@@ -356,50 +423,46 @@ std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedCont
356 return std::make_unique<GGLContext>(context.get()); 423 return std::make_unique<GGLContext>(context.get());
357} 424}
358 425
359void GRenderWindow::InitRenderTarget() { 426bool GRenderWindow::InitRenderTarget() {
360 shared_context.reset(); 427 shared_context.reset();
361 context.reset(); 428 context.reset();
362 429 if (child) {
363 delete child; 430 delete child;
364 child = nullptr; 431 }
365 432 if (container) {
366 delete container; 433 delete container;
367 container = nullptr; 434 }
368 435 if (layout()) {
369 delete layout(); 436 delete layout();
437 }
370 438
371 first_frame = false; 439 first_frame = false;
372 440
373 // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, 441 switch (Settings::values.renderer_backend) {
374 // WA_DontShowOnScreen, WA_DeleteOnClose 442 case Settings::RendererBackend::OpenGL:
375 QSurfaceFormat fmt; 443 if (!InitializeOpenGL()) {
376 fmt.setVersion(4, 3); 444 return false;
377 fmt.setProfile(QSurfaceFormat::CompatibilityProfile); 445 }
378 fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); 446 break;
379 // TODO: expose a setting for buffer value (ie default/single/double/triple) 447 case Settings::RendererBackend::Vulkan:
380 fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); 448 if (!InitializeVulkan()) {
381 shared_context = std::make_unique<QOpenGLContext>(); 449 return false;
382 shared_context->setFormat(fmt); 450 }
383 shared_context->create(); 451 break;
384 context = std::make_unique<QOpenGLContext>(); 452 }
385 context->setShareContext(shared_context.get());
386 context->setFormat(fmt);
387 context->create();
388 fmt.setSwapInterval(0);
389 453
390 child = new GGLWidgetInternal(this, shared_context.get());
391 container = QWidget::createWindowContainer(child, this); 454 container = QWidget::createWindowContainer(child, this);
392
393 QBoxLayout* layout = new QHBoxLayout(this); 455 QBoxLayout* layout = new QHBoxLayout(this);
456
394 layout->addWidget(container); 457 layout->addWidget(container);
395 layout->setMargin(0); 458 layout->setMargin(0);
396 setLayout(layout); 459 setLayout(layout);
397 460
398 // Reset minimum size to avoid unwanted resizes when this function is called for a second time. 461 // Reset minimum required size to avoid resizing issues on the main window after restarting.
399 setMinimumSize(1, 1); 462 setMinimumSize(1, 1);
400 463
401 // Show causes the window to actually be created and the OpenGL context as well, but we don't 464 // Show causes the window to actually be created and the gl context as well, but we don't want
402 // want the widget to be shown yet, so immediately hide it. 465 // the widget to be shown yet, so immediately hide it.
403 show(); 466 show();
404 hide(); 467 hide();
405 468
@@ -410,9 +473,17 @@ void GRenderWindow::InitRenderTarget() {
410 OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); 473 OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
411 474
412 OnFramebufferSizeChanged(); 475 OnFramebufferSizeChanged();
413 NotifyClientAreaSizeChanged(std::pair<unsigned, unsigned>(child->width(), child->height())); 476 NotifyClientAreaSizeChanged(child->GetSize());
414 477
415 BackupGeometry(); 478 BackupGeometry();
479
480 if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
481 if (!LoadOpenGL()) {
482 return false;
483 }
484 }
485
486 return true;
416} 487}
417 488
418void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { 489void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) {
@@ -441,6 +512,113 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal
441 setMinimumSize(minimal_size.first, minimal_size.second); 512 setMinimumSize(minimal_size.first, minimal_size.second);
442} 513}
443 514
515bool GRenderWindow::InitializeOpenGL() {
516 // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
517 // WA_DontShowOnScreen, WA_DeleteOnClose
518 QSurfaceFormat fmt;
519 fmt.setVersion(4, 3);
520 fmt.setProfile(QSurfaceFormat::CompatibilityProfile);
521 fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions);
522 // TODO: expose a setting for buffer value (ie default/single/double/triple)
523 fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
524 shared_context = std::make_unique<QOpenGLContext>();
525 shared_context->setFormat(fmt);
526 shared_context->create();
527 context = std::make_unique<QOpenGLContext>();
528 context->setShareContext(shared_context.get());
529 context->setFormat(fmt);
530 context->create();
531 fmt.setSwapInterval(false);
532
533 child = new GGLWidgetInternal(this, shared_context.get());
534 return true;
535}
536
537bool GRenderWindow::InitializeVulkan() {
538#ifdef HAS_VULKAN
539 vk_instance = std::make_unique<QVulkanInstance>();
540 vk_instance->setApiVersion(QVersionNumber(1, 1, 0));
541 vk_instance->setFlags(QVulkanInstance::Flag::NoDebugOutputRedirect);
542 if (Settings::values.renderer_debug) {
543 const auto supported_layers{vk_instance->supportedLayers()};
544 const bool found =
545 std::find_if(supported_layers.begin(), supported_layers.end(), [](const auto& layer) {
546 constexpr const char searched_layer[] = "VK_LAYER_LUNARG_standard_validation";
547 return layer.name == searched_layer;
548 });
549 if (found) {
550 vk_instance->setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation");
551 vk_instance->setExtensions(QByteArrayList() << VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
552 }
553 }
554 if (!vk_instance->create()) {
555 QMessageBox::critical(
556 this, tr("Error while initializing Vulkan 1.1!"),
557 tr("Your OS doesn't seem to support Vulkan 1.1 instances, or you do not have the "
558 "latest graphics drivers."));
559 return false;
560 }
561
562 child = new GVKWidgetInternal(this, vk_instance.get());
563 return true;
564#else
565 QMessageBox::critical(this, tr("Vulkan not available!"),
566 tr("yuzu has not been compiled with Vulkan support."));
567 return false;
568#endif
569}
570
571bool GRenderWindow::LoadOpenGL() {
572 Core::Frontend::ScopeAcquireWindowContext acquire_context{*this};
573 if (!gladLoadGL()) {
574 QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3!"),
575 tr("Your GPU may not support OpenGL 4.3, or you do not have the "
576 "latest graphics driver."));
577 return false;
578 }
579
580 QStringList unsupported_gl_extensions = GetUnsupportedGLExtensions();
581 if (!unsupported_gl_extensions.empty()) {
582 QMessageBox::critical(
583 this, tr("Error while initializing OpenGL!"),
584 tr("Your GPU may not support one or more required OpenGL extensions. Please ensure you "
585 "have the latest graphics driver.<br><br>Unsupported extensions:<br>") +
586 unsupported_gl_extensions.join(QStringLiteral("<br>")));
587 return false;
588 }
589 return true;
590}
591
592QStringList GRenderWindow::GetUnsupportedGLExtensions() const {
593 QStringList unsupported_ext;
594
595 if (!GLAD_GL_ARB_buffer_storage)
596 unsupported_ext.append(QStringLiteral("ARB_buffer_storage"));
597 if (!GLAD_GL_ARB_direct_state_access)
598 unsupported_ext.append(QStringLiteral("ARB_direct_state_access"));
599 if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev)
600 unsupported_ext.append(QStringLiteral("ARB_vertex_type_10f_11f_11f_rev"));
601 if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge)
602 unsupported_ext.append(QStringLiteral("ARB_texture_mirror_clamp_to_edge"));
603 if (!GLAD_GL_ARB_multi_bind)
604 unsupported_ext.append(QStringLiteral("ARB_multi_bind"));
605 if (!GLAD_GL_ARB_clip_control)
606 unsupported_ext.append(QStringLiteral("ARB_clip_control"));
607
608 // Extensions required to support some texture formats.
609 if (!GLAD_GL_EXT_texture_compression_s3tc)
610 unsupported_ext.append(QStringLiteral("EXT_texture_compression_s3tc"));
611 if (!GLAD_GL_ARB_texture_compression_rgtc)
612 unsupported_ext.append(QStringLiteral("ARB_texture_compression_rgtc"));
613 if (!GLAD_GL_ARB_depth_buffer_float)
614 unsupported_ext.append(QStringLiteral("ARB_depth_buffer_float"));
615
616 for (const QString& ext : unsupported_ext)
617 LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString());
618
619 return unsupported_ext;
620}
621
444void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { 622void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) {
445 this->emu_thread = emu_thread; 623 this->emu_thread = emu_thread;
446 child->DisablePainting(); 624 child->DisablePainting();
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 2fc64895f..71a2fa321 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -7,17 +7,28 @@
7#include <atomic> 7#include <atomic>
8#include <condition_variable> 8#include <condition_variable>
9#include <mutex> 9#include <mutex>
10
10#include <QImage> 11#include <QImage>
11#include <QThread> 12#include <QThread>
12#include <QWidget> 13#include <QWidget>
14
15#include "common/thread.h"
13#include "core/core.h" 16#include "core/core.h"
14#include "core/frontend/emu_window.h" 17#include "core/frontend/emu_window.h"
15 18
16class QKeyEvent; 19class QKeyEvent;
17class QScreen; 20class QScreen;
18class QTouchEvent; 21class QTouchEvent;
22class QStringList;
23class QSurface;
24class QOpenGLContext;
25#ifdef HAS_VULKAN
26class QVulkanInstance;
27#endif
19 28
29class GWidgetInternal;
20class GGLWidgetInternal; 30class GGLWidgetInternal;
31class GVKWidgetInternal;
21class GMainWindow; 32class GMainWindow;
22class GRenderWindow; 33class GRenderWindow;
23class QSurface; 34class QSurface;
@@ -123,6 +134,9 @@ public:
123 void MakeCurrent() override; 134 void MakeCurrent() override;
124 void DoneCurrent() override; 135 void DoneCurrent() override;
125 void PollEvents() override; 136 void PollEvents() override;
137 bool IsShown() const override;
138 void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
139 void* surface) const override;
126 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; 140 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
127 141
128 void ForwardKeyPressEvent(QKeyEvent* event); 142 void ForwardKeyPressEvent(QKeyEvent* event);
@@ -142,7 +156,7 @@ public:
142 156
143 void OnClientAreaResized(u32 width, u32 height); 157 void OnClientAreaResized(u32 width, u32 height);
144 158
145 void InitRenderTarget(); 159 bool InitRenderTarget();
146 160
147 void CaptureScreenshot(u32 res_scale, const QString& screenshot_path); 161 void CaptureScreenshot(u32 res_scale, const QString& screenshot_path);
148 162
@@ -165,10 +179,13 @@ private:
165 179
166 void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override; 180 void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
167 181
168 QWidget* container = nullptr; 182 bool InitializeOpenGL();
169 GGLWidgetInternal* child = nullptr; 183 bool InitializeVulkan();
184 bool LoadOpenGL();
185 QStringList GetUnsupportedGLExtensions() const;
170 186
171 QByteArray geometry; 187 QWidget* container = nullptr;
188 GWidgetInternal* child = nullptr;
172 189
173 EmuThread* emu_thread; 190 EmuThread* emu_thread;
174 // Context that backs the GGLWidgetInternal (and will be used by core to render) 191 // Context that backs the GGLWidgetInternal (and will be used by core to render)
@@ -177,9 +194,14 @@ private:
177 // current 194 // current
178 std::unique_ptr<QOpenGLContext> shared_context; 195 std::unique_ptr<QOpenGLContext> shared_context;
179 196
197#ifdef HAS_VULKAN
198 std::unique_ptr<QVulkanInstance> vk_instance;
199#endif
200
180 /// Temporary storage of the screenshot taken 201 /// Temporary storage of the screenshot taken
181 QImage screenshot_image; 202 QImage screenshot_image;
182 203
204 QByteArray geometry;
183 bool first_frame = false; 205 bool first_frame = false;
184 206
185protected: 207protected:
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index c4a07935a..cd94693c1 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -624,6 +624,10 @@ void Config::ReadPathValues() {
624void Config::ReadRendererValues() { 624void Config::ReadRendererValues() {
625 qt_config->beginGroup(QStringLiteral("Renderer")); 625 qt_config->beginGroup(QStringLiteral("Renderer"));
626 626
627 Settings::values.renderer_backend =
628 static_cast<Settings::RendererBackend>(ReadSetting(QStringLiteral("backend"), 0).toInt());
629 Settings::values.renderer_debug = ReadSetting(QStringLiteral("debug"), false).toBool();
630 Settings::values.vulkan_device = ReadSetting(QStringLiteral("vulkan_device"), 0).toInt();
627 Settings::values.resolution_factor = 631 Settings::values.resolution_factor =
628 ReadSetting(QStringLiteral("resolution_factor"), 1.0).toFloat(); 632 ReadSetting(QStringLiteral("resolution_factor"), 1.0).toFloat();
629 Settings::values.use_frame_limit = 633 Settings::values.use_frame_limit =
@@ -1055,6 +1059,9 @@ void Config::SavePathValues() {
1055void Config::SaveRendererValues() { 1059void Config::SaveRendererValues() {
1056 qt_config->beginGroup(QStringLiteral("Renderer")); 1060 qt_config->beginGroup(QStringLiteral("Renderer"));
1057 1061
1062 WriteSetting(QStringLiteral("backend"), static_cast<int>(Settings::values.renderer_backend), 0);
1063 WriteSetting(QStringLiteral("debug"), Settings::values.renderer_debug, false);
1064 WriteSetting(QStringLiteral("vulkan_device"), Settings::values.vulkan_device, 0);
1058 WriteSetting(QStringLiteral("resolution_factor"), 1065 WriteSetting(QStringLiteral("resolution_factor"),
1059 static_cast<double>(Settings::values.resolution_factor), 1.0); 1066 static_cast<double>(Settings::values.resolution_factor), 1.0);
1060 WriteSetting(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true); 1067 WriteSetting(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true);
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index 90c1f9459..9631059c7 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -36,6 +36,8 @@ void ConfigureDebug::SetConfiguration() {
36 ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); 36 ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args));
37 ui->reporting_services->setChecked(Settings::values.reporting_services); 37 ui->reporting_services->setChecked(Settings::values.reporting_services);
38 ui->quest_flag->setChecked(Settings::values.quest_flag); 38 ui->quest_flag->setChecked(Settings::values.quest_flag);
39 ui->enable_graphics_debugging->setEnabled(!Core::System::GetInstance().IsPoweredOn());
40 ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug);
39} 41}
40 42
41void ConfigureDebug::ApplyConfiguration() { 43void ConfigureDebug::ApplyConfiguration() {
@@ -46,6 +48,7 @@ void ConfigureDebug::ApplyConfiguration() {
46 Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); 48 Settings::values.program_args = ui->homebrew_args_edit->text().toStdString();
47 Settings::values.reporting_services = ui->reporting_services->isChecked(); 49 Settings::values.reporting_services = ui->reporting_services->isChecked();
48 Settings::values.quest_flag = ui->quest_flag->isChecked(); 50 Settings::values.quest_flag = ui->quest_flag->isChecked();
51 Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked();
49 Debugger::ToggleConsole(); 52 Debugger::ToggleConsole();
50 Log::Filter filter; 53 Log::Filter filter;
51 filter.ParseFilterString(Settings::values.log_filter); 54 filter.ParseFilterString(Settings::values.log_filter);
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index ce49569bb..e028c4c80 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -7,7 +7,7 @@
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>400</width> 9 <width>400</width>
10 <height>474</height> 10 <height>467</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -103,6 +103,80 @@
103 </item> 103 </item>
104 </layout> 104 </layout>
105 </item> 105 </item>
106 </layout>
107 </widget>
108 </item>
109 <item>
110 <widget class="QGroupBox" name="groupBox_3">
111 <property name="title">
112 <string>Homebrew</string>
113 </property>
114 <layout class="QVBoxLayout" name="verticalLayout_5">
115 <item>
116 <layout class="QHBoxLayout" name="horizontalLayout_4">
117 <item>
118 <widget class="QLabel" name="label_3">
119 <property name="text">
120 <string>Arguments String</string>
121 </property>
122 </widget>
123 </item>
124 <item>
125 <widget class="QLineEdit" name="homebrew_args_edit"/>
126 </item>
127 </layout>
128 </item>
129 </layout>
130 </widget>
131 </item>
132 <item>
133 <widget class="QGroupBox" name="groupBox_4">
134 <property name="title">
135 <string>Graphics</string>
136 </property>
137 <layout class="QVBoxLayout" name="verticalLayout_6">
138 <item>
139 <widget class="QCheckBox" name="enable_graphics_debugging">
140 <property name="enabled">
141 <bool>true</bool>
142 </property>
143 <property name="whatsThis">
144 <string>When checked, the graphics API enters in a slower debugging mode</string>
145 </property>
146 <property name="text">
147 <string>Enable Graphics Debugging</string>
148 </property>
149 </widget>
150 </item>
151 </layout>
152 </widget>
153 </item>
154 <item>
155 <widget class="QGroupBox" name="groupBox_5">
156 <property name="title">
157 <string>Dump</string>
158 </property>
159 <layout class="QVBoxLayout" name="verticalLayout_6">
160 <item>
161 <widget class="QCheckBox" name="dump_decompressed_nso">
162 <property name="whatsThis">
163 <string>When checked, any NSO yuzu tries to load or patch will be copied decompressed to the yuzu/dump directory.</string>
164 </property>
165 <property name="text">
166 <string>Dump Decompressed NSOs</string>
167 </property>
168 </widget>
169 </item>
170 <item>
171 <widget class="QCheckBox" name="dump_exefs">
172 <property name="whatsThis">
173 <string>When checked, any game that yuzu loads will have its ExeFS dumped to the yuzu/dump directory.</string>
174 </property>
175 <property name="text">
176 <string>Dump ExeFS</string>
177 </property>
178 </widget>
179 </item>
106 <item> 180 <item>
107 <widget class="QCheckBox" name="reporting_services"> 181 <widget class="QCheckBox" name="reporting_services">
108 <property name="text"> 182 <property name="text">
@@ -129,11 +203,11 @@
129 </widget> 203 </widget>
130 </item> 204 </item>
131 <item> 205 <item>
132 <widget class="QGroupBox" name="groupBox_5"> 206 <widget class="QGroupBox" name="groupBox_6">
133 <property name="title"> 207 <property name="title">
134 <string>Advanced</string> 208 <string>Advanced</string>
135 </property> 209 </property>
136 <layout class="QVBoxLayout" name="verticalLayout"> 210 <layout class="QVBoxLayout" name="verticalLayout_7">
137 <item> 211 <item>
138 <widget class="QCheckBox" name="quest_flag"> 212 <widget class="QCheckBox" name="quest_flag">
139 <property name="text"> 213 <property name="text">
@@ -145,29 +219,6 @@
145 </widget> 219 </widget>
146 </item> 220 </item>
147 <item> 221 <item>
148 <widget class="QGroupBox" name="groupBox_3">
149 <property name="title">
150 <string>Homebrew</string>
151 </property>
152 <layout class="QVBoxLayout" name="verticalLayout_5">
153 <item>
154 <layout class="QHBoxLayout" name="horizontalLayout_4">
155 <item>
156 <widget class="QLabel" name="label_3">
157 <property name="text">
158 <string>Arguments String</string>
159 </property>
160 </widget>
161 </item>
162 <item>
163 <widget class="QLineEdit" name="homebrew_args_edit"/>
164 </item>
165 </layout>
166 </item>
167 </layout>
168 </widget>
169 </item>
170 <item>
171 <spacer name="verticalSpacer"> 222 <spacer name="verticalSpacer">
172 <property name="orientation"> 223 <property name="orientation">
173 <enum>Qt::Vertical</enum> 224 <enum>Qt::Vertical</enum>
@@ -185,6 +236,19 @@
185 </item> 236 </item>
186 </layout> 237 </layout>
187 </widget> 238 </widget>
239 <tabstops>
240 <tabstop>toggle_gdbstub</tabstop>
241 <tabstop>gdbport_spinbox</tabstop>
242 <tabstop>log_filter_edit</tabstop>
243 <tabstop>toggle_console</tabstop>
244 <tabstop>open_log_button</tabstop>
245 <tabstop>homebrew_args_edit</tabstop>
246 <tabstop>enable_graphics_debugging</tabstop>
247 <tabstop>dump_decompressed_nso</tabstop>
248 <tabstop>dump_exefs</tabstop>
249 <tabstop>reporting_services</tabstop>
250 <tabstop>quest_flag</tabstop>
251 </tabstops>
188 <resources/> 252 <resources/>
189 <connections> 253 <connections>
190 <connection> 254 <connection>
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 2c9e322c9..f57a24e36 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -3,6 +3,13 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <QColorDialog> 5#include <QColorDialog>
6#include <QComboBox>
7#ifdef HAS_VULKAN
8#include <QVulkanInstance>
9#endif
10
11#include "common/common_types.h"
12#include "common/logging/log.h"
6#include "core/core.h" 13#include "core/core.h"
7#include "core/settings.h" 14#include "core/settings.h"
8#include "ui_configure_graphics.h" 15#include "ui_configure_graphics.h"
@@ -51,10 +58,18 @@ Resolution FromResolutionFactor(float factor) {
51 58
52ConfigureGraphics::ConfigureGraphics(QWidget* parent) 59ConfigureGraphics::ConfigureGraphics(QWidget* parent)
53 : QWidget(parent), ui(new Ui::ConfigureGraphics) { 60 : QWidget(parent), ui(new Ui::ConfigureGraphics) {
61 vulkan_device = Settings::values.vulkan_device;
62 RetrieveVulkanDevices();
63
54 ui->setupUi(this); 64 ui->setupUi(this);
55 65
56 SetConfiguration(); 66 SetConfiguration();
57 67
68 connect(ui->api, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
69 [this] { UpdateDeviceComboBox(); });
70 connect(ui->device, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this,
71 [this](int device) { UpdateDeviceSelection(device); });
72
58 connect(ui->bg_button, &QPushButton::clicked, this, [this] { 73 connect(ui->bg_button, &QPushButton::clicked, this, [this] {
59 const QColor new_bg_color = QColorDialog::getColor(bg_color); 74 const QColor new_bg_color = QColorDialog::getColor(bg_color);
60 if (!new_bg_color.isValid()) { 75 if (!new_bg_color.isValid()) {
@@ -64,11 +79,22 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
64 }); 79 });
65} 80}
66 81
82void ConfigureGraphics::UpdateDeviceSelection(int device) {
83 if (device == -1) {
84 return;
85 }
86 if (GetCurrentGraphicsBackend() == Settings::RendererBackend::Vulkan) {
87 vulkan_device = device;
88 }
89}
90
67ConfigureGraphics::~ConfigureGraphics() = default; 91ConfigureGraphics::~ConfigureGraphics() = default;
68 92
69void ConfigureGraphics::SetConfiguration() { 93void ConfigureGraphics::SetConfiguration() {
70 const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); 94 const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
71 95
96 ui->api->setEnabled(runtime_lock);
97 ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend));
72 ui->resolution_factor_combobox->setCurrentIndex( 98 ui->resolution_factor_combobox->setCurrentIndex(
73 static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor))); 99 static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor)));
74 ui->use_disk_shader_cache->setEnabled(runtime_lock); 100 ui->use_disk_shader_cache->setEnabled(runtime_lock);
@@ -80,9 +106,12 @@ void ConfigureGraphics::SetConfiguration() {
80 ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode); 106 ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode);
81 UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, 107 UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green,
82 Settings::values.bg_blue)); 108 Settings::values.bg_blue));
109 UpdateDeviceComboBox();
83} 110}
84 111
85void ConfigureGraphics::ApplyConfiguration() { 112void ConfigureGraphics::ApplyConfiguration() {
113 Settings::values.renderer_backend = GetCurrentGraphicsBackend();
114 Settings::values.vulkan_device = vulkan_device;
86 Settings::values.resolution_factor = 115 Settings::values.resolution_factor =
87 ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex())); 116 ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex()));
88 Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked(); 117 Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked();
@@ -116,3 +145,68 @@ void ConfigureGraphics::UpdateBackgroundColorButton(QColor color) {
116 const QIcon color_icon(pixmap); 145 const QIcon color_icon(pixmap);
117 ui->bg_button->setIcon(color_icon); 146 ui->bg_button->setIcon(color_icon);
118} 147}
148
149void ConfigureGraphics::UpdateDeviceComboBox() {
150 ui->device->clear();
151
152 bool enabled = false;
153 switch (GetCurrentGraphicsBackend()) {
154 case Settings::RendererBackend::OpenGL:
155 ui->device->addItem(tr("OpenGL Graphics Device"));
156 enabled = false;
157 break;
158 case Settings::RendererBackend::Vulkan:
159 for (const auto device : vulkan_devices) {
160 ui->device->addItem(device);
161 }
162 ui->device->setCurrentIndex(vulkan_device);
163 enabled = !vulkan_devices.empty();
164 break;
165 }
166 ui->device->setEnabled(enabled && !Core::System::GetInstance().IsPoweredOn());
167}
168
169void ConfigureGraphics::RetrieveVulkanDevices() {
170#ifdef HAS_VULKAN
171 QVulkanInstance instance;
172 instance.setApiVersion(QVersionNumber(1, 1, 0));
173 if (!instance.create()) {
174 LOG_INFO(Frontend, "Vulkan 1.1 not available");
175 return;
176 }
177 const auto vkEnumeratePhysicalDevices{reinterpret_cast<PFN_vkEnumeratePhysicalDevices>(
178 instance.getInstanceProcAddr("vkEnumeratePhysicalDevices"))};
179 if (vkEnumeratePhysicalDevices == nullptr) {
180 LOG_INFO(Frontend, "Failed to get pointer to vkEnumeratePhysicalDevices");
181 return;
182 }
183 u32 physical_device_count;
184 if (vkEnumeratePhysicalDevices(instance.vkInstance(), &physical_device_count, nullptr) !=
185 VK_SUCCESS) {
186 LOG_INFO(Frontend, "Failed to get physical devices count");
187 return;
188 }
189 std::vector<VkPhysicalDevice> physical_devices(physical_device_count);
190 if (vkEnumeratePhysicalDevices(instance.vkInstance(), &physical_device_count,
191 physical_devices.data()) != VK_SUCCESS) {
192 LOG_INFO(Frontend, "Failed to get physical devices");
193 return;
194 }
195
196 const auto vkGetPhysicalDeviceProperties{reinterpret_cast<PFN_vkGetPhysicalDeviceProperties>(
197 instance.getInstanceProcAddr("vkGetPhysicalDeviceProperties"))};
198 if (vkGetPhysicalDeviceProperties == nullptr) {
199 LOG_INFO(Frontend, "Failed to get pointer to vkGetPhysicalDeviceProperties");
200 return;
201 }
202 for (const auto physical_device : physical_devices) {
203 VkPhysicalDeviceProperties properties;
204 vkGetPhysicalDeviceProperties(physical_device, &properties);
205 vulkan_devices.push_back(QString::fromUtf8(properties.deviceName));
206 }
207#endif
208}
209
210Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
211 return static_cast<Settings::RendererBackend>(ui->api->currentIndex());
212}
diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h
index fae28d98e..7e0596d9c 100644
--- a/src/yuzu/configuration/configure_graphics.h
+++ b/src/yuzu/configuration/configure_graphics.h
@@ -5,7 +5,10 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <vector>
9#include <QString>
8#include <QWidget> 10#include <QWidget>
11#include "core/settings.h"
9 12
10namespace Ui { 13namespace Ui {
11class ConfigureGraphics; 14class ConfigureGraphics;
@@ -27,7 +30,16 @@ private:
27 void SetConfiguration(); 30 void SetConfiguration();
28 31
29 void UpdateBackgroundColorButton(QColor color); 32 void UpdateBackgroundColorButton(QColor color);
33 void UpdateDeviceComboBox();
34 void UpdateDeviceSelection(int device);
35
36 void RetrieveVulkanDevices();
37
38 Settings::RendererBackend GetCurrentGraphicsBackend() const;
30 39
31 std::unique_ptr<Ui::ConfigureGraphics> ui; 40 std::unique_ptr<Ui::ConfigureGraphics> ui;
32 QColor bg_color; 41 QColor bg_color;
42
43 std::vector<QString> vulkan_devices;
44 u32 vulkan_device{};
33}; 45};
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index 0309ee300..e24372204 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -7,21 +7,69 @@
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>400</width> 9 <width>400</width>
10 <height>300</height> 10 <height>321</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
14 <string>Form</string> 14 <string>Form</string>
15 </property> 15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout"> 16 <layout class="QVBoxLayout" name="verticalLayout_1">
17 <item> 17 <item>
18 <layout class="QVBoxLayout" name="verticalLayout_3"> 18 <layout class="QVBoxLayout" name="verticalLayout_2">
19 <item>
20 <widget class="QGroupBox" name="groupBox_2">
21 <property name="title">
22 <string>API Settings</string>
23 </property>
24 <layout class="QVBoxLayout" name="verticalLayout_3">
25 <item>
26 <layout class="QHBoxLayout" name="horizontalLayout_4">
27 <item>
28 <widget class="QLabel" name="label_2">
29 <property name="text">
30 <string>API:</string>
31 </property>
32 </widget>
33 </item>
34 <item>
35 <widget class="QComboBox" name="api">
36 <item>
37 <property name="text">
38 <string notr="true">OpenGL</string>
39 </property>
40 </item>
41 <item>
42 <property name="text">
43 <string notr="true">Vulkan</string>
44 </property>
45 </item>
46 </widget>
47 </item>
48 </layout>
49 </item>
50 <item>
51 <layout class="QHBoxLayout" name="horizontalLayout_5">
52 <item>
53 <widget class="QLabel" name="label_3">
54 <property name="text">
55 <string>Device:</string>
56 </property>
57 </widget>
58 </item>
59 <item>
60 <widget class="QComboBox" name="device"/>
61 </item>
62 </layout>
63 </item>
64 </layout>
65 </widget>
66 </item>
19 <item> 67 <item>
20 <widget class="QGroupBox" name="groupBox"> 68 <widget class="QGroupBox" name="groupBox">
21 <property name="title"> 69 <property name="title">
22 <string>Graphics</string> 70 <string>Graphics Settings</string>
23 </property> 71 </property>
24 <layout class="QVBoxLayout" name="verticalLayout_2"> 72 <layout class="QVBoxLayout" name="verticalLayout_4">
25 <item> 73 <item>
26 <widget class="QCheckBox" name="use_disk_shader_cache"> 74 <widget class="QCheckBox" name="use_disk_shader_cache">
27 <property name="text"> 75 <property name="text">
@@ -30,16 +78,16 @@
30 </widget> 78 </widget>
31 </item> 79 </item>
32 <item> 80 <item>
33 <widget class="QCheckBox" name="use_accurate_gpu_emulation"> 81 <widget class="QCheckBox" name="use_asynchronous_gpu_emulation">
34 <property name="text"> 82 <property name="text">
35 <string>Use accurate GPU emulation (slow)</string> 83 <string>Use asynchronous GPU emulation</string>
36 </property> 84 </property>
37 </widget> 85 </widget>
38 </item> 86 </item>
39 <item> 87 <item>
40 <widget class="QCheckBox" name="use_asynchronous_gpu_emulation"> 88 <widget class="QCheckBox" name="use_accurate_gpu_emulation">
41 <property name="text"> 89 <property name="text">
42 <string>Use asynchronous GPU emulation</string> 90 <string>Use accurate GPU emulation (slow)</string>
43 </property> 91 </property>
44 </widget> 92 </widget>
45 </item> 93 </item>
@@ -51,11 +99,11 @@
51 </widget> 99 </widget>
52 </item> 100 </item>
53 <item> 101 <item>
54 <layout class="QHBoxLayout" name="horizontalLayout"> 102 <layout class="QHBoxLayout" name="horizontalLayout_2">
55 <item> 103 <item>
56 <widget class="QLabel" name="label"> 104 <widget class="QLabel" name="label">
57 <property name="text"> 105 <property name="text">
58 <string>Internal Resolution</string> 106 <string>Internal Resolution:</string>
59 </property> 107 </property>
60 </widget> 108 </widget>
61 </item> 109 </item>
@@ -91,7 +139,7 @@
91 </layout> 139 </layout>
92 </item> 140 </item>
93 <item> 141 <item>
94 <layout class="QHBoxLayout" name="horizontalLayout_6"> 142 <layout class="QHBoxLayout" name="horizontalLayout_3">
95 <item> 143 <item>
96 <widget class="QLabel" name="bg_label"> 144 <widget class="QLabel" name="bg_label">
97 <property name="text"> 145 <property name="text">
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 67c9a7c6d..96dec50e2 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -236,6 +236,8 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
236 widget->setVisible(false); 236 widget->setVisible(false);
237 237
238 analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog}; 238 analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog};
239 analog_map_deadzone = {ui->sliderLStickDeadzone, ui->sliderRStickDeadzone};
240 analog_map_deadzone_label = {ui->labelLStickDeadzone, ui->labelRStickDeadzone};
239 241
240 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { 242 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
241 auto* const button = button_map[button_id]; 243 auto* const button = button_map[button_id];
@@ -326,6 +328,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
326 InputCommon::Polling::DeviceType::Analog); 328 InputCommon::Polling::DeviceType::Analog);
327 } 329 }
328 }); 330 });
331 connect(analog_map_deadzone[analog_id], &QSlider::valueChanged, [=] {
332 const float deadzone = analog_map_deadzone[analog_id]->value() / 100.0f;
333 analog_map_deadzone_label[analog_id]->setText(tr("Deadzone: %1").arg(deadzone));
334 analogs_param[analog_id].Set("deadzone", deadzone);
335 });
329 } 336 }
330 337
331 connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); }); 338 connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); });
@@ -484,7 +491,7 @@ void ConfigureInputPlayer::ClearAll() {
484 continue; 491 continue;
485 } 492 }
486 493
487 analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]); 494 analogs_param[analog_id].Clear();
488 } 495 }
489 } 496 }
490 497
@@ -508,6 +515,23 @@ void ConfigureInputPlayer::UpdateButtonLabels() {
508 AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id])); 515 AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
509 } 516 }
510 analog_map_stick[analog_id]->setText(tr("Set Analog Stick")); 517 analog_map_stick[analog_id]->setText(tr("Set Analog Stick"));
518
519 auto& param = analogs_param[analog_id];
520 auto* const analog_deadzone_slider = analog_map_deadzone[analog_id];
521 auto* const analog_deadzone_label = analog_map_deadzone_label[analog_id];
522
523 if (param.Has("engine") && param.Get("engine", "") == "sdl") {
524 if (!param.Has("deadzone")) {
525 param.Set("deadzone", 0.1f);
526 }
527
528 analog_deadzone_slider->setValue(static_cast<int>(param.Get("deadzone", 0.1f) * 100));
529 analog_deadzone_slider->setVisible(true);
530 analog_deadzone_label->setVisible(true);
531 } else {
532 analog_deadzone_slider->setVisible(false);
533 analog_deadzone_label->setVisible(false);
534 }
511 } 535 }
512} 536}
513 537
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
index c66027651..045704e47 100644
--- a/src/yuzu/configuration/configure_input_player.h
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -97,6 +97,8 @@ private:
97 /// Analog inputs are also represented each with a single button, used to configure with an 97 /// Analog inputs are also represented each with a single button, used to configure with an
98 /// actual analog stick 98 /// actual analog stick
99 std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick; 99 std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick;
100 std::array<QSlider*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone;
101 std::array<QLabel*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone_label;
100 102
101 static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons; 103 static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
102 104
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui
index 42db020be..1556481d0 100644
--- a/src/yuzu/configuration/configure_input_player.ui
+++ b/src/yuzu/configuration/configure_input_player.ui
@@ -170,6 +170,44 @@
170 </item> 170 </item>
171 </layout> 171 </layout>
172 </item> 172 </item>
173 <item row="4" column="0" colspan="2">
174 <layout class="QVBoxLayout" name="sliderRStickDeadzoneVerticalLayout">
175 <item>
176 <layout class="QHBoxLayout" name="sliderRStickDeadzoneHorizontalLayout">
177 <item>
178 <widget class="QLabel" name="labelRStickDeadzone">
179 <property name="text">
180 <string>Deadzone: 0</string>
181 </property>
182 <property name="alignment">
183 <enum>Qt::AlignHCenter</enum>
184 </property>
185 </widget>
186 </item>
187 </layout>
188 </item>
189 <item>
190 <widget class="QSlider" name="sliderRStickDeadzone">
191 <property name="orientation">
192 <enum>Qt::Horizontal</enum>
193 </property>
194 </widget>
195 </item>
196 </layout>
197 </item>
198 <item row="5" column="0">
199 <spacer name="RStick_verticalSpacer">
200 <property name="orientation">
201 <enum>Qt::Vertical</enum>
202 </property>
203 <property name="sizeHint" stdset="0">
204 <size>
205 <width>0</width>
206 <height>0</height>
207 </size>
208 </property>
209 </spacer>
210 </item>
173 </layout> 211 </layout>
174 </widget> 212 </widget>
175 </item> 213 </item>
@@ -745,6 +783,47 @@
745 </item> 783 </item>
746 </layout> 784 </layout>
747 </item> 785 </item>
786 <item row="5" column="1" colspan="2">
787 <layout class="QVBoxLayout" name="sliderLStickDeadzoneVerticalLayout">
788 <property name="sizeConstraint">
789 <enum>QLayout::SetDefaultConstraint</enum>
790 </property>
791 <item>
792 <layout class="QHBoxLayout" name="sliderLStickDeadzoneHorizontalLayout">
793 <item>
794 <widget class="QLabel" name="labelLStickDeadzone">
795 <property name="text">
796 <string>Deadzone: 0</string>
797 </property>
798 <property name="alignment">
799 <enum>Qt::AlignHCenter</enum>
800 </property>
801 </widget>
802 </item>
803 </layout>
804 </item>
805 <item>
806 <widget class="QSlider" name="sliderLStickDeadzone">
807 <property name="orientation">
808 <enum>Qt::Horizontal</enum>
809 </property>
810 </widget>
811 </item>
812 </layout>
813 </item>
814 <item row="6" column="1">
815 <spacer name="LStick_verticalSpacer">
816 <property name="orientation">
817 <enum>Qt::Vertical</enum>
818 </property>
819 <property name="sizeHint" stdset="0">
820 <size>
821 <width>0</width>
822 <height>0</height>
823 </size>
824 </property>
825 </spacer>
826 </item>
748 </layout> 827 </layout>
749 </widget> 828 </widget>
750 </item> 829 </item>
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index b5dd3e0d6..54ca2dc1d 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -454,7 +454,6 @@ void GMainWindow::InitializeWidgets() {
454 // Create status bar 454 // Create status bar
455 message_label = new QLabel(); 455 message_label = new QLabel();
456 // Configured separately for left alignment 456 // Configured separately for left alignment
457 message_label->setVisible(false);
458 message_label->setFrameStyle(QFrame::NoFrame); 457 message_label->setFrameStyle(QFrame::NoFrame);
459 message_label->setContentsMargins(4, 0, 4, 0); 458 message_label->setContentsMargins(4, 0, 4, 0);
460 message_label->setAlignment(Qt::AlignLeft); 459 message_label->setAlignment(Qt::AlignLeft);
@@ -476,8 +475,73 @@ void GMainWindow::InitializeWidgets() {
476 label->setVisible(false); 475 label->setVisible(false);
477 label->setFrameStyle(QFrame::NoFrame); 476 label->setFrameStyle(QFrame::NoFrame);
478 label->setContentsMargins(4, 0, 4, 0); 477 label->setContentsMargins(4, 0, 4, 0);
479 statusBar()->addPermanentWidget(label, 0); 478 statusBar()->addPermanentWidget(label);
480 } 479 }
480
481 // Setup Dock button
482 dock_status_button = new QPushButton();
483 dock_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton"));
484 dock_status_button->setFocusPolicy(Qt::NoFocus);
485 connect(dock_status_button, &QPushButton::clicked, [&] {
486 Settings::values.use_docked_mode = !Settings::values.use_docked_mode;
487 dock_status_button->setChecked(Settings::values.use_docked_mode);
488 OnDockedModeChanged(!Settings::values.use_docked_mode, Settings::values.use_docked_mode);
489 });
490 dock_status_button->setText(tr("DOCK"));
491 dock_status_button->setCheckable(true);
492 dock_status_button->setChecked(Settings::values.use_docked_mode);
493 statusBar()->insertPermanentWidget(0, dock_status_button);
494
495 // Setup ASync button
496 async_status_button = new QPushButton();
497 async_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton"));
498 async_status_button->setFocusPolicy(Qt::NoFocus);
499 connect(async_status_button, &QPushButton::clicked, [&] {
500 if (emulation_running) {
501 return;
502 }
503 Settings::values.use_asynchronous_gpu_emulation =
504 !Settings::values.use_asynchronous_gpu_emulation;
505 async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation);
506 Settings::Apply();
507 });
508 async_status_button->setText(tr("ASYNC"));
509 async_status_button->setCheckable(true);
510 async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation);
511 statusBar()->insertPermanentWidget(0, async_status_button);
512
513 // Setup Renderer API button
514 renderer_status_button = new QPushButton();
515 renderer_status_button->setObjectName(QStringLiteral("RendererStatusBarButton"));
516 renderer_status_button->setCheckable(true);
517 renderer_status_button->setFocusPolicy(Qt::NoFocus);
518 connect(renderer_status_button, &QPushButton::toggled, [=](bool checked) {
519 renderer_status_button->setText(checked ? tr("VULKAN") : tr("OPENGL"));
520 });
521 renderer_status_button->toggle();
522
523#ifndef HAS_VULKAN
524 renderer_status_button->setChecked(false);
525 renderer_status_button->setCheckable(false);
526 renderer_status_button->setDisabled(true);
527#else
528 renderer_status_button->setChecked(Settings::values.renderer_backend ==
529 Settings::RendererBackend::Vulkan);
530 connect(renderer_status_button, &QPushButton::clicked, [=] {
531 if (emulation_running) {
532 return;
533 }
534 if (renderer_status_button->isChecked()) {
535 Settings::values.renderer_backend = Settings::RendererBackend::Vulkan;
536 } else {
537 Settings::values.renderer_backend = Settings::RendererBackend::OpenGL;
538 }
539
540 Settings::Apply();
541 });
542#endif // HAS_VULKAN
543 statusBar()->insertPermanentWidget(0, renderer_status_button);
544
481 statusBar()->setVisible(true); 545 statusBar()->setVisible(true);
482 setStyleSheet(QStringLiteral("QStatusBar::item{border: none;}")); 546 setStyleSheet(QStringLiteral("QStatusBar::item{border: none;}"));
483} 547}
@@ -640,6 +704,7 @@ void GMainWindow::InitializeHotkeys() {
640 Settings::values.use_docked_mode = !Settings::values.use_docked_mode; 704 Settings::values.use_docked_mode = !Settings::values.use_docked_mode;
641 OnDockedModeChanged(!Settings::values.use_docked_mode, 705 OnDockedModeChanged(!Settings::values.use_docked_mode,
642 Settings::values.use_docked_mode); 706 Settings::values.use_docked_mode);
707 dock_status_button->setChecked(Settings::values.use_docked_mode);
643 }); 708 });
644} 709}
645 710
@@ -806,70 +871,12 @@ void GMainWindow::AllowOSSleep() {
806#endif 871#endif
807} 872}
808 873
809QStringList GMainWindow::GetUnsupportedGLExtensions() {
810 QStringList unsupported_ext;
811
812 if (!GLAD_GL_ARB_buffer_storage) {
813 unsupported_ext.append(QStringLiteral("ARB_buffer_storage"));
814 }
815 if (!GLAD_GL_ARB_direct_state_access) {
816 unsupported_ext.append(QStringLiteral("ARB_direct_state_access"));
817 }
818 if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) {
819 unsupported_ext.append(QStringLiteral("ARB_vertex_type_10f_11f_11f_rev"));
820 }
821 if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) {
822 unsupported_ext.append(QStringLiteral("ARB_texture_mirror_clamp_to_edge"));
823 }
824 if (!GLAD_GL_ARB_multi_bind) {
825 unsupported_ext.append(QStringLiteral("ARB_multi_bind"));
826 }
827 if (!GLAD_GL_ARB_clip_control) {
828 unsupported_ext.append(QStringLiteral("ARB_clip_control"));
829 }
830
831 // Extensions required to support some texture formats.
832 if (!GLAD_GL_EXT_texture_compression_s3tc) {
833 unsupported_ext.append(QStringLiteral("EXT_texture_compression_s3tc"));
834 }
835 if (!GLAD_GL_ARB_texture_compression_rgtc) {
836 unsupported_ext.append(QStringLiteral("ARB_texture_compression_rgtc"));
837 }
838 if (!GLAD_GL_ARB_depth_buffer_float) {
839 unsupported_ext.append(QStringLiteral("ARB_depth_buffer_float"));
840 }
841
842 for (const QString& ext : unsupported_ext) {
843 LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString());
844 }
845
846 return unsupported_ext;
847}
848
849bool GMainWindow::LoadROM(const QString& filename) { 874bool GMainWindow::LoadROM(const QString& filename) {
850 // Shutdown previous session if the emu thread is still active... 875 // Shutdown previous session if the emu thread is still active...
851 if (emu_thread != nullptr) 876 if (emu_thread != nullptr)
852 ShutdownGame(); 877 ShutdownGame();
853 878
854 render_window->InitRenderTarget(); 879 if (!render_window->InitRenderTarget()) {
855
856 {
857 Core::Frontend::ScopeAcquireWindowContext acquire_context{*render_window};
858 if (!gladLoadGL()) {
859 QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3 Core!"),
860 tr("Your GPU may not support OpenGL 4.3, or you do not "
861 "have the latest graphics driver."));
862 return false;
863 }
864 }
865
866 const QStringList unsupported_gl_extensions = GetUnsupportedGLExtensions();
867 if (!unsupported_gl_extensions.empty()) {
868 QMessageBox::critical(this, tr("Error while initializing OpenGL Core!"),
869 tr("Your GPU may not support one or more required OpenGL"
870 "extensions. Please ensure you have the latest graphics "
871 "driver.<br><br>Unsupported extensions:<br>") +
872 unsupported_gl_extensions.join(QStringLiteral("<br>")));
873 return false; 880 return false;
874 } 881 }
875 882
@@ -980,7 +987,9 @@ void GMainWindow::BootGame(const QString& filename) {
980 // Create and start the emulation thread 987 // Create and start the emulation thread
981 emu_thread = std::make_unique<EmuThread>(render_window); 988 emu_thread = std::make_unique<EmuThread>(render_window);
982 emit EmulationStarting(emu_thread.get()); 989 emit EmulationStarting(emu_thread.get());
983 render_window->moveContext(); 990 if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
991 render_window->moveContext();
992 }
984 emu_thread->start(); 993 emu_thread->start();
985 994
986 connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); 995 connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
@@ -1000,6 +1009,8 @@ void GMainWindow::BootGame(const QString& filename) {
1000 game_list_placeholder->hide(); 1009 game_list_placeholder->hide();
1001 } 1010 }
1002 status_bar_update_timer.start(2000); 1011 status_bar_update_timer.start(2000);
1012 async_status_button->setDisabled(true);
1013 renderer_status_button->setDisabled(true);
1003 1014
1004 const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); 1015 const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
1005 1016
@@ -1065,10 +1076,13 @@ void GMainWindow::ShutdownGame() {
1065 1076
1066 // Disable status bar updates 1077 // Disable status bar updates
1067 status_bar_update_timer.stop(); 1078 status_bar_update_timer.stop();
1068 message_label->setVisible(false);
1069 emu_speed_label->setVisible(false); 1079 emu_speed_label->setVisible(false);
1070 game_fps_label->setVisible(false); 1080 game_fps_label->setVisible(false);
1071 emu_frametime_label->setVisible(false); 1081 emu_frametime_label->setVisible(false);
1082 async_status_button->setEnabled(true);
1083#ifdef HAS_VULKAN
1084 renderer_status_button->setEnabled(true);
1085#endif
1072 1086
1073 emulation_running = false; 1087 emulation_running = false;
1074 1088
@@ -1836,6 +1850,13 @@ void GMainWindow::OnConfigure() {
1836 } 1850 }
1837 1851
1838 config->Save(); 1852 config->Save();
1853
1854 dock_status_button->setChecked(Settings::values.use_docked_mode);
1855 async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation);
1856#ifdef HAS_VULKAN
1857 renderer_status_button->setChecked(Settings::values.renderer_backend ==
1858 Settings::RendererBackend::Vulkan);
1859#endif
1839} 1860}
1840 1861
1841void GMainWindow::OnLoadAmiibo() { 1862void GMainWindow::OnLoadAmiibo() {
@@ -2028,7 +2049,6 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
2028 if (emu_thread) { 2049 if (emu_thread) {
2029 emu_thread->SetRunning(true); 2050 emu_thread->SetRunning(true);
2030 message_label->setText(status_message); 2051 message_label->setText(status_message);
2031 message_label->setVisible(true);
2032 } 2052 }
2033 } 2053 }
2034} 2054}
@@ -2195,6 +2215,18 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
2195 QWidget::closeEvent(event); 2215 QWidget::closeEvent(event);
2196} 2216}
2197 2217
2218void GMainWindow::keyPressEvent(QKeyEvent* event) {
2219 if (render_window) {
2220 render_window->ForwardKeyPressEvent(event);
2221 }
2222}
2223
2224void GMainWindow::keyReleaseEvent(QKeyEvent* event) {
2225 if (render_window) {
2226 render_window->ForwardKeyReleaseEvent(event);
2227 }
2228}
2229
2198static bool IsSingleFileDropEvent(QDropEvent* event) { 2230static bool IsSingleFileDropEvent(QDropEvent* event) {
2199 const QMimeData* mimeData = event->mimeData(); 2231 const QMimeData* mimeData = event->mimeData();
2200 return mimeData->hasUrls() && mimeData->urls().length() == 1; 2232 return mimeData->hasUrls() && mimeData->urls().length() == 1;
@@ -2227,18 +2259,6 @@ void GMainWindow::dragMoveEvent(QDragMoveEvent* event) {
2227 event->acceptProposedAction(); 2259 event->acceptProposedAction();
2228} 2260}
2229 2261
2230void GMainWindow::keyPressEvent(QKeyEvent* event) {
2231 if (render_window) {
2232 render_window->ForwardKeyPressEvent(event);
2233 }
2234}
2235
2236void GMainWindow::keyReleaseEvent(QKeyEvent* event) {
2237 if (render_window) {
2238 render_window->ForwardKeyReleaseEvent(event);
2239 }
2240}
2241
2242bool GMainWindow::ConfirmChangeGame() { 2262bool GMainWindow::ConfirmChangeGame() {
2243 if (emu_thread == nullptr) 2263 if (emu_thread == nullptr)
2244 return true; 2264 return true;
@@ -2290,8 +2310,16 @@ void GMainWindow::UpdateUITheme() {
2290 QStringList theme_paths(default_theme_paths); 2310 QStringList theme_paths(default_theme_paths);
2291 2311
2292 if (is_default_theme || current_theme.isEmpty()) { 2312 if (is_default_theme || current_theme.isEmpty()) {
2293 qApp->setStyleSheet({}); 2313 const QString theme_uri(QStringLiteral(":default/style.qss"));
2294 setStyleSheet({}); 2314 QFile f(theme_uri);
2315 if (f.open(QFile::ReadOnly | QFile::Text)) {
2316 QTextStream ts(&f);
2317 qApp->setStyleSheet(ts.readAll());
2318 setStyleSheet(ts.readAll());
2319 } else {
2320 qApp->setStyleSheet({});
2321 setStyleSheet({});
2322 }
2295 theme_paths.append(default_icons); 2323 theme_paths.append(default_icons);
2296 QIcon::setThemeName(default_icons); 2324 QIcon::setThemeName(default_icons);
2297 } else { 2325 } else {
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index a56f9a981..8eba2172c 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -27,6 +27,7 @@ class LoadingScreen;
27class MicroProfileDialog; 27class MicroProfileDialog;
28class ProfilerWidget; 28class ProfilerWidget;
29class QLabel; 29class QLabel;
30class QPushButton;
30class WaitTreeWidget; 31class WaitTreeWidget;
31enum class GameListOpenTarget; 32enum class GameListOpenTarget;
32class GameListPlaceholder; 33class GameListPlaceholder;
@@ -130,7 +131,6 @@ private:
130 void PreventOSSleep(); 131 void PreventOSSleep();
131 void AllowOSSleep(); 132 void AllowOSSleep();
132 133
133 QStringList GetUnsupportedGLExtensions();
134 bool LoadROM(const QString& filename); 134 bool LoadROM(const QString& filename);
135 void BootGame(const QString& filename); 135 void BootGame(const QString& filename);
136 void ShutdownGame(); 136 void ShutdownGame();
@@ -229,6 +229,9 @@ private:
229 QLabel* emu_speed_label = nullptr; 229 QLabel* emu_speed_label = nullptr;
230 QLabel* game_fps_label = nullptr; 230 QLabel* game_fps_label = nullptr;
231 QLabel* emu_frametime_label = nullptr; 231 QLabel* emu_frametime_label = nullptr;
232 QPushButton* async_status_button = nullptr;
233 QPushButton* renderer_status_button = nullptr;
234 QPushButton* dock_status_button = nullptr;
232 QTimer status_bar_update_timer; 235 QTimer status_bar_update_timer;
233 236
234 std::unique_ptr<Config> config; 237 std::unique_ptr<Config> config;
diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt
index b5f06ab9e..a15719a0f 100644
--- a/src/yuzu_cmd/CMakeLists.txt
+++ b/src/yuzu_cmd/CMakeLists.txt
@@ -8,11 +8,22 @@ add_executable(yuzu-cmd
8 emu_window/emu_window_sdl2_gl.h 8 emu_window/emu_window_sdl2_gl.h
9 emu_window/emu_window_sdl2.cpp 9 emu_window/emu_window_sdl2.cpp
10 emu_window/emu_window_sdl2.h 10 emu_window/emu_window_sdl2.h
11 emu_window/emu_window_sdl2_gl.cpp
12 emu_window/emu_window_sdl2_gl.h
11 resource.h 13 resource.h
12 yuzu.cpp 14 yuzu.cpp
13 yuzu.rc 15 yuzu.rc
14) 16)
15 17
18if (ENABLE_VULKAN)
19 target_sources(yuzu-cmd PRIVATE
20 emu_window/emu_window_sdl2_vk.cpp
21 emu_window/emu_window_sdl2_vk.h)
22
23 target_include_directories(yuzu-cmd PRIVATE ../../externals/Vulkan-Headers/include)
24 target_compile_definitions(yuzu-cmd PRIVATE HAS_VULKAN)
25endif()
26
16create_target_directory_groups(yuzu-cmd) 27create_target_directory_groups(yuzu-cmd)
17 28
18target_link_libraries(yuzu-cmd PRIVATE common core input_common) 29target_link_libraries(yuzu-cmd PRIVATE common core input_common)
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 161583b54..b01a36023 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -371,6 +371,12 @@ void Config::ReadValues() {
371 Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false); 371 Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false);
372 372
373 // Renderer 373 // Renderer
374 const int renderer_backend = sdl2_config->GetInteger(
375 "Renderer", "backend", static_cast<int>(Settings::RendererBackend::OpenGL));
376 Settings::values.renderer_backend = static_cast<Settings::RendererBackend>(renderer_backend);
377 Settings::values.renderer_debug = sdl2_config->GetBoolean("Renderer", "debug", false);
378 Settings::values.vulkan_device = sdl2_config->GetInteger("Renderer", "vulkan_device", 0);
379
374 Settings::values.resolution_factor = 380 Settings::values.resolution_factor =
375 static_cast<float>(sdl2_config->GetReal("Renderer", "resolution_factor", 1.0)); 381 static_cast<float>(sdl2_config->GetReal("Renderer", "resolution_factor", 1.0));
376 Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true); 382 Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index e829f8695..00fd88279 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -98,6 +98,17 @@ udp_pad_index=
98use_multi_core= 98use_multi_core=
99 99
100[Renderer] 100[Renderer]
101# Which backend API to use.
102# 0 (default): OpenGL, 1: Vulkan
103backend =
104
105# Enable graphics API debugging mode.
106# 0 (default): Disabled, 1: Enabled
107debug =
108
109# Which Vulkan physical device to use (defaults to 0)
110vulkan_device =
111
101# Whether to use software or hardware rendering. 112# Whether to use software or hardware rendering.
102# 0: Software, 1 (default): Hardware 113# 0: Software, 1 (default): Hardware
103use_hw_renderer = 114use_hw_renderer =
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index b1c512db1..e96139885 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -89,6 +89,10 @@ bool EmuWindow_SDL2::IsOpen() const {
89 return is_open; 89 return is_open;
90} 90}
91 91
92bool EmuWindow_SDL2::IsShown() const {
93 return is_shown;
94}
95
92void EmuWindow_SDL2::OnResize() { 96void EmuWindow_SDL2::OnResize() {
93 int width, height; 97 int width, height;
94 SDL_GetWindowSize(render_window, &width, &height); 98 SDL_GetWindowSize(render_window, &width, &height);
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
index eaa971f77..b38f56661 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
@@ -21,6 +21,9 @@ public:
21 /// Whether the window is still open, and a close request hasn't yet been sent 21 /// Whether the window is still open, and a close request hasn't yet been sent
22 bool IsOpen() const; 22 bool IsOpen() const;
23 23
24 /// Returns if window is shown (not minimized)
25 bool IsShown() const override;
26
24protected: 27protected:
25 /// Called by PollEvents when a key is pressed or released. 28 /// Called by PollEvents when a key is pressed or released.
26 void OnKeyEvent(int key, u8 state); 29 void OnKeyEvent(int key, u8 state);
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
index 6fde694a2..7ffa0ac09 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
@@ -9,6 +9,7 @@
9#include <SDL.h> 9#include <SDL.h>
10#include <fmt/format.h> 10#include <fmt/format.h>
11#include <glad/glad.h> 11#include <glad/glad.h>
12#include "common/assert.h"
12#include "common/logging/log.h" 13#include "common/logging/log.h"
13#include "common/scm_rev.h" 14#include "common/scm_rev.h"
14#include "common/string_util.h" 15#include "common/string_util.h"
@@ -151,6 +152,12 @@ void EmuWindow_SDL2_GL::DoneCurrent() {
151 SDL_GL_MakeCurrent(render_window, nullptr); 152 SDL_GL_MakeCurrent(render_window, nullptr);
152} 153}
153 154
155void EmuWindow_SDL2_GL::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
156 void* surface) const {
157 // Should not have been called from OpenGL
158 UNREACHABLE();
159}
160
154std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const { 161std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const {
155 return std::make_unique<SDLGLContext>(); 162 return std::make_unique<SDLGLContext>();
156} 163}
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
index 630deba93..c753085a8 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
@@ -22,6 +22,10 @@ public:
22 /// Releases the GL context from the caller thread 22 /// Releases the GL context from the caller thread
23 void DoneCurrent() override; 23 void DoneCurrent() override;
24 24
25 /// Ignored in OpenGL
26 void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
27 void* surface) const override;
28
25 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; 29 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
26 30
27private: 31private:
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
new file mode 100644
index 000000000..a203f0da9
--- /dev/null
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
@@ -0,0 +1,162 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <string>
7#include <vector>
8#include <SDL.h>
9#include <SDL_vulkan.h>
10#include <fmt/format.h>
11#include <vulkan/vulkan.h>
12#include "common/assert.h"
13#include "common/logging/log.h"
14#include "common/scm_rev.h"
15#include "core/settings.h"
16#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h"
17
18EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(bool fullscreen) : EmuWindow_SDL2(fullscreen) {
19 if (SDL_Vulkan_LoadLibrary(nullptr) != 0) {
20 LOG_CRITICAL(Frontend, "SDL failed to load the Vulkan library: {}", SDL_GetError());
21 exit(EXIT_FAILURE);
22 }
23
24 vkGetInstanceProcAddr =
25 reinterpret_cast<PFN_vkGetInstanceProcAddr>(SDL_Vulkan_GetVkGetInstanceProcAddr());
26 if (vkGetInstanceProcAddr == nullptr) {
27 LOG_CRITICAL(Frontend, "Failed to retrieve Vulkan function pointer!");
28 exit(EXIT_FAILURE);
29 }
30
31 const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name,
32 Common::g_scm_branch, Common::g_scm_desc);
33 render_window =
34 SDL_CreateWindow(window_title.c_str(),
35 SDL_WINDOWPOS_UNDEFINED, // x position
36 SDL_WINDOWPOS_UNDEFINED, // y position
37 Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height,
38 SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_VULKAN);
39
40 const bool use_standard_layers = UseStandardLayers(vkGetInstanceProcAddr);
41
42 u32 extra_ext_count{};
43 if (!SDL_Vulkan_GetInstanceExtensions(render_window, &extra_ext_count, NULL)) {
44 LOG_CRITICAL(Frontend, "Failed to query Vulkan extensions count from SDL! {}",
45 SDL_GetError());
46 exit(1);
47 }
48
49 auto extra_ext_names = std::make_unique<const char* []>(extra_ext_count);
50 if (!SDL_Vulkan_GetInstanceExtensions(render_window, &extra_ext_count, extra_ext_names.get())) {
51 LOG_CRITICAL(Frontend, "Failed to query Vulkan extensions from SDL! {}", SDL_GetError());
52 exit(1);
53 }
54 std::vector<const char*> enabled_extensions;
55 enabled_extensions.insert(enabled_extensions.begin(), extra_ext_names.get(),
56 extra_ext_names.get() + extra_ext_count);
57
58 std::vector<const char*> enabled_layers;
59 if (use_standard_layers) {
60 enabled_extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
61 enabled_layers.push_back("VK_LAYER_LUNARG_standard_validation");
62 }
63
64 VkApplicationInfo app_info{};
65 app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
66 app_info.apiVersion = VK_API_VERSION_1_1;
67 app_info.applicationVersion = VK_MAKE_VERSION(0, 1, 0);
68 app_info.pApplicationName = "yuzu-emu";
69 app_info.engineVersion = VK_MAKE_VERSION(0, 1, 0);
70 app_info.pEngineName = "yuzu-emu";
71
72 VkInstanceCreateInfo instance_ci{};
73 instance_ci.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
74 instance_ci.pApplicationInfo = &app_info;
75 instance_ci.enabledExtensionCount = static_cast<u32>(enabled_extensions.size());
76 instance_ci.ppEnabledExtensionNames = enabled_extensions.data();
77 if (Settings::values.renderer_debug) {
78 instance_ci.enabledLayerCount = static_cast<u32>(enabled_layers.size());
79 instance_ci.ppEnabledLayerNames = enabled_layers.data();
80 }
81
82 const auto vkCreateInstance =
83 reinterpret_cast<PFN_vkCreateInstance>(vkGetInstanceProcAddr(nullptr, "vkCreateInstance"));
84 if (vkCreateInstance == nullptr ||
85 vkCreateInstance(&instance_ci, nullptr, &vk_instance) != VK_SUCCESS) {
86 LOG_CRITICAL(Frontend, "Failed to create Vulkan instance!");
87 exit(EXIT_FAILURE);
88 }
89
90 vkDestroyInstance = reinterpret_cast<PFN_vkDestroyInstance>(
91 vkGetInstanceProcAddr(vk_instance, "vkDestroyInstance"));
92 if (vkDestroyInstance == nullptr) {
93 LOG_CRITICAL(Frontend, "Failed to retrieve Vulkan function pointer!");
94 exit(EXIT_FAILURE);
95 }
96
97 if (!SDL_Vulkan_CreateSurface(render_window, vk_instance, &vk_surface)) {
98 LOG_CRITICAL(Frontend, "Failed to create Vulkan surface! {}", SDL_GetError());
99 exit(EXIT_FAILURE);
100 }
101
102 OnResize();
103 OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
104 SDL_PumpEvents();
105 LOG_INFO(Frontend, "yuzu Version: {} | {}-{} (Vulkan)", Common::g_build_name,
106 Common::g_scm_branch, Common::g_scm_desc);
107}
108
109EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() {
110 vkDestroyInstance(vk_instance, nullptr);
111}
112
113void EmuWindow_SDL2_VK::SwapBuffers() {}
114
115void EmuWindow_SDL2_VK::MakeCurrent() {
116 // Unused on Vulkan
117}
118
119void EmuWindow_SDL2_VK::DoneCurrent() {
120 // Unused on Vulkan
121}
122
123void EmuWindow_SDL2_VK::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
124 void* surface) const {
125 const auto instance_proc_addr = vkGetInstanceProcAddr;
126 std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr));
127 std::memcpy(instance, &vk_instance, sizeof(vk_instance));
128 std::memcpy(surface, &vk_surface, sizeof(vk_surface));
129}
130
131std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_VK::CreateSharedContext() const {
132 return nullptr;
133}
134
135bool EmuWindow_SDL2_VK::UseStandardLayers(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr) const {
136 if (!Settings::values.renderer_debug) {
137 return false;
138 }
139
140 const auto vkEnumerateInstanceLayerProperties =
141 reinterpret_cast<PFN_vkEnumerateInstanceLayerProperties>(
142 vkGetInstanceProcAddr(nullptr, "vkEnumerateInstanceLayerProperties"));
143 if (vkEnumerateInstanceLayerProperties == nullptr) {
144 LOG_CRITICAL(Frontend, "Failed to retrieve Vulkan function pointer!");
145 return false;
146 }
147
148 u32 available_layers_count{};
149 if (vkEnumerateInstanceLayerProperties(&available_layers_count, nullptr) != VK_SUCCESS) {
150 LOG_CRITICAL(Frontend, "Failed to enumerate Vulkan validation layers!");
151 return false;
152 }
153 std::vector<VkLayerProperties> layers(available_layers_count);
154 if (vkEnumerateInstanceLayerProperties(&available_layers_count, layers.data()) != VK_SUCCESS) {
155 LOG_CRITICAL(Frontend, "Failed to enumerate Vulkan validation layers!");
156 return false;
157 }
158
159 return std::find_if(layers.begin(), layers.end(), [&](const auto& layer) {
160 return layer.layerName == std::string("VK_LAYER_LUNARG_standard_validation");
161 }) != layers.end();
162}
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
new file mode 100644
index 000000000..2a7c06a24
--- /dev/null
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
@@ -0,0 +1,39 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <vulkan/vulkan.h>
8#include "core/frontend/emu_window.h"
9#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
10
11class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 {
12public:
13 explicit EmuWindow_SDL2_VK(bool fullscreen);
14 ~EmuWindow_SDL2_VK();
15
16 /// Swap buffers to display the next frame
17 void SwapBuffers() override;
18
19 /// Makes the graphics context current for the caller thread
20 void MakeCurrent() override;
21
22 /// Releases the GL context from the caller thread
23 void DoneCurrent() override;
24
25 /// Retrieves Vulkan specific handlers from the window
26 void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
27 void* surface) const override;
28
29 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
30
31private:
32 bool UseStandardLayers(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr) const;
33
34 VkInstance vk_instance{};
35 VkSurfaceKHR vk_surface{};
36
37 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{};
38 PFN_vkDestroyInstance vkDestroyInstance{};
39};
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 3ee088a91..325795321 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -32,6 +32,9 @@
32#include "yuzu_cmd/config.h" 32#include "yuzu_cmd/config.h"
33#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 33#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
34#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" 34#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h"
35#ifdef HAS_VULKAN
36#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h"
37#endif
35 38
36#include "core/file_sys/registered_cache.h" 39#include "core/file_sys/registered_cache.h"
37 40
@@ -174,7 +177,20 @@ int main(int argc, char** argv) {
174 Settings::values.use_gdbstub = use_gdbstub; 177 Settings::values.use_gdbstub = use_gdbstub;
175 Settings::Apply(); 178 Settings::Apply();
176 179
177 std::unique_ptr<EmuWindow_SDL2> emu_window{std::make_unique<EmuWindow_SDL2_GL>(fullscreen)}; 180 std::unique_ptr<EmuWindow_SDL2> emu_window;
181 switch (Settings::values.renderer_backend) {
182 case Settings::RendererBackend::OpenGL:
183 emu_window = std::make_unique<EmuWindow_SDL2_GL>(fullscreen);
184 break;
185 case Settings::RendererBackend::Vulkan:
186#ifdef HAS_VULKAN
187 emu_window = std::make_unique<EmuWindow_SDL2_VK>(fullscreen);
188 break;
189#else
190 LOG_CRITICAL(Frontend, "Vulkan backend has not been compiled!");
191 return 1;
192#endif
193 }
178 194
179 if (!Settings::values.use_multi_core) { 195 if (!Settings::values.use_multi_core) {
180 // Single core mode must acquire OpenGL context for entire emulation session 196 // Single core mode must acquire OpenGL context for entire emulation session
diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp
index e7fe8decf..f2cc4a797 100644
--- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp
+++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp
@@ -5,10 +5,15 @@
5#include <algorithm> 5#include <algorithm>
6#include <cstdlib> 6#include <cstdlib>
7#include <string> 7#include <string>
8
9#include <fmt/format.h>
10
8#define SDL_MAIN_HANDLED 11#define SDL_MAIN_HANDLED
9#include <SDL.h> 12#include <SDL.h>
10#include <fmt/format.h> 13
11#include <glad/glad.h> 14#include <glad/glad.h>
15
16#include "common/assert.h"
12#include "common/logging/log.h" 17#include "common/logging/log.h"
13#include "common/scm_rev.h" 18#include "common/scm_rev.h"
14#include "core/settings.h" 19#include "core/settings.h"
@@ -120,3 +125,11 @@ void EmuWindow_SDL2_Hide::MakeCurrent() {
120void EmuWindow_SDL2_Hide::DoneCurrent() { 125void EmuWindow_SDL2_Hide::DoneCurrent() {
121 SDL_GL_MakeCurrent(render_window, nullptr); 126 SDL_GL_MakeCurrent(render_window, nullptr);
122} 127}
128
129bool EmuWindow_SDL2_Hide::IsShown() const {
130 return false;
131}
132
133void EmuWindow_SDL2_Hide::RetrieveVulkanHandlers(void*, void*, void*) const {
134 UNREACHABLE();
135}
diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h
index 1a8953c75..c7fccc002 100644
--- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h
+++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h
@@ -25,6 +25,13 @@ public:
25 /// Releases the GL context from the caller thread 25 /// Releases the GL context from the caller thread
26 void DoneCurrent() override; 26 void DoneCurrent() override;
27 27
28 /// Whether the screen is being shown or not.
29 bool IsShown() const override;
30
31 /// Retrieves Vulkan specific handlers from the window
32 void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
33 void* surface) const override;
34
28 /// Whether the window is still open, and a close request hasn't yet been sent 35 /// Whether the window is still open, and a close request hasn't yet been sent
29 bool IsOpen() const; 36 bool IsOpen() const;
30 37