summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt10
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp5
-rw-r--r--src/core/arm/exclusive_monitor.cpp14
-rw-r--r--src/core/arm/exclusive_monitor.h9
-rw-r--r--src/core/arm/unicorn/arm_unicorn.cpp3
-rw-r--r--src/core/core.cpp79
-rw-r--r--src/core/core.h10
-rw-r--r--src/core/core_cpu.cpp127
-rw-r--r--src/core/core_cpu.h120
-rw-r--r--src/core/core_manager.cpp70
-rw-r--r--src/core/core_manager.h63
-rw-r--r--src/core/cpu_core_manager.cpp152
-rw-r--r--src/core/cpu_core_manager.h62
-rw-r--r--src/core/cpu_manager.cpp81
-rw-r--r--src/core/cpu_manager.h50
-rw-r--r--src/core/frontend/emu_window.h7
-rw-r--r--src/core/frontend/input.h10
-rw-r--r--src/core/gdbstub/gdbstub.cpp2
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp1
-rw-r--r--src/core/hle/kernel/kernel.cpp52
-rw-r--r--src/core/hle/kernel/kernel.h19
-rw-r--r--src/core/hle/kernel/physical_core.cpp51
-rw-r--r--src/core/hle/kernel/physical_core.h77
-rw-r--r--src/core/hle/kernel/scheduler.cpp1
-rw-r--r--src/core/hle/kernel/svc.cpp2
-rw-r--r--src/core/hle/kernel/thread.cpp3
-rw-r--r--src/core/hle/kernel/wait_object.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp39
-rw-r--r--src/core/hle/service/sockets/bsd.cpp48
-rw-r--r--src/core/hle/service/sockets/bsd.h4
-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.h37
-rw-r--r--src/video_core/guest_driver.cpp36
-rw-r--r--src/video_core/guest_driver.h41
-rw-r--r--src/video_core/rasterizer_interface.h14
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp34
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp4
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp43
-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/gl_texture_cache.cpp40
-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_shader_decompiler.cpp83
-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/memory.cpp107
-rw-r--r--src/video_core/shader/decode/other.cpp9
-rw-r--r--src/video_core/shader/decode/texture.cpp114
-rw-r--r--src/video_core/shader/node.h91
-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/texture_cache/surface_base.cpp2
-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
102 files changed, 2825 insertions, 883 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 1a3647a67..d342cafe0 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -15,14 +15,14 @@ add_library(core STATIC
15 constants.h 15 constants.h
16 core.cpp 16 core.cpp
17 core.h 17 core.h
18 core_cpu.cpp 18 core_manager.cpp
19 core_cpu.h 19 core_manager.h
20 core_timing.cpp 20 core_timing.cpp
21 core_timing.h 21 core_timing.h
22 core_timing_util.cpp 22 core_timing_util.cpp
23 core_timing_util.h 23 core_timing_util.h
24 cpu_core_manager.cpp 24 cpu_manager.cpp
25 cpu_core_manager.h 25 cpu_manager.h
26 crypto/aes_util.cpp 26 crypto/aes_util.cpp
27 crypto/aes_util.h 27 crypto/aes_util.h
28 crypto/encryption_layer.cpp 28 crypto/encryption_layer.cpp
@@ -158,6 +158,8 @@ add_library(core STATIC
158 hle/kernel/mutex.h 158 hle/kernel/mutex.h
159 hle/kernel/object.cpp 159 hle/kernel/object.cpp
160 hle/kernel/object.h 160 hle/kernel/object.h
161 hle/kernel/physical_core.cpp
162 hle/kernel/physical_core.h
161 hle/kernel/process.cpp 163 hle/kernel/process.cpp
162 hle/kernel/process.h 164 hle/kernel/process.h
163 hle/kernel/process_capability.cpp 165 hle/kernel/process_capability.cpp
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index e825c0526..791640a3a 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -10,11 +10,12 @@
10#include "common/microprofile.h" 10#include "common/microprofile.h"
11#include "core/arm/dynarmic/arm_dynarmic.h" 11#include "core/arm/dynarmic/arm_dynarmic.h"
12#include "core/core.h" 12#include "core/core.h"
13#include "core/core_cpu.h" 13#include "core/core_manager.h"
14#include "core/core_timing.h" 14#include "core/core_timing.h"
15#include "core/core_timing_util.h" 15#include "core/core_timing_util.h"
16#include "core/gdbstub/gdbstub.h" 16#include "core/gdbstub/gdbstub.h"
17#include "core/hle/kernel/process.h" 17#include "core/hle/kernel/process.h"
18#include "core/hle/kernel/scheduler.h"
18#include "core/hle/kernel/svc.h" 19#include "core/hle/kernel/svc.h"
19#include "core/hle/kernel/vm_manager.h" 20#include "core/hle/kernel/vm_manager.h"
20#include "core/memory.h" 21#include "core/memory.h"
@@ -87,7 +88,7 @@ public:
87 if (GDBStub::IsServerEnabled()) { 88 if (GDBStub::IsServerEnabled()) {
88 parent.jit->HaltExecution(); 89 parent.jit->HaltExecution();
89 parent.SetPC(pc); 90 parent.SetPC(pc);
90 Kernel::Thread* thread = Kernel::GetCurrentThread(); 91 Kernel::Thread* const thread = parent.system.CurrentScheduler().GetCurrentThread();
91 parent.SaveContext(thread->GetContext()); 92 parent.SaveContext(thread->GetContext());
92 GDBStub::Break(); 93 GDBStub::Break();
93 GDBStub::SendTrap(thread, 5); 94 GDBStub::SendTrap(thread, 5);
diff --git a/src/core/arm/exclusive_monitor.cpp b/src/core/arm/exclusive_monitor.cpp
index abd59ff4b..94570e520 100644
--- a/src/core/arm/exclusive_monitor.cpp
+++ b/src/core/arm/exclusive_monitor.cpp
@@ -2,10 +2,24 @@
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#ifdef ARCHITECTURE_x86_64
6#include "core/arm/dynarmic/arm_dynarmic.h"
7#endif
5#include "core/arm/exclusive_monitor.h" 8#include "core/arm/exclusive_monitor.h"
9#include "core/memory.h"
6 10
7namespace Core { 11namespace Core {
8 12
9ExclusiveMonitor::~ExclusiveMonitor() = default; 13ExclusiveMonitor::~ExclusiveMonitor() = default;
10 14
15std::unique_ptr<Core::ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory,
16 std::size_t num_cores) {
17#ifdef ARCHITECTURE_x86_64
18 return std::make_unique<Core::DynarmicExclusiveMonitor>(memory, num_cores);
19#else
20 // TODO(merry): Passthrough exclusive monitor
21 return nullptr;
22#endif
23}
24
11} // namespace Core 25} // namespace Core
diff --git a/src/core/arm/exclusive_monitor.h b/src/core/arm/exclusive_monitor.h
index f59aca667..4ef418b90 100644
--- a/src/core/arm/exclusive_monitor.h
+++ b/src/core/arm/exclusive_monitor.h
@@ -4,8 +4,14 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <memory>
8
7#include "common/common_types.h" 9#include "common/common_types.h"
8 10
11namespace Memory {
12class Memory;
13}
14
9namespace Core { 15namespace Core {
10 16
11class ExclusiveMonitor { 17class ExclusiveMonitor {
@@ -22,4 +28,7 @@ public:
22 virtual bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) = 0; 28 virtual bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) = 0;
23}; 29};
24 30
31std::unique_ptr<Core::ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory,
32 std::size_t num_cores);
33
25} // namespace Core 34} // namespace Core
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp
index 48182c99a..f99ad5802 100644
--- a/src/core/arm/unicorn/arm_unicorn.cpp
+++ b/src/core/arm/unicorn/arm_unicorn.cpp
@@ -9,6 +9,7 @@
9#include "core/arm/unicorn/arm_unicorn.h" 9#include "core/arm/unicorn/arm_unicorn.h"
10#include "core/core.h" 10#include "core/core.h"
11#include "core/core_timing.h" 11#include "core/core_timing.h"
12#include "core/hle/kernel/scheduler.h"
12#include "core/hle/kernel/svc.h" 13#include "core/hle/kernel/svc.h"
13 14
14namespace Core { 15namespace Core {
@@ -177,7 +178,7 @@ void ARM_Unicorn::ExecuteInstructions(std::size_t num_instructions) {
177 uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address); 178 uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address);
178 } 179 }
179 180
180 Kernel::Thread* thread = Kernel::GetCurrentThread(); 181 Kernel::Thread* const thread = system.CurrentScheduler().GetCurrentThread();
181 SaveContext(thread->GetContext()); 182 SaveContext(thread->GetContext());
182 if (last_bkpt_hit || GDBStub::IsMemoryBreak() || GDBStub::GetCpuStepFlag()) { 183 if (last_bkpt_hit || GDBStub::IsMemoryBreak() || GDBStub::GetCpuStepFlag()) {
183 last_bkpt_hit = false; 184 last_bkpt_hit = false;
diff --git a/src/core/core.cpp b/src/core/core.cpp
index d697b80ef..0eb0c0dca 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -11,9 +11,9 @@
11#include "common/string_util.h" 11#include "common/string_util.h"
12#include "core/arm/exclusive_monitor.h" 12#include "core/arm/exclusive_monitor.h"
13#include "core/core.h" 13#include "core/core.h"
14#include "core/core_cpu.h" 14#include "core/core_manager.h"
15#include "core/core_timing.h" 15#include "core/core_timing.h"
16#include "core/cpu_core_manager.h" 16#include "core/cpu_manager.h"
17#include "core/file_sys/bis_factory.h" 17#include "core/file_sys/bis_factory.h"
18#include "core/file_sys/card_image.h" 18#include "core/file_sys/card_image.h"
19#include "core/file_sys/mode.h" 19#include "core/file_sys/mode.h"
@@ -28,6 +28,7 @@
28#include "core/hardware_interrupt_manager.h" 28#include "core/hardware_interrupt_manager.h"
29#include "core/hle/kernel/client_port.h" 29#include "core/hle/kernel/client_port.h"
30#include "core/hle/kernel/kernel.h" 30#include "core/hle/kernel/kernel.h"
31#include "core/hle/kernel/physical_core.h"
31#include "core/hle/kernel/process.h" 32#include "core/hle/kernel/process.h"
32#include "core/hle/kernel/scheduler.h" 33#include "core/hle/kernel/scheduler.h"
33#include "core/hle/kernel/thread.h" 34#include "core/hle/kernel/thread.h"
@@ -113,16 +114,25 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
113struct System::Impl { 114struct System::Impl {
114 explicit Impl(System& system) 115 explicit Impl(System& system)
115 : kernel{system}, fs_controller{system}, memory{system}, 116 : kernel{system}, fs_controller{system}, memory{system},
116 cpu_core_manager{system}, reporter{system}, applet_manager{system} {} 117 cpu_manager{system}, reporter{system}, applet_manager{system} {}
117 118
118 Cpu& CurrentCpuCore() { 119 CoreManager& CurrentCoreManager() {
119 return cpu_core_manager.GetCurrentCore(); 120 return cpu_manager.GetCurrentCoreManager();
121 }
122
123 Kernel::PhysicalCore& CurrentPhysicalCore() {
124 const auto index = cpu_manager.GetActiveCoreIndex();
125 return kernel.PhysicalCore(index);
126 }
127
128 Kernel::PhysicalCore& GetPhysicalCore(std::size_t index) {
129 return kernel.PhysicalCore(index);
120 } 130 }
121 131
122 ResultStatus RunLoop(bool tight_loop) { 132 ResultStatus RunLoop(bool tight_loop) {
123 status = ResultStatus::Success; 133 status = ResultStatus::Success;
124 134
125 cpu_core_manager.RunLoop(tight_loop); 135 cpu_manager.RunLoop(tight_loop);
126 136
127 return status; 137 return status;
128 } 138 }
@@ -131,8 +141,8 @@ struct System::Impl {
131 LOG_DEBUG(HW_Memory, "initialized OK"); 141 LOG_DEBUG(HW_Memory, "initialized OK");
132 142
133 core_timing.Initialize(); 143 core_timing.Initialize();
134 cpu_core_manager.Initialize();
135 kernel.Initialize(); 144 kernel.Initialize();
145 cpu_manager.Initialize();
136 146
137 const auto current_time = std::chrono::duration_cast<std::chrono::seconds>( 147 const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
138 std::chrono::system_clock::now().time_since_epoch()); 148 std::chrono::system_clock::now().time_since_epoch());
@@ -205,7 +215,6 @@ struct System::Impl {
205 // Main process has been loaded and been made current. 215 // Main process has been loaded and been made current.
206 // Begin GPU and CPU execution. 216 // Begin GPU and CPU execution.
207 gpu_core->Start(); 217 gpu_core->Start();
208 cpu_core_manager.StartThreads();
209 218
210 // Initialize cheat engine 219 // Initialize cheat engine
211 if (cheat_engine) { 220 if (cheat_engine) {
@@ -259,7 +268,9 @@ struct System::Impl {
259 is_powered_on = false; 268 is_powered_on = false;
260 exit_lock = false; 269 exit_lock = false;
261 270
262 gpu_core->WaitIdle(); 271 if (gpu_core) {
272 gpu_core->WaitIdle();
273 }
263 274
264 // Shutdown emulation session 275 // Shutdown emulation session
265 renderer.reset(); 276 renderer.reset();
@@ -272,7 +283,7 @@ struct System::Impl {
272 gpu_core.reset(); 283 gpu_core.reset();
273 284
274 // Close all CPU/threading state 285 // Close all CPU/threading state
275 cpu_core_manager.Shutdown(); 286 cpu_manager.Shutdown();
276 287
277 // Shutdown kernel and core timing 288 // Shutdown kernel and core timing
278 kernel.Shutdown(); 289 kernel.Shutdown();
@@ -342,7 +353,7 @@ struct System::Impl {
342 std::unique_ptr<Tegra::GPU> gpu_core; 353 std::unique_ptr<Tegra::GPU> gpu_core;
343 std::unique_ptr<Hardware::InterruptManager> interrupt_manager; 354 std::unique_ptr<Hardware::InterruptManager> interrupt_manager;
344 Memory::Memory memory; 355 Memory::Memory memory;
345 CpuCoreManager cpu_core_manager; 356 CpuManager cpu_manager;
346 bool is_powered_on = false; 357 bool is_powered_on = false;
347 bool exit_lock = false; 358 bool exit_lock = false;
348 359
@@ -377,12 +388,12 @@ struct System::Impl {
377System::System() : impl{std::make_unique<Impl>(*this)} {} 388System::System() : impl{std::make_unique<Impl>(*this)} {}
378System::~System() = default; 389System::~System() = default;
379 390
380Cpu& System::CurrentCpuCore() { 391CoreManager& System::CurrentCoreManager() {
381 return impl->CurrentCpuCore(); 392 return impl->CurrentCoreManager();
382} 393}
383 394
384const Cpu& System::CurrentCpuCore() const { 395const CoreManager& System::CurrentCoreManager() const {
385 return impl->CurrentCpuCore(); 396 return impl->CurrentCoreManager();
386} 397}
387 398
388System::ResultStatus System::RunLoop(bool tight_loop) { 399System::ResultStatus System::RunLoop(bool tight_loop) {
@@ -394,7 +405,7 @@ System::ResultStatus System::SingleStep() {
394} 405}
395 406
396void System::InvalidateCpuInstructionCaches() { 407void System::InvalidateCpuInstructionCaches() {
397 impl->cpu_core_manager.InvalidateAllInstructionCaches(); 408 impl->kernel.InvalidateAllInstructionCaches();
398} 409}
399 410
400System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) { 411System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
@@ -406,13 +417,11 @@ bool System::IsPoweredOn() const {
406} 417}
407 418
408void System::PrepareReschedule() { 419void System::PrepareReschedule() {
409 CurrentCpuCore().PrepareReschedule(); 420 impl->CurrentPhysicalCore().Stop();
410} 421}
411 422
412void System::PrepareReschedule(const u32 core_index) { 423void System::PrepareReschedule(const u32 core_index) {
413 if (core_index < GlobalScheduler().CpuCoresCount()) { 424 impl->kernel.PrepareReschedule(core_index);
414 CpuCore(core_index).PrepareReschedule();
415 }
416} 425}
417 426
418PerfStatsResults System::GetAndResetPerfStats() { 427PerfStatsResults System::GetAndResetPerfStats() {
@@ -428,31 +437,31 @@ const TelemetrySession& System::TelemetrySession() const {
428} 437}
429 438
430ARM_Interface& System::CurrentArmInterface() { 439ARM_Interface& System::CurrentArmInterface() {
431 return CurrentCpuCore().ArmInterface(); 440 return impl->CurrentPhysicalCore().ArmInterface();
432} 441}
433 442
434const ARM_Interface& System::CurrentArmInterface() const { 443const ARM_Interface& System::CurrentArmInterface() const {
435 return CurrentCpuCore().ArmInterface(); 444 return impl->CurrentPhysicalCore().ArmInterface();
436} 445}
437 446
438std::size_t System::CurrentCoreIndex() const { 447std::size_t System::CurrentCoreIndex() const {
439 return CurrentCpuCore().CoreIndex(); 448 return impl->cpu_manager.GetActiveCoreIndex();
440} 449}
441 450
442Kernel::Scheduler& System::CurrentScheduler() { 451Kernel::Scheduler& System::CurrentScheduler() {
443 return CurrentCpuCore().Scheduler(); 452 return impl->CurrentPhysicalCore().Scheduler();
444} 453}
445 454
446const Kernel::Scheduler& System::CurrentScheduler() const { 455const Kernel::Scheduler& System::CurrentScheduler() const {
447 return CurrentCpuCore().Scheduler(); 456 return impl->CurrentPhysicalCore().Scheduler();
448} 457}
449 458
450Kernel::Scheduler& System::Scheduler(std::size_t core_index) { 459Kernel::Scheduler& System::Scheduler(std::size_t core_index) {
451 return CpuCore(core_index).Scheduler(); 460 return impl->GetPhysicalCore(core_index).Scheduler();
452} 461}
453 462
454const Kernel::Scheduler& System::Scheduler(std::size_t core_index) const { 463const Kernel::Scheduler& System::Scheduler(std::size_t core_index) const {
455 return CpuCore(core_index).Scheduler(); 464 return impl->GetPhysicalCore(core_index).Scheduler();
456} 465}
457 466
458/// Gets the global scheduler 467/// Gets the global scheduler
@@ -474,28 +483,28 @@ const Kernel::Process* System::CurrentProcess() const {
474} 483}
475 484
476ARM_Interface& System::ArmInterface(std::size_t core_index) { 485ARM_Interface& System::ArmInterface(std::size_t core_index) {
477 return CpuCore(core_index).ArmInterface(); 486 return impl->GetPhysicalCore(core_index).ArmInterface();
478} 487}
479 488
480const ARM_Interface& System::ArmInterface(std::size_t core_index) const { 489const ARM_Interface& System::ArmInterface(std::size_t core_index) const {
481 return CpuCore(core_index).ArmInterface(); 490 return impl->GetPhysicalCore(core_index).ArmInterface();
482} 491}
483 492
484Cpu& System::CpuCore(std::size_t core_index) { 493CoreManager& System::GetCoreManager(std::size_t core_index) {
485 return impl->cpu_core_manager.GetCore(core_index); 494 return impl->cpu_manager.GetCoreManager(core_index);
486} 495}
487 496
488const Cpu& System::CpuCore(std::size_t core_index) const { 497const CoreManager& System::GetCoreManager(std::size_t core_index) const {
489 ASSERT(core_index < NUM_CPU_CORES); 498 ASSERT(core_index < NUM_CPU_CORES);
490 return impl->cpu_core_manager.GetCore(core_index); 499 return impl->cpu_manager.GetCoreManager(core_index);
491} 500}
492 501
493ExclusiveMonitor& System::Monitor() { 502ExclusiveMonitor& System::Monitor() {
494 return impl->cpu_core_manager.GetExclusiveMonitor(); 503 return impl->kernel.GetExclusiveMonitor();
495} 504}
496 505
497const ExclusiveMonitor& System::Monitor() const { 506const ExclusiveMonitor& System::Monitor() const {
498 return impl->cpu_core_manager.GetExclusiveMonitor(); 507 return impl->kernel.GetExclusiveMonitor();
499} 508}
500 509
501Memory::Memory& System::Memory() { 510Memory::Memory& System::Memory() {
diff --git a/src/core/core.h b/src/core/core.h
index e240c5c58..e69d68fcf 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -93,7 +93,7 @@ class Memory;
93namespace Core { 93namespace Core {
94 94
95class ARM_Interface; 95class ARM_Interface;
96class Cpu; 96class CoreManager;
97class ExclusiveMonitor; 97class ExclusiveMonitor;
98class FrameLimiter; 98class FrameLimiter;
99class PerfStats; 99class PerfStats;
@@ -218,10 +218,10 @@ public:
218 const ARM_Interface& ArmInterface(std::size_t core_index) const; 218 const ARM_Interface& ArmInterface(std::size_t core_index) const;
219 219
220 /// Gets a CPU interface to the CPU core with the specified index 220 /// Gets a CPU interface to the CPU core with the specified index
221 Cpu& CpuCore(std::size_t core_index); 221 CoreManager& GetCoreManager(std::size_t core_index);
222 222
223 /// Gets a CPU interface to the CPU core with the specified index 223 /// Gets a CPU interface to the CPU core with the specified index
224 const Cpu& CpuCore(std::size_t core_index) const; 224 const CoreManager& GetCoreManager(std::size_t core_index) const;
225 225
226 /// Gets a reference to the exclusive monitor 226 /// Gets a reference to the exclusive monitor
227 ExclusiveMonitor& Monitor(); 227 ExclusiveMonitor& Monitor();
@@ -364,10 +364,10 @@ private:
364 System(); 364 System();
365 365
366 /// Returns the currently running CPU core 366 /// Returns the currently running CPU core
367 Cpu& CurrentCpuCore(); 367 CoreManager& CurrentCoreManager();
368 368
369 /// Returns the currently running CPU core 369 /// Returns the currently running CPU core
370 const Cpu& CurrentCpuCore() const; 370 const CoreManager& CurrentCoreManager() const;
371 371
372 /** 372 /**
373 * Initialize the emulated system. 373 * Initialize the emulated system.
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp
deleted file mode 100644
index 630cd4feb..000000000
--- a/src/core/core_cpu.cpp
+++ /dev/null
@@ -1,127 +0,0 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <condition_variable>
6#include <mutex>
7
8#include "common/logging/log.h"
9#ifdef ARCHITECTURE_x86_64
10#include "core/arm/dynarmic/arm_dynarmic.h"
11#endif
12#include "core/arm/exclusive_monitor.h"
13#include "core/arm/unicorn/arm_unicorn.h"
14#include "core/core.h"
15#include "core/core_cpu.h"
16#include "core/core_timing.h"
17#include "core/hle/kernel/scheduler.h"
18#include "core/hle/kernel/thread.h"
19#include "core/hle/lock.h"
20#include "core/settings.h"
21
22namespace Core {
23
24void CpuBarrier::NotifyEnd() {
25 std::unique_lock lock{mutex};
26 end = true;
27 condition.notify_all();
28}
29
30bool CpuBarrier::Rendezvous() {
31 if (!Settings::values.use_multi_core) {
32 // Meaningless when running in single-core mode
33 return true;
34 }
35
36 if (!end) {
37 std::unique_lock lock{mutex};
38
39 --cores_waiting;
40 if (!cores_waiting) {
41 cores_waiting = NUM_CPU_CORES;
42 condition.notify_all();
43 return true;
44 }
45
46 condition.wait(lock);
47 return true;
48 }
49
50 return false;
51}
52
53Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier,
54 std::size_t core_index)
55 : cpu_barrier{cpu_barrier}, global_scheduler{system.GlobalScheduler()},
56 core_timing{system.CoreTiming()}, core_index{core_index} {
57#ifdef ARCHITECTURE_x86_64
58 arm_interface = std::make_unique<ARM_Dynarmic>(system, exclusive_monitor, core_index);
59#else
60 arm_interface = std::make_unique<ARM_Unicorn>(system);
61 LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
62#endif
63
64 scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface, core_index);
65}
66
67Cpu::~Cpu() = default;
68
69std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(
70 [[maybe_unused]] Memory::Memory& memory, [[maybe_unused]] std::size_t num_cores) {
71#ifdef ARCHITECTURE_x86_64
72 return std::make_unique<DynarmicExclusiveMonitor>(memory, num_cores);
73#else
74 // TODO(merry): Passthrough exclusive monitor
75 return nullptr;
76#endif
77}
78
79void Cpu::RunLoop(bool tight_loop) {
80 // Wait for all other CPU cores to complete the previous slice, such that they run in lock-step
81 if (!cpu_barrier.Rendezvous()) {
82 // If rendezvous failed, session has been killed
83 return;
84 }
85
86 Reschedule();
87
88 // If we don't have a currently active thread then don't execute instructions,
89 // instead advance to the next event and try to yield to the next thread
90 if (Kernel::GetCurrentThread() == nullptr) {
91 LOG_TRACE(Core, "Core-{} idling", core_index);
92 core_timing.Idle();
93 } else {
94 if (tight_loop) {
95 arm_interface->Run();
96 } else {
97 arm_interface->Step();
98 }
99 // We are stopping a run, exclusive state must be cleared
100 arm_interface->ClearExclusiveState();
101 }
102 core_timing.Advance();
103
104 Reschedule();
105}
106
107void Cpu::SingleStep() {
108 return RunLoop(false);
109}
110
111void Cpu::PrepareReschedule() {
112 arm_interface->PrepareReschedule();
113}
114
115void Cpu::Reschedule() {
116 // Lock the global kernel mutex when we manipulate the HLE state
117 std::lock_guard lock(HLE::g_hle_lock);
118
119 global_scheduler.SelectThread(core_index);
120 scheduler->TryDoContextSwitch();
121}
122
123void Cpu::Shutdown() {
124 scheduler->Shutdown();
125}
126
127} // namespace Core
diff --git a/src/core/core_cpu.h b/src/core/core_cpu.h
deleted file mode 100644
index 78f5021a2..000000000
--- a/src/core/core_cpu.h
+++ /dev/null
@@ -1,120 +0,0 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <atomic>
8#include <condition_variable>
9#include <cstddef>
10#include <memory>
11#include <mutex>
12#include "common/common_types.h"
13
14namespace Kernel {
15class GlobalScheduler;
16class Scheduler;
17} // namespace Kernel
18
19namespace Core {
20class System;
21}
22
23namespace Core::Timing {
24class CoreTiming;
25}
26
27namespace Memory {
28class Memory;
29}
30
31namespace Core {
32
33class ARM_Interface;
34class ExclusiveMonitor;
35
36constexpr unsigned NUM_CPU_CORES{4};
37
38class CpuBarrier {
39public:
40 bool IsAlive() const {
41 return !end;
42 }
43
44 void NotifyEnd();
45
46 bool Rendezvous();
47
48private:
49 unsigned cores_waiting{NUM_CPU_CORES};
50 std::mutex mutex;
51 std::condition_variable condition;
52 std::atomic<bool> end{};
53};
54
55class Cpu {
56public:
57 Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier,
58 std::size_t core_index);
59 ~Cpu();
60
61 void RunLoop(bool tight_loop = true);
62
63 void SingleStep();
64
65 void PrepareReschedule();
66
67 ARM_Interface& ArmInterface() {
68 return *arm_interface;
69 }
70
71 const ARM_Interface& ArmInterface() const {
72 return *arm_interface;
73 }
74
75 Kernel::Scheduler& Scheduler() {
76 return *scheduler;
77 }
78
79 const Kernel::Scheduler& Scheduler() const {
80 return *scheduler;
81 }
82
83 bool IsMainCore() const {
84 return core_index == 0;
85 }
86
87 std::size_t CoreIndex() const {
88 return core_index;
89 }
90
91 void Shutdown();
92
93 /**
94 * Creates an exclusive monitor to handle exclusive reads/writes.
95 *
96 * @param memory The current memory subsystem that the monitor may wish
97 * to keep track of.
98 *
99 * @param num_cores The number of cores to assume about the CPU.
100 *
101 * @returns The constructed exclusive monitor instance, or nullptr if the current
102 * CPU backend is unable to use an exclusive monitor.
103 */
104 static std::unique_ptr<ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory,
105 std::size_t num_cores);
106
107private:
108 void Reschedule();
109
110 std::unique_ptr<ARM_Interface> arm_interface;
111 CpuBarrier& cpu_barrier;
112 Kernel::GlobalScheduler& global_scheduler;
113 std::unique_ptr<Kernel::Scheduler> scheduler;
114 Timing::CoreTiming& core_timing;
115
116 std::atomic<bool> reschedule_pending = false;
117 std::size_t core_index;
118};
119
120} // namespace Core
diff --git a/src/core/core_manager.cpp b/src/core/core_manager.cpp
new file mode 100644
index 000000000..8eacf92dd
--- /dev/null
+++ b/src/core/core_manager.cpp
@@ -0,0 +1,70 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <condition_variable>
6#include <mutex>
7
8#include "common/logging/log.h"
9#ifdef ARCHITECTURE_x86_64
10#include "core/arm/dynarmic/arm_dynarmic.h"
11#endif
12#include "core/arm/exclusive_monitor.h"
13#include "core/arm/unicorn/arm_unicorn.h"
14#include "core/core.h"
15#include "core/core_manager.h"
16#include "core/core_timing.h"
17#include "core/hle/kernel/kernel.h"
18#include "core/hle/kernel/physical_core.h"
19#include "core/hle/kernel/scheduler.h"
20#include "core/hle/kernel/thread.h"
21#include "core/hle/lock.h"
22#include "core/settings.h"
23
24namespace Core {
25
26CoreManager::CoreManager(System& system, std::size_t core_index)
27 : global_scheduler{system.GlobalScheduler()}, physical_core{system.Kernel().PhysicalCore(
28 core_index)},
29 core_timing{system.CoreTiming()}, core_index{core_index} {}
30
31CoreManager::~CoreManager() = default;
32
33void CoreManager::RunLoop(bool tight_loop) {
34 Reschedule();
35
36 // If we don't have a currently active thread then don't execute instructions,
37 // instead advance to the next event and try to yield to the next thread
38 if (Kernel::GetCurrentThread() == nullptr) {
39 LOG_TRACE(Core, "Core-{} idling", core_index);
40 core_timing.Idle();
41 } else {
42 if (tight_loop) {
43 physical_core.Run();
44 } else {
45 physical_core.Step();
46 }
47 }
48 core_timing.Advance();
49
50 Reschedule();
51}
52
53void CoreManager::SingleStep() {
54 return RunLoop(false);
55}
56
57void CoreManager::PrepareReschedule() {
58 physical_core.Stop();
59}
60
61void CoreManager::Reschedule() {
62 // Lock the global kernel mutex when we manipulate the HLE state
63 std::lock_guard lock(HLE::g_hle_lock);
64
65 global_scheduler.SelectThread(core_index);
66
67 physical_core.Scheduler().TryDoContextSwitch();
68}
69
70} // namespace Core
diff --git a/src/core/core_manager.h b/src/core/core_manager.h
new file mode 100644
index 000000000..b14e723d7
--- /dev/null
+++ b/src/core/core_manager.h
@@ -0,0 +1,63 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <atomic>
8#include <cstddef>
9#include <memory>
10#include "common/common_types.h"
11
12namespace Kernel {
13class GlobalScheduler;
14class PhysicalCore;
15} // namespace Kernel
16
17namespace Core {
18class System;
19}
20
21namespace Core::Timing {
22class CoreTiming;
23}
24
25namespace Memory {
26class Memory;
27}
28
29namespace Core {
30
31constexpr unsigned NUM_CPU_CORES{4};
32
33class CoreManager {
34public:
35 CoreManager(System& system, std::size_t core_index);
36 ~CoreManager();
37
38 void RunLoop(bool tight_loop = true);
39
40 void SingleStep();
41
42 void PrepareReschedule();
43
44 bool IsMainCore() const {
45 return core_index == 0;
46 }
47
48 std::size_t CoreIndex() const {
49 return core_index;
50 }
51
52private:
53 void Reschedule();
54
55 Kernel::GlobalScheduler& global_scheduler;
56 Kernel::PhysicalCore& physical_core;
57 Timing::CoreTiming& core_timing;
58
59 std::atomic<bool> reschedule_pending = false;
60 std::size_t core_index;
61};
62
63} // namespace Core
diff --git a/src/core/cpu_core_manager.cpp b/src/core/cpu_core_manager.cpp
deleted file mode 100644
index f04a34133..000000000
--- a/src/core/cpu_core_manager.cpp
+++ /dev/null
@@ -1,152 +0,0 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "core/arm/exclusive_monitor.h"
7#include "core/core.h"
8#include "core/core_cpu.h"
9#include "core/core_timing.h"
10#include "core/cpu_core_manager.h"
11#include "core/gdbstub/gdbstub.h"
12#include "core/settings.h"
13
14namespace Core {
15namespace {
16void RunCpuCore(const System& system, Cpu& cpu_state) {
17 while (system.IsPoweredOn()) {
18 cpu_state.RunLoop(true);
19 }
20}
21} // Anonymous namespace
22
23CpuCoreManager::CpuCoreManager(System& system) : system{system} {}
24CpuCoreManager::~CpuCoreManager() = default;
25
26void CpuCoreManager::Initialize() {
27 barrier = std::make_unique<CpuBarrier>();
28 exclusive_monitor = Cpu::MakeExclusiveMonitor(system.Memory(), cores.size());
29
30 for (std::size_t index = 0; index < cores.size(); ++index) {
31 cores[index] = std::make_unique<Cpu>(system, *exclusive_monitor, *barrier, index);
32 }
33}
34
35void CpuCoreManager::StartThreads() {
36 // Create threads for CPU cores 1-3, and build thread_to_cpu map
37 // CPU core 0 is run on the main thread
38 thread_to_cpu[std::this_thread::get_id()] = cores[0].get();
39 if (!Settings::values.use_multi_core) {
40 return;
41 }
42
43 for (std::size_t index = 0; index < core_threads.size(); ++index) {
44 core_threads[index] = std::make_unique<std::thread>(RunCpuCore, std::cref(system),
45 std::ref(*cores[index + 1]));
46 thread_to_cpu[core_threads[index]->get_id()] = cores[index + 1].get();
47 }
48}
49
50void CpuCoreManager::Shutdown() {
51 barrier->NotifyEnd();
52 if (Settings::values.use_multi_core) {
53 for (auto& thread : core_threads) {
54 thread->join();
55 thread.reset();
56 }
57 }
58
59 thread_to_cpu.clear();
60 for (auto& cpu_core : cores) {
61 cpu_core->Shutdown();
62 cpu_core.reset();
63 }
64
65 exclusive_monitor.reset();
66 barrier.reset();
67}
68
69Cpu& CpuCoreManager::GetCore(std::size_t index) {
70 return *cores.at(index);
71}
72
73const Cpu& CpuCoreManager::GetCore(std::size_t index) const {
74 return *cores.at(index);
75}
76
77ExclusiveMonitor& CpuCoreManager::GetExclusiveMonitor() {
78 return *exclusive_monitor;
79}
80
81const ExclusiveMonitor& CpuCoreManager::GetExclusiveMonitor() const {
82 return *exclusive_monitor;
83}
84
85Cpu& CpuCoreManager::GetCurrentCore() {
86 if (Settings::values.use_multi_core) {
87 const auto& search = thread_to_cpu.find(std::this_thread::get_id());
88 ASSERT(search != thread_to_cpu.end());
89 ASSERT(search->second);
90 return *search->second;
91 }
92
93 // Otherwise, use single-threaded mode active_core variable
94 return *cores[active_core];
95}
96
97const Cpu& CpuCoreManager::GetCurrentCore() const {
98 if (Settings::values.use_multi_core) {
99 const auto& search = thread_to_cpu.find(std::this_thread::get_id());
100 ASSERT(search != thread_to_cpu.end());
101 ASSERT(search->second);
102 return *search->second;
103 }
104
105 // Otherwise, use single-threaded mode active_core variable
106 return *cores[active_core];
107}
108
109void CpuCoreManager::RunLoop(bool tight_loop) {
110 // Update thread_to_cpu in case Core 0 is run from a different host thread
111 thread_to_cpu[std::this_thread::get_id()] = cores[0].get();
112
113 if (GDBStub::IsServerEnabled()) {
114 GDBStub::HandlePacket();
115
116 // If the loop is halted and we want to step, use a tiny (1) number of instructions to
117 // execute. Otherwise, get out of the loop function.
118 if (GDBStub::GetCpuHaltFlag()) {
119 if (GDBStub::GetCpuStepFlag()) {
120 tight_loop = false;
121 } else {
122 return;
123 }
124 }
125 }
126
127 auto& core_timing = system.CoreTiming();
128 core_timing.ResetRun();
129 bool keep_running{};
130 do {
131 keep_running = false;
132 for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
133 core_timing.SwitchContext(active_core);
134 if (core_timing.CanCurrentContextRun()) {
135 cores[active_core]->RunLoop(tight_loop);
136 }
137 keep_running |= core_timing.CanCurrentContextRun();
138 }
139 } while (keep_running);
140
141 if (GDBStub::IsServerEnabled()) {
142 GDBStub::SetCpuStepFlag(false);
143 }
144}
145
146void CpuCoreManager::InvalidateAllInstructionCaches() {
147 for (auto& cpu : cores) {
148 cpu->ArmInterface().ClearInstructionCache();
149 }
150}
151
152} // namespace Core
diff --git a/src/core/cpu_core_manager.h b/src/core/cpu_core_manager.h
deleted file mode 100644
index 2cbbf8216..000000000
--- a/src/core/cpu_core_manager.h
+++ /dev/null
@@ -1,62 +0,0 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <map>
9#include <memory>
10#include <thread>
11
12namespace Core {
13
14class Cpu;
15class CpuBarrier;
16class ExclusiveMonitor;
17class System;
18
19class CpuCoreManager {
20public:
21 explicit CpuCoreManager(System& system);
22 CpuCoreManager(const CpuCoreManager&) = delete;
23 CpuCoreManager(CpuCoreManager&&) = delete;
24
25 ~CpuCoreManager();
26
27 CpuCoreManager& operator=(const CpuCoreManager&) = delete;
28 CpuCoreManager& operator=(CpuCoreManager&&) = delete;
29
30 void Initialize();
31 void StartThreads();
32 void Shutdown();
33
34 Cpu& GetCore(std::size_t index);
35 const Cpu& GetCore(std::size_t index) const;
36
37 Cpu& GetCurrentCore();
38 const Cpu& GetCurrentCore() const;
39
40 ExclusiveMonitor& GetExclusiveMonitor();
41 const ExclusiveMonitor& GetExclusiveMonitor() const;
42
43 void RunLoop(bool tight_loop);
44
45 void InvalidateAllInstructionCaches();
46
47private:
48 static constexpr std::size_t NUM_CPU_CORES = 4;
49
50 std::unique_ptr<ExclusiveMonitor> exclusive_monitor;
51 std::unique_ptr<CpuBarrier> barrier;
52 std::array<std::unique_ptr<Cpu>, NUM_CPU_CORES> cores;
53 std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> core_threads;
54 std::size_t active_core{}; ///< Active core, only used in single thread mode
55
56 /// Map of guest threads to CPU cores
57 std::map<std::thread::id, Cpu*> thread_to_cpu;
58
59 System& system;
60};
61
62} // namespace Core
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
new file mode 100644
index 000000000..70ddbdcca
--- /dev/null
+++ b/src/core/cpu_manager.cpp
@@ -0,0 +1,81 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/arm/exclusive_monitor.h"
6#include "core/core.h"
7#include "core/core_manager.h"
8#include "core/core_timing.h"
9#include "core/cpu_manager.h"
10#include "core/gdbstub/gdbstub.h"
11
12namespace Core {
13
14CpuManager::CpuManager(System& system) : system{system} {}
15CpuManager::~CpuManager() = default;
16
17void CpuManager::Initialize() {
18 for (std::size_t index = 0; index < core_managers.size(); ++index) {
19 core_managers[index] = std::make_unique<CoreManager>(system, index);
20 }
21}
22
23void CpuManager::Shutdown() {
24 for (auto& cpu_core : core_managers) {
25 cpu_core.reset();
26 }
27}
28
29CoreManager& CpuManager::GetCoreManager(std::size_t index) {
30 return *core_managers.at(index);
31}
32
33const CoreManager& CpuManager::GetCoreManager(std::size_t index) const {
34 return *core_managers.at(index);
35}
36
37CoreManager& CpuManager::GetCurrentCoreManager() {
38 // Otherwise, use single-threaded mode active_core variable
39 return *core_managers[active_core];
40}
41
42const CoreManager& CpuManager::GetCurrentCoreManager() const {
43 // Otherwise, use single-threaded mode active_core variable
44 return *core_managers[active_core];
45}
46
47void CpuManager::RunLoop(bool tight_loop) {
48 if (GDBStub::IsServerEnabled()) {
49 GDBStub::HandlePacket();
50
51 // If the loop is halted and we want to step, use a tiny (1) number of instructions to
52 // execute. Otherwise, get out of the loop function.
53 if (GDBStub::GetCpuHaltFlag()) {
54 if (GDBStub::GetCpuStepFlag()) {
55 tight_loop = false;
56 } else {
57 return;
58 }
59 }
60 }
61
62 auto& core_timing = system.CoreTiming();
63 core_timing.ResetRun();
64 bool keep_running{};
65 do {
66 keep_running = false;
67 for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
68 core_timing.SwitchContext(active_core);
69 if (core_timing.CanCurrentContextRun()) {
70 core_managers[active_core]->RunLoop(tight_loop);
71 }
72 keep_running |= core_timing.CanCurrentContextRun();
73 }
74 } while (keep_running);
75
76 if (GDBStub::IsServerEnabled()) {
77 GDBStub::SetCpuStepFlag(false);
78 }
79}
80
81} // namespace Core
diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h
new file mode 100644
index 000000000..feb619e1b
--- /dev/null
+++ b/src/core/cpu_manager.h
@@ -0,0 +1,50 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <memory>
9
10namespace Core {
11
12class CoreManager;
13class System;
14
15class CpuManager {
16public:
17 explicit CpuManager(System& system);
18 CpuManager(const CpuManager&) = delete;
19 CpuManager(CpuManager&&) = delete;
20
21 ~CpuManager();
22
23 CpuManager& operator=(const CpuManager&) = delete;
24 CpuManager& operator=(CpuManager&&) = delete;
25
26 void Initialize();
27 void Shutdown();
28
29 CoreManager& GetCoreManager(std::size_t index);
30 const CoreManager& GetCoreManager(std::size_t index) const;
31
32 CoreManager& GetCurrentCoreManager();
33 const CoreManager& GetCurrentCoreManager() const;
34
35 std::size_t GetActiveCoreIndex() const {
36 return active_core;
37 }
38
39 void RunLoop(bool tight_loop);
40
41private:
42 static constexpr std::size_t NUM_CPU_CORES = 4;
43
44 std::array<std::unique_ptr<CoreManager>, NUM_CPU_CORES> core_managers;
45 std::size_t active_core{}; ///< Active core, only used in single thread mode
46
47 System& system;
48};
49
50} // namespace Core
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/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index 37cb28848..67e95999d 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -35,7 +35,7 @@
35#include "common/swap.h" 35#include "common/swap.h"
36#include "core/arm/arm_interface.h" 36#include "core/arm/arm_interface.h"
37#include "core/core.h" 37#include "core/core.h"
38#include "core/core_cpu.h" 38#include "core/core_manager.h"
39#include "core/gdbstub/gdbstub.h" 39#include "core/gdbstub/gdbstub.h"
40#include "core/hle/kernel/process.h" 40#include "core/hle/kernel/process.h"
41#include "core/hle/kernel/scheduler.h" 41#include "core/hle/kernel/scheduler.h"
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index db189c8e3..2ea3dcb61 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -8,7 +8,6 @@
8#include "common/assert.h" 8#include "common/assert.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/core.h" 10#include "core/core.h"
11#include "core/core_cpu.h"
12#include "core/hle/kernel/address_arbiter.h" 11#include "core/hle/kernel/address_arbiter.h"
13#include "core/hle/kernel/errors.h" 12#include "core/hle/kernel/errors.h"
14#include "core/hle/kernel/scheduler.h" 13#include "core/hle/kernel/scheduler.h"
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 1d0783bd3..edd4c4259 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -3,13 +3,15 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <atomic> 5#include <atomic>
6#include <functional>
6#include <memory> 7#include <memory>
7#include <mutex> 8#include <mutex>
8#include <utility> 9#include <utility>
9 10
10#include "common/assert.h" 11#include "common/assert.h"
11#include "common/logging/log.h" 12#include "common/logging/log.h"
12 13#include "core/arm/arm_interface.h"
14#include "core/arm/exclusive_monitor.h"
13#include "core/core.h" 15#include "core/core.h"
14#include "core/core_timing.h" 16#include "core/core_timing.h"
15#include "core/core_timing_util.h" 17#include "core/core_timing_util.h"
@@ -17,6 +19,7 @@
17#include "core/hle/kernel/errors.h" 19#include "core/hle/kernel/errors.h"
18#include "core/hle/kernel/handle_table.h" 20#include "core/hle/kernel/handle_table.h"
19#include "core/hle/kernel/kernel.h" 21#include "core/hle/kernel/kernel.h"
22#include "core/hle/kernel/physical_core.h"
20#include "core/hle/kernel/process.h" 23#include "core/hle/kernel/process.h"
21#include "core/hle/kernel/resource_limit.h" 24#include "core/hle/kernel/resource_limit.h"
22#include "core/hle/kernel/scheduler.h" 25#include "core/hle/kernel/scheduler.h"
@@ -98,6 +101,7 @@ struct KernelCore::Impl {
98 void Initialize(KernelCore& kernel) { 101 void Initialize(KernelCore& kernel) {
99 Shutdown(); 102 Shutdown();
100 103
104 InitializePhysicalCores();
101 InitializeSystemResourceLimit(kernel); 105 InitializeSystemResourceLimit(kernel);
102 InitializeThreads(); 106 InitializeThreads();
103 InitializePreemption(); 107 InitializePreemption();
@@ -121,6 +125,21 @@ struct KernelCore::Impl {
121 global_scheduler.Shutdown(); 125 global_scheduler.Shutdown();
122 126
123 named_ports.clear(); 127 named_ports.clear();
128
129 for (auto& core : cores) {
130 core.Shutdown();
131 }
132 cores.clear();
133
134 exclusive_monitor.reset();
135 }
136
137 void InitializePhysicalCores() {
138 exclusive_monitor =
139 Core::MakeExclusiveMonitor(system.Memory(), global_scheduler.CpuCoresCount());
140 for (std::size_t i = 0; i < global_scheduler.CpuCoresCount(); i++) {
141 cores.emplace_back(system, i, *exclusive_monitor);
142 }
124 } 143 }
125 144
126 // Creates the default system resource limit 145 // Creates the default system resource limit
@@ -186,6 +205,9 @@ struct KernelCore::Impl {
186 /// the ConnectToPort SVC. 205 /// the ConnectToPort SVC.
187 NamedPortTable named_ports; 206 NamedPortTable named_ports;
188 207
208 std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;
209 std::vector<Kernel::PhysicalCore> cores;
210
189 // System context 211 // System context
190 Core::System& system; 212 Core::System& system;
191}; 213};
@@ -240,6 +262,34 @@ const Kernel::GlobalScheduler& KernelCore::GlobalScheduler() const {
240 return impl->global_scheduler; 262 return impl->global_scheduler;
241} 263}
242 264
265Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) {
266 return impl->cores[id];
267}
268
269const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const {
270 return impl->cores[id];
271}
272
273Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() {
274 return *impl->exclusive_monitor;
275}
276
277const Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() const {
278 return *impl->exclusive_monitor;
279}
280
281void KernelCore::InvalidateAllInstructionCaches() {
282 for (std::size_t i = 0; i < impl->global_scheduler.CpuCoresCount(); i++) {
283 PhysicalCore(i).ArmInterface().ClearInstructionCache();
284 }
285}
286
287void KernelCore::PrepareReschedule(std::size_t id) {
288 if (id < impl->global_scheduler.CpuCoresCount()) {
289 impl->cores[id].Stop();
290 }
291}
292
243void KernelCore::AddNamedPort(std::string name, std::shared_ptr<ClientPort> port) { 293void KernelCore::AddNamedPort(std::string name, std::shared_ptr<ClientPort> port) {
244 impl->named_ports.emplace(std::move(name), std::move(port)); 294 impl->named_ports.emplace(std::move(name), std::move(port));
245} 295}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 3bf0068ed..fccffaf3a 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -11,8 +11,9 @@
11#include "core/hle/kernel/object.h" 11#include "core/hle/kernel/object.h"
12 12
13namespace Core { 13namespace Core {
14class ExclusiveMonitor;
14class System; 15class System;
15} 16} // namespace Core
16 17
17namespace Core::Timing { 18namespace Core::Timing {
18class CoreTiming; 19class CoreTiming;
@@ -25,6 +26,7 @@ class AddressArbiter;
25class ClientPort; 26class ClientPort;
26class GlobalScheduler; 27class GlobalScheduler;
27class HandleTable; 28class HandleTable;
29class PhysicalCore;
28class Process; 30class Process;
29class ResourceLimit; 31class ResourceLimit;
30class Thread; 32class Thread;
@@ -84,6 +86,21 @@ public:
84 /// Gets the sole instance of the global scheduler 86 /// Gets the sole instance of the global scheduler
85 const Kernel::GlobalScheduler& GlobalScheduler() const; 87 const Kernel::GlobalScheduler& GlobalScheduler() const;
86 88
89 /// Gets the an instance of the respective physical CPU core.
90 Kernel::PhysicalCore& PhysicalCore(std::size_t id);
91
92 /// Gets the an instance of the respective physical CPU core.
93 const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const;
94
95 /// Stops execution of 'id' core, in order to reschedule a new thread.
96 void PrepareReschedule(std::size_t id);
97
98 Core::ExclusiveMonitor& GetExclusiveMonitor();
99
100 const Core::ExclusiveMonitor& GetExclusiveMonitor() const;
101
102 void InvalidateAllInstructionCaches();
103
87 /// Adds a port to the named port table 104 /// Adds a port to the named port table
88 void AddNamedPort(std::string name, std::shared_ptr<ClientPort> port); 105 void AddNamedPort(std::string name, std::shared_ptr<ClientPort> port);
89 106
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp
new file mode 100644
index 000000000..9303dd273
--- /dev/null
+++ b/src/core/hle/kernel/physical_core.cpp
@@ -0,0 +1,51 @@
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 "common/logging/log.h"
6#include "core/arm/arm_interface.h"
7#ifdef ARCHITECTURE_x86_64
8#include "core/arm/dynarmic/arm_dynarmic.h"
9#endif
10#include "core/arm/exclusive_monitor.h"
11#include "core/arm/unicorn/arm_unicorn.h"
12#include "core/core.h"
13#include "core/hle/kernel/physical_core.h"
14#include "core/hle/kernel/scheduler.h"
15#include "core/hle/kernel/thread.h"
16
17namespace Kernel {
18
19PhysicalCore::PhysicalCore(Core::System& system, std::size_t id,
20 Core::ExclusiveMonitor& exclusive_monitor)
21 : core_index{id} {
22#ifdef ARCHITECTURE_x86_64
23 arm_interface = std::make_unique<Core::ARM_Dynarmic>(system, exclusive_monitor, core_index);
24#else
25 arm_interface = std::make_shared<Core::ARM_Unicorn>(system);
26 LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
27#endif
28
29 scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface, core_index);
30}
31
32PhysicalCore::~PhysicalCore() = default;
33
34void PhysicalCore::Run() {
35 arm_interface->Run();
36 arm_interface->ClearExclusiveState();
37}
38
39void PhysicalCore::Step() {
40 arm_interface->Step();
41}
42
43void PhysicalCore::Stop() {
44 arm_interface->PrepareReschedule();
45}
46
47void PhysicalCore::Shutdown() {
48 scheduler->Shutdown();
49}
50
51} // namespace Kernel
diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h
new file mode 100644
index 000000000..4c32c0f1b
--- /dev/null
+++ b/src/core/hle/kernel/physical_core.h
@@ -0,0 +1,77 @@
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 <cstddef>
8#include <memory>
9
10namespace Kernel {
11class Scheduler;
12} // namespace Kernel
13
14namespace Core {
15class ARM_Interface;
16class ExclusiveMonitor;
17class System;
18} // namespace Core
19
20namespace Kernel {
21
22class PhysicalCore {
23public:
24 PhysicalCore(Core::System& system, std::size_t id, Core::ExclusiveMonitor& exclusive_monitor);
25 ~PhysicalCore();
26
27 PhysicalCore(const PhysicalCore&) = delete;
28 PhysicalCore& operator=(const PhysicalCore&) = delete;
29
30 PhysicalCore(PhysicalCore&&) = default;
31 PhysicalCore& operator=(PhysicalCore&&) = default;
32
33 /// Execute current jit state
34 void Run();
35 /// Execute a single instruction in current jit.
36 void Step();
37 /// Stop JIT execution/exit
38 void Stop();
39
40 // Shutdown this physical core.
41 void Shutdown();
42
43 Core::ARM_Interface& ArmInterface() {
44 return *arm_interface;
45 }
46
47 const Core::ARM_Interface& ArmInterface() const {
48 return *arm_interface;
49 }
50
51 bool IsMainCore() const {
52 return core_index == 0;
53 }
54
55 bool IsSystemCore() const {
56 return core_index == 3;
57 }
58
59 std::size_t CoreIndex() const {
60 return core_index;
61 }
62
63 Kernel::Scheduler& Scheduler() {
64 return *scheduler;
65 }
66
67 const Kernel::Scheduler& Scheduler() const {
68 return *scheduler;
69 }
70
71private:
72 std::size_t core_index;
73 std::unique_ptr<Core::ARM_Interface> arm_interface;
74 std::unique_ptr<Kernel::Scheduler> scheduler;
75};
76
77} // namespace Kernel
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index d36fcd7d9..eb196a690 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -14,7 +14,6 @@
14#include "common/logging/log.h" 14#include "common/logging/log.h"
15#include "core/arm/arm_interface.h" 15#include "core/arm/arm_interface.h"
16#include "core/core.h" 16#include "core/core.h"
17#include "core/core_cpu.h"
18#include "core/core_timing.h" 17#include "core/core_timing.h"
19#include "core/hle/kernel/kernel.h" 18#include "core/hle/kernel/kernel.h"
20#include "core/hle/kernel/process.h" 19#include "core/hle/kernel/process.h"
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index dbcdb0b88..1d99bf7a2 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -15,7 +15,7 @@
15#include "common/string_util.h" 15#include "common/string_util.h"
16#include "core/arm/exclusive_monitor.h" 16#include "core/arm/exclusive_monitor.h"
17#include "core/core.h" 17#include "core/core.h"
18#include "core/core_cpu.h" 18#include "core/core_manager.h"
19#include "core/core_timing.h" 19#include "core/core_timing.h"
20#include "core/core_timing_util.h" 20#include "core/core_timing_util.h"
21#include "core/hle/kernel/address_arbiter.h" 21#include "core/hle/kernel/address_arbiter.h"
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index e84e5ce0d..e965b5b04 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -13,7 +13,6 @@
13#include "common/thread_queue_list.h" 13#include "common/thread_queue_list.h"
14#include "core/arm/arm_interface.h" 14#include "core/arm/arm_interface.h"
15#include "core/core.h" 15#include "core/core.h"
16#include "core/core_cpu.h"
17#include "core/core_timing.h" 16#include "core/core_timing.h"
18#include "core/core_timing_util.h" 17#include "core/core_timing_util.h"
19#include "core/hle/kernel/errors.h" 18#include "core/hle/kernel/errors.h"
@@ -356,7 +355,7 @@ void Thread::SetActivity(ThreadActivity value) {
356 // Set status if not waiting 355 // Set status if not waiting
357 if (status == ThreadStatus::Ready || status == ThreadStatus::Running) { 356 if (status == ThreadStatus::Ready || status == ThreadStatus::Running) {
358 SetStatus(ThreadStatus::Paused); 357 SetStatus(ThreadStatus::Paused);
359 Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule(); 358 kernel.PrepareReschedule(processor_id);
360 } 359 }
361 } else if (status == ThreadStatus::Paused) { 360 } else if (status == ThreadStatus::Paused) {
362 // Ready to reschedule 361 // Ready to reschedule
diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp
index 745f2c4e8..a0c806e8f 100644
--- a/src/core/hle/kernel/wait_object.cpp
+++ b/src/core/hle/kernel/wait_object.cpp
@@ -7,7 +7,6 @@
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "core/core.h" 9#include "core/core.h"
10#include "core/core_cpu.h"
11#include "core/hle/kernel/kernel.h" 10#include "core/hle/kernel/kernel.h"
12#include "core/hle/kernel/object.h" 11#include "core/hle/kernel/object.h"
13#include "core/hle/kernel/process.h" 12#include "core/hle/kernel/process.h"
@@ -96,7 +95,7 @@ void WaitObject::WakeupWaitingThread(std::shared_ptr<Thread> thread) {
96 } 95 }
97 if (resume) { 96 if (resume) {
98 thread->ResumeFromWait(); 97 thread->ResumeFromWait();
99 Core::System::GetInstance().PrepareReschedule(thread->GetProcessorID()); 98 kernel.PrepareReschedule(thread->GetProcessorID());
100 } 99 }
101} 100}
102 101
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/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index 884ad173b..f67fab2f9 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -42,6 +42,26 @@ void BSD::Socket(Kernel::HLERequestContext& ctx) {
42 rb.Push<u32>(0); // bsd errno 42 rb.Push<u32>(0); // bsd errno
43} 43}
44 44
45void BSD::Select(Kernel::HLERequestContext& ctx) {
46 LOG_WARNING(Service, "(STUBBED) called");
47
48 IPC::ResponseBuilder rb{ctx, 4};
49
50 rb.Push(RESULT_SUCCESS);
51 rb.Push<u32>(0); // ret
52 rb.Push<u32>(0); // bsd errno
53}
54
55void BSD::Bind(Kernel::HLERequestContext& ctx) {
56 LOG_WARNING(Service, "(STUBBED) called");
57
58 IPC::ResponseBuilder rb{ctx, 4};
59
60 rb.Push(RESULT_SUCCESS);
61 rb.Push<u32>(0); // ret
62 rb.Push<u32>(0); // bsd errno
63}
64
45void BSD::Connect(Kernel::HLERequestContext& ctx) { 65void BSD::Connect(Kernel::HLERequestContext& ctx) {
46 LOG_WARNING(Service, "(STUBBED) called"); 66 LOG_WARNING(Service, "(STUBBED) called");
47 67
@@ -52,6 +72,26 @@ void BSD::Connect(Kernel::HLERequestContext& ctx) {
52 rb.Push<u32>(0); // bsd errno 72 rb.Push<u32>(0); // bsd errno
53} 73}
54 74
75void BSD::Listen(Kernel::HLERequestContext& ctx) {
76 LOG_WARNING(Service, "(STUBBED) called");
77
78 IPC::ResponseBuilder rb{ctx, 4};
79
80 rb.Push(RESULT_SUCCESS);
81 rb.Push<u32>(0); // ret
82 rb.Push<u32>(0); // bsd errno
83}
84
85void BSD::SetSockOpt(Kernel::HLERequestContext& ctx) {
86 LOG_WARNING(Service, "(STUBBED) called");
87
88 IPC::ResponseBuilder rb{ctx, 4};
89
90 rb.Push(RESULT_SUCCESS);
91 rb.Push<u32>(0); // ret
92 rb.Push<u32>(0); // bsd errno
93}
94
55void BSD::SendTo(Kernel::HLERequestContext& ctx) { 95void BSD::SendTo(Kernel::HLERequestContext& ctx) {
56 LOG_WARNING(Service, "(STUBBED) called"); 96 LOG_WARNING(Service, "(STUBBED) called");
57 97
@@ -80,7 +120,7 @@ BSD::BSD(const char* name) : ServiceFramework(name) {
80 {2, &BSD::Socket, "Socket"}, 120 {2, &BSD::Socket, "Socket"},
81 {3, nullptr, "SocketExempt"}, 121 {3, nullptr, "SocketExempt"},
82 {4, nullptr, "Open"}, 122 {4, nullptr, "Open"},
83 {5, nullptr, "Select"}, 123 {5, &BSD::Select, "Select"},
84 {6, nullptr, "Poll"}, 124 {6, nullptr, "Poll"},
85 {7, nullptr, "Sysctl"}, 125 {7, nullptr, "Sysctl"},
86 {8, nullptr, "Recv"}, 126 {8, nullptr, "Recv"},
@@ -88,15 +128,15 @@ BSD::BSD(const char* name) : ServiceFramework(name) {
88 {10, nullptr, "Send"}, 128 {10, nullptr, "Send"},
89 {11, &BSD::SendTo, "SendTo"}, 129 {11, &BSD::SendTo, "SendTo"},
90 {12, nullptr, "Accept"}, 130 {12, nullptr, "Accept"},
91 {13, nullptr, "Bind"}, 131 {13, &BSD::Bind, "Bind"},
92 {14, &BSD::Connect, "Connect"}, 132 {14, &BSD::Connect, "Connect"},
93 {15, nullptr, "GetPeerName"}, 133 {15, nullptr, "GetPeerName"},
94 {16, nullptr, "GetSockName"}, 134 {16, nullptr, "GetSockName"},
95 {17, nullptr, "GetSockOpt"}, 135 {17, nullptr, "GetSockOpt"},
96 {18, nullptr, "Listen"}, 136 {18, &BSD::Listen, "Listen"},
97 {19, nullptr, "Ioctl"}, 137 {19, nullptr, "Ioctl"},
98 {20, nullptr, "Fcntl"}, 138 {20, nullptr, "Fcntl"},
99 {21, nullptr, "SetSockOpt"}, 139 {21, &BSD::SetSockOpt, "SetSockOpt"},
100 {22, nullptr, "Shutdown"}, 140 {22, nullptr, "Shutdown"},
101 {23, nullptr, "ShutdownAllSockets"}, 141 {23, nullptr, "ShutdownAllSockets"},
102 {24, nullptr, "Write"}, 142 {24, nullptr, "Write"},
diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h
index 0fe0e65c6..3098e3baf 100644
--- a/src/core/hle/service/sockets/bsd.h
+++ b/src/core/hle/service/sockets/bsd.h
@@ -18,7 +18,11 @@ private:
18 void RegisterClient(Kernel::HLERequestContext& ctx); 18 void RegisterClient(Kernel::HLERequestContext& ctx);
19 void StartMonitoring(Kernel::HLERequestContext& ctx); 19 void StartMonitoring(Kernel::HLERequestContext& ctx);
20 void Socket(Kernel::HLERequestContext& ctx); 20 void Socket(Kernel::HLERequestContext& ctx);
21 void Select(Kernel::HLERequestContext& ctx);
22 void Bind(Kernel::HLERequestContext& ctx);
21 void Connect(Kernel::HLERequestContext& ctx); 23 void Connect(Kernel::HLERequestContext& ctx);
24 void Listen(Kernel::HLERequestContext& ctx);
25 void SetSockOpt(Kernel::HLERequestContext& ctx);
22 void SendTo(Kernel::HLERequestContext& ctx); 26 void SendTo(Kernel::HLERequestContext& ctx);
23 void Close(Kernel::HLERequestContext& ctx); 27 void Close(Kernel::HLERequestContext& ctx);
24 28
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 ab5c8e622..81b6d9eff 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -227,6 +227,28 @@ enum class AtomicOp : u64 {
227 Exch = 8, 227 Exch = 8,
228}; 228};
229 229
230enum class GlobalAtomicOp : u64 {
231 Add = 0,
232 Min = 1,
233 Max = 2,
234 Inc = 3,
235 Dec = 4,
236 And = 5,
237 Or = 6,
238 Xor = 7,
239 Exch = 8,
240 SafeAdd = 10,
241};
242
243enum class GlobalAtomicType : u64 {
244 U32 = 0,
245 S32 = 1,
246 U64 = 2,
247 F32_FTZ_RN = 3,
248 F16x2_FTZ_RN = 4,
249 S64 = 5,
250};
251
230enum class UniformType : u64 { 252enum class UniformType : u64 {
231 UnsignedByte = 0, 253 UnsignedByte = 0,
232 SignedByte = 1, 254 SignedByte = 1,
@@ -958,6 +980,12 @@ union Instruction {
958 } stg; 980 } stg;
959 981
960 union { 982 union {
983 BitField<52, 4, GlobalAtomicOp> operation;
984 BitField<49, 3, GlobalAtomicType> type;
985 BitField<28, 20, s64> offset;
986 } atom;
987
988 union {
961 BitField<52, 4, AtomicOp> operation; 989 BitField<52, 4, AtomicOp> operation;
962 BitField<28, 2, AtomicType> type; 990 BitField<28, 2, AtomicType> type;
963 BitField<30, 22, s64> offset; 991 BitField<30, 22, s64> offset;
@@ -1096,6 +1124,11 @@ union Instruction {
1096 } fset; 1124 } fset;
1097 1125
1098 union { 1126 union {
1127 BitField<47, 1, u64> ftz;
1128 BitField<48, 4, PredCondition> cond;
1129 } fcmp;
1130
1131 union {
1099 BitField<49, 1, u64> bf; 1132 BitField<49, 1, u64> bf;
1100 BitField<35, 3, PredCondition> cond; 1133 BitField<35, 3, PredCondition> cond;
1101 BitField<50, 1, u64> ftz; 1134 BitField<50, 1, u64> ftz;
@@ -1691,6 +1724,7 @@ public:
1691 ST_S, 1724 ST_S,
1692 ST, // Store in generic memory 1725 ST, // Store in generic memory
1693 STG, // Store in global memory 1726 STG, // Store in global memory
1727 ATOM, // Atomic operation on global memory
1694 ATOMS, // Atomic operation on shared memory 1728 ATOMS, // Atomic operation on shared memory
1695 AL2P, // Transforms attribute memory into physical memory 1729 AL2P, // Transforms attribute memory into physical memory
1696 TEX, 1730 TEX,
@@ -1772,6 +1806,7 @@ public:
1772 ICMP_R, 1806 ICMP_R,
1773 ICMP_CR, 1807 ICMP_CR,
1774 ICMP_IMM, 1808 ICMP_IMM,
1809 FCMP_R,
1775 MUFU, // Multi-Function Operator 1810 MUFU, // Multi-Function Operator
1776 RRO_C, // Range Reduction Operator 1811 RRO_C, // Range Reduction Operator
1777 RRO_R, 1812 RRO_R,
@@ -1995,6 +2030,7 @@ private:
1995 INST("1110111101010---", Id::ST_L, Type::Memory, "ST_L"), 2030 INST("1110111101010---", Id::ST_L, Type::Memory, "ST_L"),
1996 INST("101-------------", Id::ST, Type::Memory, "ST"), 2031 INST("101-------------", Id::ST, Type::Memory, "ST"),
1997 INST("1110111011011---", Id::STG, Type::Memory, "STG"), 2032 INST("1110111011011---", Id::STG, Type::Memory, "STG"),
2033 INST("11101101--------", Id::ATOM, Type::Memory, "ATOM"),
1998 INST("11101100--------", Id::ATOMS, Type::Memory, "ATOMS"), 2034 INST("11101100--------", Id::ATOMS, Type::Memory, "ATOMS"),
1999 INST("1110111110100---", Id::AL2P, Type::Memory, "AL2P"), 2035 INST("1110111110100---", Id::AL2P, Type::Memory, "AL2P"),
2000 INST("110000----111---", Id::TEX, Type::Texture, "TEX"), 2036 INST("110000----111---", Id::TEX, Type::Texture, "TEX"),
@@ -2075,6 +2111,7 @@ private:
2075 INST("0101110100100---", Id::HSETP2_R, Type::HalfSetPredicate, "HSETP2_R"), 2111 INST("0101110100100---", Id::HSETP2_R, Type::HalfSetPredicate, "HSETP2_R"),
2076 INST("0111111-0-------", Id::HSETP2_IMM, Type::HalfSetPredicate, "HSETP2_IMM"), 2112 INST("0111111-0-------", Id::HSETP2_IMM, Type::HalfSetPredicate, "HSETP2_IMM"),
2077 INST("0101110100011---", Id::HSET2_R, Type::HalfSet, "HSET2_R"), 2113 INST("0101110100011---", Id::HSET2_R, Type::HalfSet, "HSET2_R"),
2114 INST("010110111010----", Id::FCMP_R, Type::Arithmetic, "FCMP_R"),
2078 INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"), 2115 INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"),
2079 INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"), 2116 INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"),
2080 INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"), 2117 INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"),
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/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..362942e09 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
@@ -942,8 +946,15 @@ void RasterizerOpenGL::SetupDrawTextures(std::size_t stage_index, const Shader&
942 u32 binding = device.GetBaseBindings(stage_index).sampler; 946 u32 binding = device.GetBaseBindings(stage_index).sampler;
943 for (const auto& entry : shader->GetShaderEntries().samplers) { 947 for (const auto& entry : shader->GetShaderEntries().samplers) {
944 const auto shader_type = static_cast<Tegra::Engines::ShaderType>(stage_index); 948 const auto shader_type = static_cast<Tegra::Engines::ShaderType>(stage_index);
945 const auto texture = GetTextureInfo(maxwell3d, entry, shader_type); 949 if (!entry.IsIndexed()) {
946 SetupTexture(binding++, texture, entry); 950 const auto texture = GetTextureInfo(maxwell3d, entry, shader_type);
951 SetupTexture(binding++, texture, entry);
952 } else {
953 for (std::size_t i = 0; i < entry.Size(); ++i) {
954 const auto texture = GetTextureInfo(maxwell3d, entry, shader_type, i);
955 SetupTexture(binding++, texture, entry);
956 }
957 }
947 } 958 }
948} 959}
949 960
@@ -952,8 +963,17 @@ void RasterizerOpenGL::SetupComputeTextures(const Shader& kernel) {
952 const auto& compute = system.GPU().KeplerCompute(); 963 const auto& compute = system.GPU().KeplerCompute();
953 u32 binding = 0; 964 u32 binding = 0;
954 for (const auto& entry : kernel->GetShaderEntries().samplers) { 965 for (const auto& entry : kernel->GetShaderEntries().samplers) {
955 const auto texture = GetTextureInfo(compute, entry, Tegra::Engines::ShaderType::Compute); 966 if (!entry.IsIndexed()) {
956 SetupTexture(binding++, texture, entry); 967 const auto texture =
968 GetTextureInfo(compute, entry, Tegra::Engines::ShaderType::Compute);
969 SetupTexture(binding++, texture, entry);
970 } else {
971 for (std::size_t i = 0; i < entry.Size(); ++i) {
972 const auto texture =
973 GetTextureInfo(compute, entry, Tegra::Engines::ShaderType::Compute, i);
974 SetupTexture(binding++, texture, entry);
975 }
976 }
957 } 977 }
958} 978}
959 979
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 2996aaf08..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) {
@@ -1019,7 +1040,6 @@ private:
1019 } 1040 }
1020 return {{"gl_ViewportIndex", Type::Int}}; 1041 return {{"gl_ViewportIndex", Type::Int}};
1021 case 3: 1042 case 3:
1022 UNIMPLEMENTED_MSG("Requires some state changes for gl_PointSize to work in shader");
1023 return {{"gl_PointSize", Type::Float}}; 1043 return {{"gl_PointSize", Type::Float}};
1024 } 1044 }
1025 return {}; 1045 return {};
@@ -1099,7 +1119,11 @@ private:
1099 } else if (!meta->ptp.empty()) { 1119 } else if (!meta->ptp.empty()) {
1100 expr += "Offsets"; 1120 expr += "Offsets";
1101 } 1121 }
1102 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 }
1103 expr += coord_constructors.at(count + (has_array ? 1 : 0) + 1127 expr += coord_constructors.at(count + (has_array ? 1 : 0) +
1104 (has_shadow && !separate_dc ? 1 : 0) - 1); 1128 (has_shadow && !separate_dc ? 1 : 0) - 1);
1105 expr += '('; 1129 expr += '(';
@@ -1311,6 +1335,8 @@ private:
1311 const std::string final_offset = fmt::format("({} - {}) >> 2", real, base); 1335 const std::string final_offset = fmt::format("({} - {}) >> 2", real, base);
1312 target = {fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset), 1336 target = {fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset),
1313 Type::Uint}; 1337 Type::Uint};
1338 } else if (const auto cv = std::get_if<CustomVarNode>(&*dest)) {
1339 target = {GetCustomVariable(cv->GetIndex()), Type::Float};
1314 } else { 1340 } else {
1315 UNREACHABLE_MSG("Assign called without a proper target"); 1341 UNREACHABLE_MSG("Assign called without a proper target");
1316 } 1342 }
@@ -1858,10 +1884,7 @@ private:
1858 1884
1859 template <const std::string_view& opname, Type type> 1885 template <const std::string_view& opname, Type type>
1860 Expression Atomic(Operation operation) { 1886 Expression Atomic(Operation operation) {
1861 ASSERT(stage == ShaderType::Compute); 1887 return {fmt::format("atomic{}({}, {})", opname, Visit(operation[0]).GetCode(),
1862 auto& smem = std::get<SmemNode>(*operation[0]);
1863
1864 return {fmt::format("atomic{}(smem[{} >> 2], {})", opname, Visit(smem.GetAddress()).AsInt(),
1865 Visit(operation[1]).As(type)), 1888 Visit(operation[1]).As(type)),
1866 type}; 1889 type};
1867 } 1890 }
@@ -2241,6 +2264,10 @@ private:
2241 return GetDeclarationWithSuffix(index, "gpr"); 2264 return GetDeclarationWithSuffix(index, "gpr");
2242 } 2265 }
2243 2266
2267 std::string GetCustomVariable(u32 index) const {
2268 return GetDeclarationWithSuffix(index, "custom_var");
2269 }
2270
2244 std::string GetPredicate(Tegra::Shader::Pred pred) const { 2271 std::string GetPredicate(Tegra::Shader::Pred pred) const {
2245 return GetDeclarationWithSuffix(static_cast<u32>(pred), "pred"); 2272 return GetDeclarationWithSuffix(static_cast<u32>(pred), "pred");
2246 } 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/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index e95eb069e..d4b81cd87 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -176,6 +176,19 @@ GLint GetSwizzleSource(SwizzleSource source) {
176 return GL_NONE; 176 return GL_NONE;
177} 177}
178 178
179GLenum GetComponent(PixelFormat format, bool is_first) {
180 switch (format) {
181 case PixelFormat::Z24S8:
182 case PixelFormat::Z32FS8:
183 return is_first ? GL_DEPTH_COMPONENT : GL_STENCIL_INDEX;
184 case PixelFormat::S8Z24:
185 return is_first ? GL_STENCIL_INDEX : GL_DEPTH_COMPONENT;
186 default:
187 UNREACHABLE();
188 return GL_DEPTH_COMPONENT;
189 }
190}
191
179void ApplyTextureDefaults(const SurfaceParams& params, GLuint texture) { 192void ApplyTextureDefaults(const SurfaceParams& params, GLuint texture) {
180 if (params.IsBuffer()) { 193 if (params.IsBuffer()) {
181 return; 194 return;
@@ -184,7 +197,7 @@ void ApplyTextureDefaults(const SurfaceParams& params, GLuint texture) {
184 glTextureParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 197 glTextureParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
185 glTextureParameteri(texture, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 198 glTextureParameteri(texture, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
186 glTextureParameteri(texture, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 199 glTextureParameteri(texture, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
187 glTextureParameteri(texture, GL_TEXTURE_MAX_LEVEL, params.num_levels - 1); 200 glTextureParameteri(texture, GL_TEXTURE_MAX_LEVEL, static_cast<GLint>(params.num_levels - 1));
188 if (params.num_levels == 1) { 201 if (params.num_levels == 1) {
189 glTextureParameterf(texture, GL_TEXTURE_LOD_BIAS, 1000.0f); 202 glTextureParameterf(texture, GL_TEXTURE_LOD_BIAS, 1000.0f);
190 } 203 }
@@ -416,11 +429,21 @@ void CachedSurfaceView::ApplySwizzle(SwizzleSource x_source, SwizzleSource y_sou
416 if (new_swizzle == swizzle) 429 if (new_swizzle == swizzle)
417 return; 430 return;
418 swizzle = new_swizzle; 431 swizzle = new_swizzle;
419 const std::array<GLint, 4> gl_swizzle = {GetSwizzleSource(x_source), GetSwizzleSource(y_source), 432 const std::array gl_swizzle = {GetSwizzleSource(x_source), GetSwizzleSource(y_source),
420 GetSwizzleSource(z_source), 433 GetSwizzleSource(z_source), GetSwizzleSource(w_source)};
421 GetSwizzleSource(w_source)};
422 const GLuint handle = GetTexture(); 434 const GLuint handle = GetTexture();
423 glTextureParameteriv(handle, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle.data()); 435 const PixelFormat format = surface.GetSurfaceParams().pixel_format;
436 switch (format) {
437 case PixelFormat::Z24S8:
438 case PixelFormat::Z32FS8:
439 case PixelFormat::S8Z24:
440 glTextureParameteri(handle, GL_DEPTH_STENCIL_TEXTURE_MODE,
441 GetComponent(format, x_source == SwizzleSource::R));
442 break;
443 default:
444 glTextureParameteriv(handle, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle.data());
445 break;
446 }
424} 447}
425 448
426OGLTextureView CachedSurfaceView::CreateTextureView() const { 449OGLTextureView CachedSurfaceView::CreateTextureView() const {
@@ -529,8 +552,11 @@ void TextureCacheOpenGL::ImageBlit(View& src_view, View& dst_view,
529 const Common::Rectangle<u32>& dst_rect = copy_config.dst_rect; 552 const Common::Rectangle<u32>& dst_rect = copy_config.dst_rect;
530 const bool is_linear = copy_config.filter == Tegra::Engines::Fermi2D::Filter::Linear; 553 const bool is_linear = copy_config.filter == Tegra::Engines::Fermi2D::Filter::Linear;
531 554
532 glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom, dst_rect.left, 555 glBlitFramebuffer(static_cast<GLint>(src_rect.left), static_cast<GLint>(src_rect.top),
533 dst_rect.top, dst_rect.right, dst_rect.bottom, buffers, 556 static_cast<GLint>(src_rect.right), static_cast<GLint>(src_rect.bottom),
557 static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.top),
558 static_cast<GLint>(dst_rect.right), static_cast<GLint>(dst_rect.bottom),
559 buffers,
534 is_linear && (buffers == GL_COLOR_BUFFER_BIT) ? GL_LINEAR : GL_NEAREST); 560 is_linear && (buffers == GL_COLOR_BUFFER_BIT) ? GL_LINEAR : GL_NEAREST);
535} 561}
536 562
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_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
index b53078721..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 }
@@ -1123,15 +1138,7 @@ private:
1123 } 1138 }
1124 1139
1125 if (const auto gmem = std::get_if<GmemNode>(&*node)) { 1140 if (const auto gmem = std::get_if<GmemNode>(&*node)) {
1126 const Id gmem_buffer = global_buffers.at(gmem->GetDescriptor()); 1141 return {OpLoad(t_uint, GetGlobalMemoryPointer(*gmem)), Type::Uint};
1127 const Id real = AsUint(Visit(gmem->GetRealAddress()));
1128 const Id base = AsUint(Visit(gmem->GetBaseAddress()));
1129
1130 Id offset = OpISub(t_uint, real, base);
1131 offset = OpUDiv(t_uint, offset, Constant(t_uint, 4U));
1132 return {OpLoad(t_float,
1133 OpAccessChain(t_gmem_float, gmem_buffer, Constant(t_uint, 0U), offset)),
1134 Type::Float};
1135 } 1142 }
1136 1143
1137 if (const auto lmem = std::get_if<LmemNode>(&*node)) { 1144 if (const auto lmem = std::get_if<LmemNode>(&*node)) {
@@ -1142,10 +1149,7 @@ private:
1142 } 1149 }
1143 1150
1144 if (const auto smem = std::get_if<SmemNode>(&*node)) { 1151 if (const auto smem = std::get_if<SmemNode>(&*node)) {
1145 Id address = AsUint(Visit(smem->GetAddress())); 1152 return {OpLoad(t_uint, GetSharedMemoryPointer(*smem)), Type::Uint};
1146 address = OpShiftRightLogical(t_uint, address, Constant(t_uint, 2U));
1147 const Id pointer = OpAccessChain(t_smem_uint, shared_memory, address);
1148 return {OpLoad(t_uint, pointer), Type::Uint};
1149 } 1153 }
1150 1154
1151 if (const auto internal_flag = std::get_if<InternalFlagNode>(&*node)) { 1155 if (const auto internal_flag = std::get_if<InternalFlagNode>(&*node)) {
@@ -1339,20 +1343,13 @@ private:
1339 target = {OpAccessChain(t_prv_float, local_memory, address), Type::Float}; 1343 target = {OpAccessChain(t_prv_float, local_memory, address), Type::Float};
1340 1344
1341 } else if (const auto smem = std::get_if<SmemNode>(&*dest)) { 1345 } else if (const auto smem = std::get_if<SmemNode>(&*dest)) {
1342 ASSERT(stage == ShaderType::Compute); 1346 target = {GetSharedMemoryPointer(*smem), Type::Uint};
1343 Id address = AsUint(Visit(smem->GetAddress()));
1344 address = OpShiftRightLogical(t_uint, address, Constant(t_uint, 2U));
1345 target = {OpAccessChain(t_smem_uint, shared_memory, address), Type::Uint};
1346 1347
1347 } else if (const auto gmem = std::get_if<GmemNode>(&*dest)) { 1348 } else if (const auto gmem = std::get_if<GmemNode>(&*dest)) {
1348 const Id real = AsUint(Visit(gmem->GetRealAddress())); 1349 target = {GetGlobalMemoryPointer(*gmem), Type::Uint};
1349 const Id base = AsUint(Visit(gmem->GetBaseAddress()));
1350 const Id diff = OpISub(t_uint, real, base);
1351 const Id offset = OpShiftRightLogical(t_uint, diff, Constant(t_uint, 2));
1352 1350
1353 const Id gmem_buffer = global_buffers.at(gmem->GetDescriptor()); 1351 } else if (const auto cv = std::get_if<CustomVarNode>(&*dest)) {
1354 target = {OpAccessChain(t_gmem_float, gmem_buffer, Constant(t_uint, 0), offset), 1352 target = {custom_variables.at(cv->GetIndex()), Type::Float};
1355 Type::Float};
1356 1353
1357 } else { 1354 } else {
1358 UNIMPLEMENTED(); 1355 UNIMPLEMENTED();
@@ -1804,11 +1801,16 @@ private:
1804 return {}; 1801 return {};
1805 } 1802 }
1806 1803
1807 Expression UAtomicAdd(Operation operation) { 1804 Expression AtomicAdd(Operation operation) {
1808 const auto& smem = std::get<SmemNode>(*operation[0]); 1805 Id pointer;
1809 Id address = AsUint(Visit(smem.GetAddress())); 1806 if (const auto smem = std::get_if<SmemNode>(&*operation[0])) {
1810 address = OpShiftRightLogical(t_uint, address, Constant(t_uint, 2U)); 1807 pointer = GetSharedMemoryPointer(*smem);
1811 const Id pointer = OpAccessChain(t_smem_uint, shared_memory, address); 1808 } else if (const auto gmem = std::get_if<GmemNode>(&*operation[0])) {
1809 pointer = GetGlobalMemoryPointer(*gmem);
1810 } else {
1811 UNREACHABLE();
1812 return {Constant(t_uint, 0), Type::Uint};
1813 }
1812 1814
1813 const Id scope = Constant(t_uint, static_cast<u32>(spv::Scope::Device)); 1815 const Id scope = Constant(t_uint, static_cast<u32>(spv::Scope::Device));
1814 const Id semantics = Constant(t_uint, 0U); 1816 const Id semantics = Constant(t_uint, 0U);
@@ -2243,6 +2245,22 @@ private:
2243 return {}; 2245 return {};
2244 } 2246 }
2245 2247
2248 Id GetGlobalMemoryPointer(const GmemNode& gmem) {
2249 const Id real = AsUint(Visit(gmem.GetRealAddress()));
2250 const Id base = AsUint(Visit(gmem.GetBaseAddress()));
2251 const Id diff = OpISub(t_uint, real, base);
2252 const Id offset = OpShiftRightLogical(t_uint, diff, Constant(t_uint, 2));
2253 const Id buffer = global_buffers.at(gmem.GetDescriptor());
2254 return OpAccessChain(t_gmem_uint, buffer, Constant(t_uint, 0), offset);
2255 }
2256
2257 Id GetSharedMemoryPointer(const SmemNode& smem) {
2258 ASSERT(stage == ShaderType::Compute);
2259 Id address = AsUint(Visit(smem.GetAddress()));
2260 address = OpShiftRightLogical(t_uint, address, Constant(t_uint, 2U));
2261 return OpAccessChain(t_smem_uint, shared_memory, address);
2262 }
2263
2246 static constexpr std::array operation_decompilers = { 2264 static constexpr std::array operation_decompilers = {
2247 &SPIRVDecompiler::Assign, 2265 &SPIRVDecompiler::Assign,
2248 2266
@@ -2389,7 +2407,7 @@ private:
2389 &SPIRVDecompiler::AtomicImageXor, 2407 &SPIRVDecompiler::AtomicImageXor,
2390 &SPIRVDecompiler::AtomicImageExchange, 2408 &SPIRVDecompiler::AtomicImageExchange,
2391 2409
2392 &SPIRVDecompiler::UAtomicAdd, 2410 &SPIRVDecompiler::AtomicAdd,
2393 2411
2394 &SPIRVDecompiler::Branch, 2412 &SPIRVDecompiler::Branch,
2395 &SPIRVDecompiler::BranchIndirect, 2413 &SPIRVDecompiler::BranchIndirect,
@@ -2485,9 +2503,9 @@ private:
2485 2503
2486 Id t_smem_uint{}; 2504 Id t_smem_uint{};
2487 2505
2488 const Id t_gmem_float = TypePointer(spv::StorageClass::StorageBuffer, t_float); 2506 const Id t_gmem_uint = TypePointer(spv::StorageClass::StorageBuffer, t_uint);
2489 const Id t_gmem_array = 2507 const Id t_gmem_array =
2490 Name(Decorate(TypeRuntimeArray(t_float), spv::Decoration::ArrayStride, 4U), "GmemArray"); 2508 Name(Decorate(TypeRuntimeArray(t_uint), spv::Decoration::ArrayStride, 4U), "GmemArray");
2491 const Id t_gmem_struct = MemberDecorate( 2509 const Id t_gmem_struct = MemberDecorate(
2492 Decorate(TypeStruct(t_gmem_array), spv::Decoration::Block), 0, spv::Decoration::Offset, 0); 2510 Decorate(TypeStruct(t_gmem_array), spv::Decoration::Block), 0, spv::Decoration::Offset, 0);
2493 const Id t_gmem_ssbo = TypePointer(spv::StorageClass::StorageBuffer, t_gmem_struct); 2511 const Id t_gmem_ssbo = TypePointer(spv::StorageClass::StorageBuffer, t_gmem_struct);
@@ -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/memory.cpp b/src/video_core/shader/decode/memory.cpp
index 7591a715f..b5fbc4d58 100644
--- a/src/video_core/shader/decode/memory.cpp
+++ b/src/video_core/shader/decode/memory.cpp
@@ -19,9 +19,12 @@ namespace VideoCommon::Shader {
19using Tegra::Shader::AtomicOp; 19using Tegra::Shader::AtomicOp;
20using Tegra::Shader::AtomicType; 20using Tegra::Shader::AtomicType;
21using Tegra::Shader::Attribute; 21using Tegra::Shader::Attribute;
22using Tegra::Shader::GlobalAtomicOp;
23using Tegra::Shader::GlobalAtomicType;
22using Tegra::Shader::Instruction; 24using Tegra::Shader::Instruction;
23using Tegra::Shader::OpCode; 25using Tegra::Shader::OpCode;
24using Tegra::Shader::Register; 26using Tegra::Shader::Register;
27using Tegra::Shader::StoreType;
25 28
26namespace { 29namespace {
27 30
@@ -61,6 +64,27 @@ u32 GetMemorySize(Tegra::Shader::UniformType uniform_type) {
61 } 64 }
62} 65}
63 66
67Node ExtractUnaligned(Node value, Node address, u32 mask, u32 size) {
68 Node offset = Operation(OperationCode::UBitwiseAnd, address, Immediate(mask));
69 offset = Operation(OperationCode::ULogicalShiftLeft, std::move(offset), Immediate(3));
70 return Operation(OperationCode::UBitfieldExtract, std::move(value), std::move(offset),
71 Immediate(size));
72}
73
74Node InsertUnaligned(Node dest, Node value, Node address, u32 mask, u32 size) {
75 Node offset = Operation(OperationCode::UBitwiseAnd, std::move(address), Immediate(mask));
76 offset = Operation(OperationCode::ULogicalShiftLeft, std::move(offset), Immediate(3));
77 return Operation(OperationCode::UBitfieldInsert, std::move(dest), std::move(value),
78 std::move(offset), Immediate(size));
79}
80
81Node Sign16Extend(Node value) {
82 Node sign = Operation(OperationCode::UBitwiseAnd, value, Immediate(1U << 15));
83 Node is_sign = Operation(OperationCode::LogicalUEqual, std::move(sign), Immediate(1U << 15));
84 Node extend = Operation(OperationCode::Select, is_sign, Immediate(0xFFFF0000), Immediate(0));
85 return Operation(OperationCode::UBitwiseOr, std::move(value), std::move(extend));
86}
87
64} // Anonymous namespace 88} // Anonymous namespace
65 89
66u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { 90u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
@@ -136,26 +160,31 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
136 LOG_DEBUG(HW_GPU, "LD_L cache management mode: {}", static_cast<u64>(instr.ld_l.unknown)); 160 LOG_DEBUG(HW_GPU, "LD_L cache management mode: {}", static_cast<u64>(instr.ld_l.unknown));
137 [[fallthrough]]; 161 [[fallthrough]];
138 case OpCode::Id::LD_S: { 162 case OpCode::Id::LD_S: {
139 const auto GetMemory = [&](s32 offset) { 163 const auto GetAddress = [&](s32 offset) {
140 ASSERT(offset % 4 == 0); 164 ASSERT(offset % 4 == 0);
141 const Node immediate_offset = Immediate(static_cast<s32>(instr.smem_imm) + offset); 165 const Node immediate_offset = Immediate(static_cast<s32>(instr.smem_imm) + offset);
142 const Node address = Operation(OperationCode::IAdd, NO_PRECISE, GetRegister(instr.gpr8), 166 return Operation(OperationCode::IAdd, GetRegister(instr.gpr8), immediate_offset);
143 immediate_offset); 167 };
144 return opcode->get().GetId() == OpCode::Id::LD_S ? GetSharedMemory(address) 168 const auto GetMemory = [&](s32 offset) {
145 : GetLocalMemory(address); 169 return opcode->get().GetId() == OpCode::Id::LD_S ? GetSharedMemory(GetAddress(offset))
170 : GetLocalMemory(GetAddress(offset));
146 }; 171 };
147 172
148 switch (instr.ldst_sl.type.Value()) { 173 switch (instr.ldst_sl.type.Value()) {
149 case Tegra::Shader::StoreType::Bits32: 174 case StoreType::Signed16:
150 case Tegra::Shader::StoreType::Bits64: 175 SetRegister(bb, instr.gpr0,
151 case Tegra::Shader::StoreType::Bits128: { 176 Sign16Extend(ExtractUnaligned(GetMemory(0), GetAddress(0), 0b10, 16)));
152 const u32 count = [&]() { 177 break;
178 case StoreType::Bits32:
179 case StoreType::Bits64:
180 case StoreType::Bits128: {
181 const u32 count = [&] {
153 switch (instr.ldst_sl.type.Value()) { 182 switch (instr.ldst_sl.type.Value()) {
154 case Tegra::Shader::StoreType::Bits32: 183 case StoreType::Bits32:
155 return 1; 184 return 1;
156 case Tegra::Shader::StoreType::Bits64: 185 case StoreType::Bits64:
157 return 2; 186 return 2;
158 case Tegra::Shader::StoreType::Bits128: 187 case StoreType::Bits128:
159 return 4; 188 return 4;
160 default: 189 default:
161 UNREACHABLE(); 190 UNREACHABLE();
@@ -212,12 +241,7 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
212 // To handle unaligned loads get the bytes used to dereference global memory and extract 241 // To handle unaligned loads get the bytes used to dereference global memory and extract
213 // those bytes from the loaded u32. 242 // those bytes from the loaded u32.
214 if (IsUnaligned(type)) { 243 if (IsUnaligned(type)) {
215 Node mask = Immediate(GetUnalignedMask(type)); 244 gmem = ExtractUnaligned(gmem, real_address, GetUnalignedMask(type), size);
216 Node offset = Operation(OperationCode::UBitwiseAnd, real_address, std::move(mask));
217 offset = Operation(OperationCode::ULogicalShiftLeft, offset, Immediate(3));
218
219 gmem = Operation(OperationCode::UBitfieldExtract, std::move(gmem),
220 std::move(offset), Immediate(size));
221 } 245 }
222 246
223 SetTemporary(bb, i, gmem); 247 SetTemporary(bb, i, gmem);
@@ -269,21 +293,28 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
269 return Operation(OperationCode::IAdd, NO_PRECISE, GetRegister(instr.gpr8), immediate); 293 return Operation(OperationCode::IAdd, NO_PRECISE, GetRegister(instr.gpr8), immediate);
270 }; 294 };
271 295
272 const auto set_memory = opcode->get().GetId() == OpCode::Id::ST_L 296 const bool is_local = opcode->get().GetId() == OpCode::Id::ST_L;
273 ? &ShaderIR::SetLocalMemory 297 const auto set_memory = is_local ? &ShaderIR::SetLocalMemory : &ShaderIR::SetSharedMemory;
274 : &ShaderIR::SetSharedMemory; 298 const auto get_memory = is_local ? &ShaderIR::GetLocalMemory : &ShaderIR::GetSharedMemory;
275 299
276 switch (instr.ldst_sl.type.Value()) { 300 switch (instr.ldst_sl.type.Value()) {
277 case Tegra::Shader::StoreType::Bits128: 301 case StoreType::Bits128:
278 (this->*set_memory)(bb, GetAddress(12), GetRegister(instr.gpr0.Value() + 3)); 302 (this->*set_memory)(bb, GetAddress(12), GetRegister(instr.gpr0.Value() + 3));
279 (this->*set_memory)(bb, GetAddress(8), GetRegister(instr.gpr0.Value() + 2)); 303 (this->*set_memory)(bb, GetAddress(8), GetRegister(instr.gpr0.Value() + 2));
280 [[fallthrough]]; 304 [[fallthrough]];
281 case Tegra::Shader::StoreType::Bits64: 305 case StoreType::Bits64:
282 (this->*set_memory)(bb, GetAddress(4), GetRegister(instr.gpr0.Value() + 1)); 306 (this->*set_memory)(bb, GetAddress(4), GetRegister(instr.gpr0.Value() + 1));
283 [[fallthrough]]; 307 [[fallthrough]];
284 case Tegra::Shader::StoreType::Bits32: 308 case StoreType::Bits32:
285 (this->*set_memory)(bb, GetAddress(0), GetRegister(instr.gpr0)); 309 (this->*set_memory)(bb, GetAddress(0), GetRegister(instr.gpr0));
286 break; 310 break;
311 case StoreType::Signed16: {
312 Node address = GetAddress(0);
313 Node memory = (this->*get_memory)(address);
314 (this->*set_memory)(
315 bb, address, InsertUnaligned(memory, GetRegister(instr.gpr0), address, 0b10, 16));
316 break;
317 }
287 default: 318 default:
288 UNIMPLEMENTED_MSG("{} unhandled type: {}", opcode->get().GetName(), 319 UNIMPLEMENTED_MSG("{} unhandled type: {}", opcode->get().GetName(),
289 static_cast<u32>(instr.ldst_sl.type.Value())); 320 static_cast<u32>(instr.ldst_sl.type.Value()));
@@ -323,18 +354,32 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
323 Node value = GetRegister(instr.gpr0.Value() + i); 354 Node value = GetRegister(instr.gpr0.Value() + i);
324 355
325 if (IsUnaligned(type)) { 356 if (IsUnaligned(type)) {
326 Node mask = Immediate(GetUnalignedMask(type)); 357 const u32 mask = GetUnalignedMask(type);
327 Node offset = Operation(OperationCode::UBitwiseAnd, real_address, std::move(mask)); 358 value = InsertUnaligned(gmem, std::move(value), real_address, mask, size);
328 offset = Operation(OperationCode::ULogicalShiftLeft, offset, Immediate(3));
329
330 value = Operation(OperationCode::UBitfieldInsert, gmem, std::move(value), offset,
331 Immediate(size));
332 } 359 }
333 360
334 bb.push_back(Operation(OperationCode::Assign, gmem, value)); 361 bb.push_back(Operation(OperationCode::Assign, gmem, value));
335 } 362 }
336 break; 363 break;
337 } 364 }
365 case OpCode::Id::ATOM: {
366 UNIMPLEMENTED_IF_MSG(instr.atom.operation != GlobalAtomicOp::Add, "operation={}",
367 static_cast<int>(instr.atom.operation.Value()));
368 UNIMPLEMENTED_IF_MSG(instr.atom.type != GlobalAtomicType::S32, "type={}",
369 static_cast<int>(instr.atom.type.Value()));
370
371 const auto [real_address, base_address, descriptor] =
372 TrackGlobalMemory(bb, instr, true, true);
373 if (!real_address || !base_address) {
374 // Tracking failed, skip atomic.
375 break;
376 }
377
378 Node gmem = MakeNode<GmemNode>(real_address, base_address, descriptor);
379 Node value = Operation(OperationCode::AtomicAdd, std::move(gmem), GetRegister(instr.gpr20));
380 SetRegister(bb, instr.gpr0, std::move(value));
381 break;
382 }
338 case OpCode::Id::ATOMS: { 383 case OpCode::Id::ATOMS: {
339 UNIMPLEMENTED_IF_MSG(instr.atoms.operation != AtomicOp::Add, "operation={}", 384 UNIMPLEMENTED_IF_MSG(instr.atoms.operation != AtomicOp::Add, "operation={}",
340 static_cast<int>(instr.atoms.operation.Value())); 385 static_cast<int>(instr.atoms.operation.Value()));
@@ -348,7 +393,7 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
348 Node memory = GetSharedMemory(std::move(address)); 393 Node memory = GetSharedMemory(std::move(address));
349 Node data = GetRegister(instr.gpr20); 394 Node data = GetRegister(instr.gpr20);
350 395
351 Node value = Operation(OperationCode::UAtomicAdd, std::move(memory), std::move(data)); 396 Node value = Operation(OperationCode::AtomicAdd, std::move(memory), std::move(data));
352 SetRegister(bb, instr.gpr0, std::move(value)); 397 SetRegister(bb, instr.gpr0, std::move(value));
353 break; 398 break;
354 } 399 }
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/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 075c7d07c..a0a7b9111 100644
--- a/src/video_core/shader/node.h
+++ b/src/video_core/shader/node.h
@@ -162,7 +162,7 @@ enum class OperationCode {
162 AtomicImageXor, /// (MetaImage, int[N] coords) -> void 162 AtomicImageXor, /// (MetaImage, int[N] coords) -> void
163 AtomicImageExchange, /// (MetaImage, int[N] coords) -> void 163 AtomicImageExchange, /// (MetaImage, int[N] coords) -> void
164 164
165 UAtomicAdd, /// (smem, uint) -> uint 165 AtomicAdd, /// (memory, {u}int) -> {u}int
166 166
167 Branch, /// (uint branch_target) -> void 167 Branch, /// (uint branch_target) -> void
168 BranchIndirect, /// (uint branch_target) -> void 168 BranchIndirect, /// (uint branch_target) -> void
@@ -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/texture_cache/surface_base.cpp b/src/video_core/texture_cache/surface_base.cpp
index 829268b4c..84469b7ba 100644
--- a/src/video_core/texture_cache/surface_base.cpp
+++ b/src/video_core/texture_cache/surface_base.cpp
@@ -135,7 +135,7 @@ std::vector<CopyParams> SurfaceBaseImpl::BreakDownLayered(const SurfaceParams& i
135 for (u32 level = 0; level < mipmaps; level++) { 135 for (u32 level = 0; level < mipmaps; level++) {
136 const u32 width = SurfaceParams::IntersectWidth(params, in_params, level, level); 136 const u32 width = SurfaceParams::IntersectWidth(params, in_params, level, level);
137 const u32 height = SurfaceParams::IntersectHeight(params, in_params, level, level); 137 const u32 height = SurfaceParams::IntersectHeight(params, in_params, level, level);
138 result.emplace_back(width, height, layer, level); 138 result.emplace_back(0, 0, layer, 0, 0, layer, level, level, width, height, 1);
139 } 139 }
140 } 140 }
141 return result; 141 return result;
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 59918847a..280d81ba9 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 =
@@ -1056,6 +1060,9 @@ void Config::SavePathValues() {
1056void Config::SaveRendererValues() { 1060void Config::SaveRendererValues() {
1057 qt_config->beginGroup(QStringLiteral("Renderer")); 1061 qt_config->beginGroup(QStringLiteral("Renderer"));
1058 1062
1063 WriteSetting(QStringLiteral("backend"), static_cast<int>(Settings::values.renderer_backend), 0);
1064 WriteSetting(QStringLiteral("debug"), Settings::values.renderer_debug, false);
1065 WriteSetting(QStringLiteral("vulkan_device"), Settings::values.vulkan_device, 0);
1059 WriteSetting(QStringLiteral("resolution_factor"), 1066 WriteSetting(QStringLiteral("resolution_factor"),
1060 static_cast<double>(Settings::values.resolution_factor), 1.0); 1067 static_cast<double>(Settings::values.resolution_factor), 1.0);
1061 WriteSetting(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true); 1068 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