summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/CMakeLists.txt2
-rw-r--r--src/common/common_funcs.h4
-rw-r--r--src/common/host_memory.cpp6
-rw-r--r--src/common/settings.cpp1
-rw-r--r--src/common/x64/cpu_detect.cpp59
-rw-r--r--src/common/x64/cpu_detect.h4
-rw-r--r--src/core/CMakeLists.txt14
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp5
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_cp15.cpp22
-rw-r--r--src/core/arm/exclusive_monitor.cpp4
-rw-r--r--src/core/hle/kernel/physical_core.cpp4
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp12
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_instructions.h1
-rw-r--r--src/shader_recompiler/backend/glasm/glasm_emit_context.cpp4
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp12
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_instructions.h1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp12
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_instructions.h1
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp4
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.h1
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.cpp4
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.h1
-rw-r--r--src/shader_recompiler/frontend/ir/opcodes.inc1
-rw-r--r--src/shader_recompiler/frontend/ir/patch.h4
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp3
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate_program.cpp2
-rw-r--r--src/shader_recompiler/host_translate_info.h1
-rw-r--r--src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp3
-rw-r--r--src/shader_recompiler/ir_opt/passes.h6
-rw-r--r--src/shader_recompiler/ir_opt/texture_pass.cpp12
-rw-r--r--src/shader_recompiler/shader_info.h1
-rw-r--r--src/video_core/CMakeLists.txt15
-rw-r--r--src/video_core/engines/maxwell_3d.h4
-rw-r--r--src/video_core/engines/maxwell_dma.cpp37
-rw-r--r--src/video_core/macro/macro.cpp3
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp4
-rw-r--r--src/video_core/renderer_opengl/gl_state_tracker.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.cpp2
-rw-r--r--src/video_core/shader_environment.cpp2
-rw-r--r--src/yuzu/CMakeLists.txt2
-rw-r--r--src/yuzu/bootmanager.cpp337
-rw-r--r--src/yuzu/compatdb.cpp161
-rw-r--r--src/yuzu/compatdb.h11
-rw-r--r--src/yuzu/compatdb.ui277
-rw-r--r--src/yuzu/game_list_p.h12
-rw-r--r--src/yuzu/main.cpp66
-rw-r--r--src/yuzu/main.h7
50 files changed, 787 insertions, 374 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index 0a1f3bf18..8e3a8f5a8 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -217,7 +217,7 @@ else()
217endif() 217endif()
218 218
219target_link_libraries(audio_core PUBLIC common core) 219target_link_libraries(audio_core PUBLIC common core)
220if (ARCHITECTURE_x86_64) 220if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
221 target_link_libraries(audio_core PRIVATE dynarmic) 221 target_link_libraries(audio_core PRIVATE dynarmic)
222endif() 222endif()
223 223
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index e1e2a90fc..0dad9338a 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -31,8 +31,10 @@
31 31
32#ifndef _MSC_VER 32#ifndef _MSC_VER
33 33
34#ifdef ARCHITECTURE_x86_64 34#if defined(ARCHITECTURE_x86_64)
35#define Crash() __asm__ __volatile__("int $3") 35#define Crash() __asm__ __volatile__("int $3")
36#elif defined(ARCHITECTURE_arm64)
37#define Crash() __asm__ __volatile__("brk #0")
36#else 38#else
37#define Crash() exit(1) 39#define Crash() exit(1)
38#endif 40#endif
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp
index 7f9659612..909f6cf3f 100644
--- a/src/common/host_memory.cpp
+++ b/src/common/host_memory.cpp
@@ -359,6 +359,12 @@ public:
359 } 359 }
360 }); 360 });
361 361
362 long page_size = sysconf(_SC_PAGESIZE);
363 if (page_size != 0x1000) {
364 LOG_CRITICAL(HW_Memory, "page size {:#x} is incompatible with 4K paging", page_size);
365 throw std::bad_alloc{};
366 }
367
362 // Backing memory initialization 368 // Backing memory initialization
363#if defined(__FreeBSD__) && __FreeBSD__ < 13 369#if defined(__FreeBSD__) && __FreeBSD__ < 13
364 // XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30 370 // XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 0a560ebb7..8173462cb 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -151,6 +151,7 @@ void UpdateRescalingInfo() {
151 ASSERT(false); 151 ASSERT(false);
152 info.up_scale = 1; 152 info.up_scale = 1;
153 info.down_shift = 0; 153 info.down_shift = 0;
154 break;
154 } 155 }
155 info.up_factor = static_cast<f32>(info.up_scale) / (1U << info.down_shift); 156 info.up_factor = static_cast<f32>(info.up_scale) / (1U << info.down_shift);
156 info.down_factor = static_cast<f32>(1U << info.down_shift) / info.up_scale; 157 info.down_factor = static_cast<f32>(1U << info.down_shift) / info.up_scale;
diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp
index 1a27532d4..e54383a4a 100644
--- a/src/common/x64/cpu_detect.cpp
+++ b/src/common/x64/cpu_detect.cpp
@@ -4,14 +4,27 @@
4 4
5#include <array> 5#include <array>
6#include <cstring> 6#include <cstring>
7#include <fstream>
7#include <iterator> 8#include <iterator>
9#include <optional>
8#include <string_view> 10#include <string_view>
11#include <thread>
12#include <vector>
9#include "common/bit_util.h" 13#include "common/bit_util.h"
10#include "common/common_types.h" 14#include "common/common_types.h"
15#include "common/logging/log.h"
11#include "common/x64/cpu_detect.h" 16#include "common/x64/cpu_detect.h"
12 17
18#ifdef _WIN32
19#include <windows.h>
20#endif
21
13#ifdef _MSC_VER 22#ifdef _MSC_VER
14#include <intrin.h> 23#include <intrin.h>
24
25static inline u64 xgetbv(u32 index) {
26 return _xgetbv(index);
27}
15#else 28#else
16 29
17#if defined(__DragonFly__) || defined(__FreeBSD__) 30#if defined(__DragonFly__) || defined(__FreeBSD__)
@@ -39,12 +52,11 @@ static inline void __cpuid(int info[4], u32 function_id) {
39} 52}
40 53
41#define _XCR_XFEATURE_ENABLED_MASK 0 54#define _XCR_XFEATURE_ENABLED_MASK 0
42static inline u64 _xgetbv(u32 index) { 55static inline u64 xgetbv(u32 index) {
43 u32 eax, edx; 56 u32 eax, edx;
44 __asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(index)); 57 __asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(index));
45 return ((u64)edx << 32) | eax; 58 return ((u64)edx << 32) | eax;
46} 59}
47
48#endif // _MSC_VER 60#endif // _MSC_VER
49 61
50namespace Common { 62namespace Common {
@@ -107,7 +119,7 @@ static CPUCaps Detect() {
107 // - Is the XSAVE bit set in CPUID? 119 // - Is the XSAVE bit set in CPUID?
108 // - XGETBV result has the XCR bit set. 120 // - XGETBV result has the XCR bit set.
109 if (Common::Bit<28>(cpu_id[2]) && Common::Bit<27>(cpu_id[2])) { 121 if (Common::Bit<28>(cpu_id[2]) && Common::Bit<27>(cpu_id[2])) {
110 if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) { 122 if ((xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) {
111 caps.avx = true; 123 caps.avx = true;
112 if (Common::Bit<12>(cpu_id[2])) 124 if (Common::Bit<12>(cpu_id[2]))
113 caps.fma = true; 125 caps.fma = true;
@@ -192,4 +204,45 @@ const CPUCaps& GetCPUCaps() {
192 return caps; 204 return caps;
193} 205}
194 206
207std::optional<int> GetProcessorCount() {
208#if defined(_WIN32)
209 // Get the buffer length.
210 DWORD length = 0;
211 GetLogicalProcessorInformation(nullptr, &length);
212 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
213 LOG_ERROR(Frontend, "Failed to query core count.");
214 return std::nullopt;
215 }
216 std::vector<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> buffer(
217 length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION));
218 // Now query the core count.
219 if (!GetLogicalProcessorInformation(buffer.data(), &length)) {
220 LOG_ERROR(Frontend, "Failed to query core count.");
221 return std::nullopt;
222 }
223 return static_cast<int>(
224 std::count_if(buffer.cbegin(), buffer.cend(), [](const auto& proc_info) {
225 return proc_info.Relationship == RelationProcessorCore;
226 }));
227#elif defined(__unix__)
228 const int thread_count = std::thread::hardware_concurrency();
229 std::ifstream smt("/sys/devices/system/cpu/smt/active");
230 char state = '0';
231 if (smt) {
232 smt.read(&state, sizeof(state));
233 }
234 switch (state) {
235 case '0':
236 return thread_count;
237 case '1':
238 return thread_count / 2;
239 default:
240 return std::nullopt;
241 }
242#else
243 // Shame on you
244 return std::nullopt;
245#endif
246}
247
195} // namespace Common 248} // namespace Common
diff --git a/src/common/x64/cpu_detect.h b/src/common/x64/cpu_detect.h
index 6830f3795..ca8db19d6 100644
--- a/src/common/x64/cpu_detect.h
+++ b/src/common/x64/cpu_detect.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <optional>
7#include <string_view> 8#include <string_view>
8#include "common/common_types.h" 9#include "common/common_types.h"
9 10
@@ -74,4 +75,7 @@ struct CPUCaps {
74 */ 75 */
75const CPUCaps& GetCPUCaps(); 76const CPUCaps& GetCPUCaps();
76 77
78/// Detects CPU core count
79std::optional<int> GetProcessorCount();
80
77} // namespace Common 81} // namespace Common
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index f6e082c36..f67f1ce92 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -497,10 +497,6 @@ add_library(core STATIC
497 hle/service/hid/irsensor/processor_base.h 497 hle/service/hid/irsensor/processor_base.h
498 hle/service/hid/irsensor/tera_plugin_processor.cpp 498 hle/service/hid/irsensor/tera_plugin_processor.cpp
499 hle/service/hid/irsensor/tera_plugin_processor.h 499 hle/service/hid/irsensor/tera_plugin_processor.h
500 hle/service/jit/jit_context.cpp
501 hle/service/jit/jit_context.h
502 hle/service/jit/jit.cpp
503 hle/service/jit/jit.h
504 hle/service/lbl/lbl.cpp 500 hle/service/lbl/lbl.cpp
505 hle/service/lbl/lbl.h 501 hle/service/lbl/lbl.h
506 hle/service/ldn/lan_discovery.cpp 502 hle/service/ldn/lan_discovery.cpp
@@ -805,14 +801,18 @@ if (ENABLE_WEB_SERVICE)
805 target_link_libraries(core PRIVATE web_service) 801 target_link_libraries(core PRIVATE web_service)
806endif() 802endif()
807 803
808if (ARCHITECTURE_x86_64) 804if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
809 target_sources(core PRIVATE 805 target_sources(core PRIVATE
810 arm/dynarmic/arm_dynarmic_32.cpp
811 arm/dynarmic/arm_dynarmic_32.h
812 arm/dynarmic/arm_dynarmic_64.cpp 806 arm/dynarmic/arm_dynarmic_64.cpp
813 arm/dynarmic/arm_dynarmic_64.h 807 arm/dynarmic/arm_dynarmic_64.h
808 arm/dynarmic/arm_dynarmic_32.cpp
809 arm/dynarmic/arm_dynarmic_32.h
814 arm/dynarmic/arm_dynarmic_cp15.cpp 810 arm/dynarmic/arm_dynarmic_cp15.cpp
815 arm/dynarmic/arm_dynarmic_cp15.h 811 arm/dynarmic/arm_dynarmic_cp15.h
812 hle/service/jit/jit_context.cpp
813 hle/service/jit/jit_context.h
814 hle/service/jit/jit.cpp
815 hle/service/jit/jit.h
816 ) 816 )
817 target_link_libraries(core PRIVATE dynarmic) 817 target_link_libraries(core PRIVATE dynarmic)
818endif() 818endif()
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 287ba102e..227e06ea1 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -301,6 +301,11 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
301 } 301 }
302 } 302 }
303 303
304#ifdef ARCHITECTURE_arm64
305 // TODO: remove when fixed in dynarmic
306 config.optimizations &= ~Dynarmic::OptimizationFlag::BlockLinking;
307#endif
308
304 return std::make_unique<Dynarmic::A32::Jit>(config); 309 return std::make_unique<Dynarmic::A32::Jit>(config);
305} 310}
306 311
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
index 200efe4db..5a4eba3eb 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
@@ -52,12 +52,16 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1
52 case 4: 52 case 4:
53 // CP15_DATA_SYNC_BARRIER 53 // CP15_DATA_SYNC_BARRIER
54 return Callback{ 54 return Callback{
55 [](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t { 55 [](void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
56#ifdef _MSC_VER 56#if defined(_MSC_VER) && defined(ARCHITECTURE_x86_64)
57 _mm_mfence(); 57 _mm_mfence();
58 _mm_lfence(); 58 _mm_lfence();
59#else 59#elif defined(ARCHITECTURE_x86_64)
60 asm volatile("mfence\n\tlfence\n\t" : : : "memory"); 60 asm volatile("mfence\n\tlfence\n\t" : : : "memory");
61#elif defined(ARCHITECTURE_arm64)
62 asm volatile("dsb sy\n\t" : : : "memory");
63#else
64#error Unsupported architecture
61#endif 65#endif
62 return 0; 66 return 0;
63 }, 67 },
@@ -66,11 +70,15 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1
66 case 5: 70 case 5:
67 // CP15_DATA_MEMORY_BARRIER 71 // CP15_DATA_MEMORY_BARRIER
68 return Callback{ 72 return Callback{
69 [](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t { 73 [](void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
70#ifdef _MSC_VER 74#if defined(_MSC_VER) && defined(ARCHITECTURE_x86_64)
71 _mm_mfence(); 75 _mm_mfence();
72#else 76#elif defined(ARCHITECTURE_x86_64)
73 asm volatile("mfence\n\t" : : : "memory"); 77 asm volatile("mfence\n\t" : : : "memory");
78#elif defined(ARCHITECTURE_arm64)
79 asm volatile("dmb sy\n\t" : : : "memory");
80#else
81#error Unsupported architecture
74#endif 82#endif
75 return 0; 83 return 0;
76 }, 84 },
@@ -115,7 +123,7 @@ CallbackOrAccessOneWord DynarmicCP15::CompileGetOneWord(bool two, unsigned opc1,
115CallbackOrAccessTwoWords DynarmicCP15::CompileGetTwoWords(bool two, unsigned opc, CoprocReg CRm) { 123CallbackOrAccessTwoWords DynarmicCP15::CompileGetTwoWords(bool two, unsigned opc, CoprocReg CRm) {
116 if (!two && opc == 0 && CRm == CoprocReg::C14) { 124 if (!two && opc == 0 && CRm == CoprocReg::C14) {
117 // CNTPCT 125 // CNTPCT
118 const auto callback = [](Dynarmic::A32::Jit*, void* arg, u32, u32) -> u64 { 126 const auto callback = [](void* arg, u32, u32) -> u64 {
119 const auto& parent_arg = *static_cast<ARM_Dynarmic_32*>(arg); 127 const auto& parent_arg = *static_cast<ARM_Dynarmic_32*>(arg);
120 return parent_arg.system.CoreTiming().GetClockTicks(); 128 return parent_arg.system.CoreTiming().GetClockTicks();
121 }; 129 };
diff --git a/src/core/arm/exclusive_monitor.cpp b/src/core/arm/exclusive_monitor.cpp
index 2db0b035d..20550faeb 100644
--- a/src/core/arm/exclusive_monitor.cpp
+++ b/src/core/arm/exclusive_monitor.cpp
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#ifdef ARCHITECTURE_x86_64 4#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
5#include "core/arm/dynarmic/arm_exclusive_monitor.h" 5#include "core/arm/dynarmic/arm_exclusive_monitor.h"
6#endif 6#endif
7#include "core/arm/exclusive_monitor.h" 7#include "core/arm/exclusive_monitor.h"
@@ -13,7 +13,7 @@ ExclusiveMonitor::~ExclusiveMonitor() = default;
13 13
14std::unique_ptr<Core::ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory, 14std::unique_ptr<Core::ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory,
15 std::size_t num_cores) { 15 std::size_t num_cores) {
16#ifdef ARCHITECTURE_x86_64 16#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
17 return std::make_unique<Core::DynarmicExclusiveMonitor>(memory, num_cores); 17 return std::make_unique<Core::DynarmicExclusiveMonitor>(memory, num_cores);
18#else 18#else
19 // TODO(merry): Passthrough exclusive monitor 19 // TODO(merry): Passthrough exclusive monitor
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp
index d4375962f..3044922ac 100644
--- a/src/core/hle/kernel/physical_core.cpp
+++ b/src/core/hle/kernel/physical_core.cpp
@@ -12,7 +12,7 @@ namespace Kernel {
12 12
13PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_) 13PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_)
14 : core_index{core_index_}, system{system_}, scheduler{scheduler_} { 14 : core_index{core_index_}, system{system_}, scheduler{scheduler_} {
15#ifdef ARCHITECTURE_x86_64 15#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
16 // TODO(bunnei): Initialization relies on a core being available. We may later replace this with 16 // TODO(bunnei): Initialization relies on a core being available. We may later replace this with
17 // a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager. 17 // a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager.
18 auto& kernel = system.Kernel(); 18 auto& kernel = system.Kernel();
@@ -26,7 +26,7 @@ PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KSche
26PhysicalCore::~PhysicalCore() = default; 26PhysicalCore::~PhysicalCore() = default;
27 27
28void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) { 28void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) {
29#ifdef ARCHITECTURE_x86_64 29#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
30 auto& kernel = system.Kernel(); 30 auto& kernel = system.Kernel();
31 if (!is_64_bit) { 31 if (!is_64_bit) {
32 // We already initialized a 64-bit core, replace with a 32-bit one. 32 // We already initialized a 64-bit core, replace with a 32-bit one.
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
index 0a7d42dda..d6562c842 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
@@ -379,6 +379,18 @@ void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) {
379 ctx.Add("MOV.S {}.x,primitive_invocation.x;", inst); 379 ctx.Add("MOV.S {}.x,primitive_invocation.x;", inst);
380} 380}
381 381
382void EmitInvocationInfo(EmitContext& ctx, IR::Inst& inst) {
383 switch (ctx.stage) {
384 case Stage::TessellationControl:
385 case Stage::TessellationEval:
386 ctx.Add("SHL.U {}.x,primitive.vertexcount,16;", inst);
387 break;
388 default:
389 LOG_WARNING(Shader, "(STUBBED) called");
390 ctx.Add("MOV.S {}.x,0x00ff0000;", inst);
391 }
392}
393
382void EmitSampleId(EmitContext& ctx, IR::Inst& inst) { 394void EmitSampleId(EmitContext& ctx, IR::Inst& inst) {
383 ctx.Add("MOV.S {}.x,fragment.sampleid.x;", inst); 395 ctx.Add("MOV.S {}.x,fragment.sampleid.x;", inst);
384} 396}
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
index d645fd532..eaaf9ba39 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
@@ -69,6 +69,7 @@ void EmitSetOFlag(EmitContext& ctx);
69void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst); 69void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst);
70void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst); 70void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst);
71void EmitInvocationId(EmitContext& ctx, IR::Inst& inst); 71void EmitInvocationId(EmitContext& ctx, IR::Inst& inst);
72void EmitInvocationInfo(EmitContext& ctx, IR::Inst& inst);
72void EmitSampleId(EmitContext& ctx, IR::Inst& inst); 73void EmitSampleId(EmitContext& ctx, IR::Inst& inst);
73void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst); 74void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst);
74void EmitYDirection(EmitContext& ctx, IR::Inst& inst); 75void EmitYDirection(EmitContext& ctx, IR::Inst& inst);
diff --git a/src/shader_recompiler/backend/glasm/glasm_emit_context.cpp b/src/shader_recompiler/backend/glasm/glasm_emit_context.cpp
index 89603c1c4..333a91cc5 100644
--- a/src/shader_recompiler/backend/glasm/glasm_emit_context.cpp
+++ b/src/shader_recompiler/backend/glasm/glasm_emit_context.cpp
@@ -95,6 +95,10 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
95 if (info.uses_invocation_id) { 95 if (info.uses_invocation_id) {
96 Add("ATTRIB primitive_invocation=primitive.invocation;"); 96 Add("ATTRIB primitive_invocation=primitive.invocation;");
97 } 97 }
98 if (info.uses_invocation_info &&
99 (stage == Stage::TessellationControl || stage == Stage::TessellationEval)) {
100 Add("ATTRIB primitive_vertexcount = primitive.vertexcount;");
101 }
98 if (info.stores_tess_level_outer) { 102 if (info.stores_tess_level_outer) {
99 Add("OUTPUT result_patch_tessouter[]={{result.patch.tessouter[0..3]}};"); 103 Add("OUTPUT result_patch_tessouter[]={{result.patch.tessouter[0..3]}};");
100 } 104 }
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
index d7c845469..c1671c37b 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
@@ -399,6 +399,18 @@ void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) {
399 ctx.AddU32("{}=uint(gl_InvocationID);", inst); 399 ctx.AddU32("{}=uint(gl_InvocationID);", inst);
400} 400}
401 401
402void EmitInvocationInfo(EmitContext& ctx, IR::Inst& inst) {
403 switch (ctx.stage) {
404 case Stage::TessellationControl:
405 case Stage::TessellationEval:
406 ctx.AddU32("{}=uint(gl_PatchVerticesIn)<<16;", inst);
407 break;
408 default:
409 LOG_WARNING(Shader, "(STUBBED) called");
410 ctx.AddU32("{}=uint(0x00ff0000);", inst);
411 }
412}
413
402void EmitSampleId(EmitContext& ctx, IR::Inst& inst) { 414void EmitSampleId(EmitContext& ctx, IR::Inst& inst) {
403 ctx.AddU32("{}=uint(gl_SampleID);", inst); 415 ctx.AddU32("{}=uint(gl_SampleID);", inst);
404} 416}
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
index 96e683b5e..4151c89de 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
@@ -83,6 +83,7 @@ void EmitSetOFlag(EmitContext& ctx);
83void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst); 83void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst);
84void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst); 84void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst);
85void EmitInvocationId(EmitContext& ctx, IR::Inst& inst); 85void EmitInvocationId(EmitContext& ctx, IR::Inst& inst);
86void EmitInvocationInfo(EmitContext& ctx, IR::Inst& inst);
86void EmitSampleId(EmitContext& ctx, IR::Inst& inst); 87void EmitSampleId(EmitContext& ctx, IR::Inst& inst);
87void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst); 88void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst);
88void EmitYDirection(EmitContext& ctx, IR::Inst& inst); 89void EmitYDirection(EmitContext& ctx, IR::Inst& inst);
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
index a4751b42d..5b3b5d1f3 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
@@ -512,6 +512,18 @@ Id EmitInvocationId(EmitContext& ctx) {
512 return ctx.OpLoad(ctx.U32[1], ctx.invocation_id); 512 return ctx.OpLoad(ctx.U32[1], ctx.invocation_id);
513} 513}
514 514
515Id EmitInvocationInfo(EmitContext& ctx) {
516 switch (ctx.stage) {
517 case Stage::TessellationControl:
518 case Stage::TessellationEval:
519 return ctx.OpShiftLeftLogical(ctx.U32[1], ctx.OpLoad(ctx.U32[1], ctx.patch_vertices_in),
520 ctx.Const(16u));
521 default:
522 LOG_WARNING(Shader, "(STUBBED) called");
523 return ctx.Const(0x00ff0000u);
524 }
525}
526
515Id EmitSampleId(EmitContext& ctx) { 527Id EmitSampleId(EmitContext& ctx) {
516 return ctx.OpLoad(ctx.U32[1], ctx.sample_id); 528 return ctx.OpLoad(ctx.U32[1], ctx.sample_id);
517} 529}
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
index 7070c8fda..e31cdc5e8 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
@@ -72,6 +72,7 @@ void EmitSetOFlag(EmitContext& ctx);
72Id EmitWorkgroupId(EmitContext& ctx); 72Id EmitWorkgroupId(EmitContext& ctx);
73Id EmitLocalInvocationId(EmitContext& ctx); 73Id EmitLocalInvocationId(EmitContext& ctx);
74Id EmitInvocationId(EmitContext& ctx); 74Id EmitInvocationId(EmitContext& ctx);
75Id EmitInvocationInfo(EmitContext& ctx);
75Id EmitSampleId(EmitContext& ctx); 76Id EmitSampleId(EmitContext& ctx);
76Id EmitIsHelperInvocation(EmitContext& ctx); 77Id EmitIsHelperInvocation(EmitContext& ctx);
77Id EmitYDirection(EmitContext& ctx); 78Id EmitYDirection(EmitContext& ctx);
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index c26ad8f93..0bfc2dd89 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -1325,6 +1325,10 @@ void EmitContext::DefineInputs(const IR::Program& program) {
1325 if (info.uses_invocation_id) { 1325 if (info.uses_invocation_id) {
1326 invocation_id = DefineInput(*this, U32[1], false, spv::BuiltIn::InvocationId); 1326 invocation_id = DefineInput(*this, U32[1], false, spv::BuiltIn::InvocationId);
1327 } 1327 }
1328 if (info.uses_invocation_info &&
1329 (stage == Shader::Stage::TessellationControl || stage == Shader::Stage::TessellationEval)) {
1330 patch_vertices_in = DefineInput(*this, U32[1], false, spv::BuiltIn::PatchVertices);
1331 }
1328 if (info.uses_sample_id) { 1332 if (info.uses_sample_id) {
1329 sample_id = DefineInput(*this, U32[1], false, spv::BuiltIn::SampleId); 1333 sample_id = DefineInput(*this, U32[1], false, spv::BuiltIn::SampleId);
1330 } 1334 }
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
index c86e50911..dde45b4bc 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
@@ -204,6 +204,7 @@ public:
204 Id workgroup_id{}; 204 Id workgroup_id{};
205 Id local_invocation_id{}; 205 Id local_invocation_id{};
206 Id invocation_id{}; 206 Id invocation_id{};
207 Id patch_vertices_in{};
207 Id sample_id{}; 208 Id sample_id{};
208 Id is_helper_invocation{}; 209 Id is_helper_invocation{};
209 Id subgroup_local_invocation_id{}; 210 Id subgroup_local_invocation_id{};
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
index d4425f06d..0cdac0eff 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
@@ -362,6 +362,10 @@ U32 IREmitter::InvocationId() {
362 return Inst<U32>(Opcode::InvocationId); 362 return Inst<U32>(Opcode::InvocationId);
363} 363}
364 364
365U32 IREmitter::InvocationInfo() {
366 return Inst<U32>(Opcode::InvocationInfo);
367}
368
365U32 IREmitter::SampleId() { 369U32 IREmitter::SampleId() {
366 return Inst<U32>(Opcode::SampleId); 370 return Inst<U32>(Opcode::SampleId);
367} 371}
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h
index f163c18d9..2df992feb 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.h
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.h
@@ -97,6 +97,7 @@ public:
97 [[nodiscard]] U32 LocalInvocationIdZ(); 97 [[nodiscard]] U32 LocalInvocationIdZ();
98 98
99 [[nodiscard]] U32 InvocationId(); 99 [[nodiscard]] U32 InvocationId();
100 [[nodiscard]] U32 InvocationInfo();
100 [[nodiscard]] U32 SampleId(); 101 [[nodiscard]] U32 SampleId();
101 [[nodiscard]] U1 IsHelperInvocation(); 102 [[nodiscard]] U1 IsHelperInvocation();
102 [[nodiscard]] F32 YDirection(); 103 [[nodiscard]] F32 YDirection();
diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc
index 88aa077ee..1fe3749cc 100644
--- a/src/shader_recompiler/frontend/ir/opcodes.inc
+++ b/src/shader_recompiler/frontend/ir/opcodes.inc
@@ -59,6 +59,7 @@ OPCODE(SetOFlag, Void, U1,
59OPCODE(WorkgroupId, U32x3, ) 59OPCODE(WorkgroupId, U32x3, )
60OPCODE(LocalInvocationId, U32x3, ) 60OPCODE(LocalInvocationId, U32x3, )
61OPCODE(InvocationId, U32, ) 61OPCODE(InvocationId, U32, )
62OPCODE(InvocationInfo, U32, )
62OPCODE(SampleId, U32, ) 63OPCODE(SampleId, U32, )
63OPCODE(IsHelperInvocation, U1, ) 64OPCODE(IsHelperInvocation, U1, )
64OPCODE(YDirection, F32, ) 65OPCODE(YDirection, F32, )
diff --git a/src/shader_recompiler/frontend/ir/patch.h b/src/shader_recompiler/frontend/ir/patch.h
index 1e37c8eb6..5077e56c2 100644
--- a/src/shader_recompiler/frontend/ir/patch.h
+++ b/src/shader_recompiler/frontend/ir/patch.h
@@ -14,8 +14,6 @@ enum class Patch : u64 {
14 TessellationLodBottom, 14 TessellationLodBottom,
15 TessellationLodInteriorU, 15 TessellationLodInteriorU,
16 TessellationLodInteriorV, 16 TessellationLodInteriorV,
17 ComponentPadding0,
18 ComponentPadding1,
19 Component0, 17 Component0,
20 Component1, 18 Component1,
21 Component2, 19 Component2,
@@ -137,7 +135,7 @@ enum class Patch : u64 {
137 Component118, 135 Component118,
138 Component119, 136 Component119,
139}; 137};
140static_assert(static_cast<u64>(Patch::Component119) == 127); 138static_assert(static_cast<u64>(Patch::Component119) == 125);
141 139
142[[nodiscard]] bool IsGeneric(Patch patch) noexcept; 140[[nodiscard]] bool IsGeneric(Patch patch) noexcept;
143 141
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp
index 52be12f9c..753c62098 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp
@@ -117,8 +117,7 @@ enum class SpecialRegister : u64 {
117 case SpecialRegister::SR_THREAD_KILL: 117 case SpecialRegister::SR_THREAD_KILL:
118 return IR::U32{ir.Select(ir.IsHelperInvocation(), ir.Imm32(-1), ir.Imm32(0))}; 118 return IR::U32{ir.Select(ir.IsHelperInvocation(), ir.Imm32(-1), ir.Imm32(0))};
119 case SpecialRegister::SR_INVOCATION_INFO: 119 case SpecialRegister::SR_INVOCATION_INFO:
120 LOG_WARNING(Shader, "(STUBBED) SR_INVOCATION_INFO"); 120 return ir.InvocationInfo();
121 return ir.Imm32(0x00ff'0000);
122 case SpecialRegister::SR_TID: { 121 case SpecialRegister::SR_TID: {
123 const IR::Value tid{ir.LocalInvocationId()}; 122 const IR::Value tid{ir.LocalInvocationId()};
124 return ir.BitFieldInsert(ir.BitFieldInsert(IR::U32{ir.CompositeExtract(tid, 0)}, 123 return ir.BitFieldInsert(ir.BitFieldInsert(IR::U32{ir.CompositeExtract(tid, 0)},
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
index b7162f719..376aae0ea 100644
--- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
@@ -223,7 +223,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
223 Optimization::PositionPass(env, program); 223 Optimization::PositionPass(env, program);
224 224
225 Optimization::GlobalMemoryToStorageBufferPass(program); 225 Optimization::GlobalMemoryToStorageBufferPass(program);
226 Optimization::TexturePass(env, program); 226 Optimization::TexturePass(env, program, host_info);
227 227
228 if (Settings::values.resolution_info.active) { 228 if (Settings::values.resolution_info.active) {
229 Optimization::RescalingPass(program); 229 Optimization::RescalingPass(program);
diff --git a/src/shader_recompiler/host_translate_info.h b/src/shader_recompiler/host_translate_info.h
index 881874310..cc1500690 100644
--- a/src/shader_recompiler/host_translate_info.h
+++ b/src/shader_recompiler/host_translate_info.h
@@ -13,6 +13,7 @@ struct HostTranslateInfo {
13 bool support_float16{}; ///< True when the device supports 16-bit floats 13 bool support_float16{}; ///< True when the device supports 16-bit floats
14 bool support_int64{}; ///< True when the device supports 64-bit integers 14 bool support_int64{}; ///< True when the device supports 64-bit integers
15 bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered 15 bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered
16 bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers
16}; 17};
17 18
18} // namespace Shader 19} // namespace Shader
diff --git a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
index 7cff8ecdc..5a4195217 100644
--- a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
+++ b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
@@ -468,6 +468,9 @@ void VisitUsages(Info& info, IR::Inst& inst) {
468 case IR::Opcode::InvocationId: 468 case IR::Opcode::InvocationId:
469 info.uses_invocation_id = true; 469 info.uses_invocation_id = true;
470 break; 470 break;
471 case IR::Opcode::InvocationInfo:
472 info.uses_invocation_info = true;
473 break;
471 case IR::Opcode::SampleId: 474 case IR::Opcode::SampleId:
472 info.uses_sample_id = true; 475 info.uses_sample_id = true;
473 break; 476 break;
diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h
index 24f609d69..586a0668f 100644
--- a/src/shader_recompiler/ir_opt/passes.h
+++ b/src/shader_recompiler/ir_opt/passes.h
@@ -6,6 +6,10 @@
6#include "shader_recompiler/environment.h" 6#include "shader_recompiler/environment.h"
7#include "shader_recompiler/frontend/ir/program.h" 7#include "shader_recompiler/frontend/ir/program.h"
8 8
9namespace Shader {
10struct HostTranslateInfo;
11}
12
9namespace Shader::Optimization { 13namespace Shader::Optimization {
10 14
11void CollectShaderInfoPass(Environment& env, IR::Program& program); 15void CollectShaderInfoPass(Environment& env, IR::Program& program);
@@ -18,7 +22,7 @@ void LowerInt64ToInt32(IR::Program& program);
18void RescalingPass(IR::Program& program); 22void RescalingPass(IR::Program& program);
19void SsaRewritePass(IR::Program& program); 23void SsaRewritePass(IR::Program& program);
20void PositionPass(Environment& env, IR::Program& program); 24void PositionPass(Environment& env, IR::Program& program);
21void TexturePass(Environment& env, IR::Program& program); 25void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo& host_info);
22void VerificationPass(const IR::Program& program); 26void VerificationPass(const IR::Program& program);
23 27
24// Dual Vertex 28// Dual Vertex
diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp
index 9eff84a3d..f5c86fcb1 100644
--- a/src/shader_recompiler/ir_opt/texture_pass.cpp
+++ b/src/shader_recompiler/ir_opt/texture_pass.cpp
@@ -7,11 +7,11 @@
7 7
8#include <boost/container/small_vector.hpp> 8#include <boost/container/small_vector.hpp>
9 9
10#include "common/settings.h"
11#include "shader_recompiler/environment.h" 10#include "shader_recompiler/environment.h"
12#include "shader_recompiler/frontend/ir/basic_block.h" 11#include "shader_recompiler/frontend/ir/basic_block.h"
13#include "shader_recompiler/frontend/ir/breadth_first_search.h" 12#include "shader_recompiler/frontend/ir/breadth_first_search.h"
14#include "shader_recompiler/frontend/ir/ir_emitter.h" 13#include "shader_recompiler/frontend/ir/ir_emitter.h"
14#include "shader_recompiler/host_translate_info.h"
15#include "shader_recompiler/ir_opt/passes.h" 15#include "shader_recompiler/ir_opt/passes.h"
16#include "shader_recompiler/shader_info.h" 16#include "shader_recompiler/shader_info.h"
17 17
@@ -461,7 +461,7 @@ void PatchImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
461 ir.FPRecip(ir.ConvertUToF(32, 32, ir.CompositeExtract(texture_size, 1)))))); 461 ir.FPRecip(ir.ConvertUToF(32, 32, ir.CompositeExtract(texture_size, 1))))));
462} 462}
463 463
464void PathTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_format) { 464void PatchTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_format) {
465 const auto it{IR::Block::InstructionList::s_iterator_to(inst)}; 465 const auto it{IR::Block::InstructionList::s_iterator_to(inst)};
466 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; 466 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
467 auto get_max_value = [pixel_format]() -> float { 467 auto get_max_value = [pixel_format]() -> float {
@@ -494,7 +494,7 @@ void PathTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_f
494} 494}
495} // Anonymous namespace 495} // Anonymous namespace
496 496
497void TexturePass(Environment& env, IR::Program& program) { 497void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo& host_info) {
498 TextureInstVector to_replace; 498 TextureInstVector to_replace;
499 for (IR::Block* const block : program.post_order_blocks) { 499 for (IR::Block* const block : program.post_order_blocks) {
500 for (IR::Inst& inst : block->Instructions()) { 500 for (IR::Inst& inst : block->Instructions()) {
@@ -639,11 +639,11 @@ void TexturePass(Environment& env, IR::Program& program) {
639 inst->SetArg(0, IR::Value{}); 639 inst->SetArg(0, IR::Value{});
640 } 640 }
641 641
642 if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL && 642 if (!host_info.support_snorm_render_buffer && inst->GetOpcode() == IR::Opcode::ImageFetch &&
643 inst->GetOpcode() == IR::Opcode::ImageFetch && flags.type == TextureType::Buffer) { 643 flags.type == TextureType::Buffer) {
644 const auto pixel_format = ReadTexturePixelFormat(env, cbuf); 644 const auto pixel_format = ReadTexturePixelFormat(env, cbuf);
645 if (pixel_format != TexturePixelFormat::OTHER) { 645 if (pixel_format != TexturePixelFormat::OTHER) {
646 PathTexelFetch(*texture_inst.block, *texture_inst.inst, pixel_format); 646 PatchTexelFetch(*texture_inst.block, *texture_inst.inst, pixel_format);
647 } 647 }
648 } 648 }
649 } 649 }
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h
index f31e1f821..ee6252bb5 100644
--- a/src/shader_recompiler/shader_info.h
+++ b/src/shader_recompiler/shader_info.h
@@ -127,6 +127,7 @@ struct Info {
127 bool uses_workgroup_id{}; 127 bool uses_workgroup_id{};
128 bool uses_local_invocation_id{}; 128 bool uses_local_invocation_id{};
129 bool uses_invocation_id{}; 129 bool uses_invocation_id{};
130 bool uses_invocation_info{};
130 bool uses_sample_id{}; 131 bool uses_sample_id{};
131 bool uses_is_helper_invocation{}; 132 bool uses_is_helper_invocation{};
132 bool uses_subgroup_invocation_id{}; 133 bool uses_subgroup_invocation_id{};
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 106991969..d7f7d336c 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -73,8 +73,6 @@ add_library(video_core STATIC
73 macro/macro_hle.h 73 macro/macro_hle.h
74 macro/macro_interpreter.cpp 74 macro/macro_interpreter.cpp
75 macro/macro_interpreter.h 75 macro/macro_interpreter.h
76 macro/macro_jit_x64.cpp
77 macro/macro_jit_x64.h
78 fence_manager.h 76 fence_manager.h
79 gpu.cpp 77 gpu.cpp
80 gpu.h 78 gpu.h
@@ -245,7 +243,7 @@ add_library(video_core STATIC
245create_target_directory_groups(video_core) 243create_target_directory_groups(video_core)
246 244
247target_link_libraries(video_core PUBLIC common core) 245target_link_libraries(video_core PUBLIC common core)
248target_link_libraries(video_core PUBLIC glad shader_recompiler xbyak) 246target_link_libraries(video_core PUBLIC glad shader_recompiler)
249 247
250if (YUZU_USE_BUNDLED_FFMPEG AND NOT WIN32) 248if (YUZU_USE_BUNDLED_FFMPEG AND NOT WIN32)
251 add_dependencies(video_core ffmpeg-build) 249 add_dependencies(video_core ffmpeg-build)
@@ -282,8 +280,19 @@ else()
282 280
283 -Wno-sign-conversion 281 -Wno-sign-conversion
284 ) 282 )
283
284 # xbyak
285 set_source_files_properties(macro/macro_jit_x64.cpp PROPERTIES COMPILE_OPTIONS "-Wno-conversion;-Wno-shadow")
285endif() 286endif()
286 287
287if (ARCHITECTURE_x86_64) 288if (ARCHITECTURE_x86_64)
289 target_sources(video_core PRIVATE
290 macro/macro_jit_x64.cpp
291 macro/macro_jit_x64.h
292 )
293 target_link_libraries(video_core PUBLIC xbyak)
294endif()
295
296if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
288 target_link_libraries(video_core PRIVATE dynarmic) 297 target_link_libraries(video_core PRIVATE dynarmic)
289endif() 298endif()
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index a948fcb14..910ab213a 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -2970,7 +2970,7 @@ public:
2970 CullFace gl_cull_face; ///< 0x1920 2970 CullFace gl_cull_face; ///< 0x1920
2971 Viewport::PixelCenter viewport_pixel_center; ///< 0x1924 2971 Viewport::PixelCenter viewport_pixel_center; ///< 0x1924
2972 INSERT_PADDING_BYTES_NOINIT(0x4); 2972 INSERT_PADDING_BYTES_NOINIT(0x4);
2973 u32 viewport_scale_offset_enbled; ///< 0x192C 2973 u32 viewport_scale_offset_enabled; ///< 0x192C
2974 INSERT_PADDING_BYTES_NOINIT(0xC); 2974 INSERT_PADDING_BYTES_NOINIT(0xC);
2975 ViewportClipControl viewport_clip_control; ///< 0x193C 2975 ViewportClipControl viewport_clip_control; ///< 0x193C
2976 UserClip::Op user_clip_op; ///< 0x1940 2976 UserClip::Op user_clip_op; ///< 0x1940
@@ -3482,7 +3482,7 @@ ASSERT_REG_POSITION(gl_cull_test_enabled, 0x1918);
3482ASSERT_REG_POSITION(gl_front_face, 0x191C); 3482ASSERT_REG_POSITION(gl_front_face, 0x191C);
3483ASSERT_REG_POSITION(gl_cull_face, 0x1920); 3483ASSERT_REG_POSITION(gl_cull_face, 0x1920);
3484ASSERT_REG_POSITION(viewport_pixel_center, 0x1924); 3484ASSERT_REG_POSITION(viewport_pixel_center, 0x1924);
3485ASSERT_REG_POSITION(viewport_scale_offset_enbled, 0x192C); 3485ASSERT_REG_POSITION(viewport_scale_offset_enabled, 0x192C);
3486ASSERT_REG_POSITION(viewport_clip_control, 0x193C); 3486ASSERT_REG_POSITION(viewport_clip_control, 0x193C);
3487ASSERT_REG_POSITION(user_clip_op, 0x1940); 3487ASSERT_REG_POSITION(user_clip_op, 0x1940);
3488ASSERT_REG_POSITION(render_enable_override, 0x1944); 3488ASSERT_REG_POSITION(render_enable_override, 0x1944);
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index 4eb7a100d..54523a4b2 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -102,26 +102,29 @@ void MaxwellDMA::Launch() {
102 const bool is_src_pitch = IsPitchKind(static_cast<PTEKind>(src_kind)); 102 const bool is_src_pitch = IsPitchKind(static_cast<PTEKind>(src_kind));
103 const bool is_dst_pitch = IsPitchKind(static_cast<PTEKind>(dst_kind)); 103 const bool is_dst_pitch = IsPitchKind(static_cast<PTEKind>(dst_kind));
104 if (!is_src_pitch && is_dst_pitch) { 104 if (!is_src_pitch && is_dst_pitch) {
105 std::vector<u8> tmp_buffer(regs.line_length_in); 105 UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0);
106 std::vector<u8> dst_buffer(regs.line_length_in); 106 UNIMPLEMENTED_IF(regs.offset_in % 16 != 0);
107 memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(), 107 UNIMPLEMENTED_IF(regs.offset_out % 16 != 0);
108 regs.line_length_in); 108 std::vector<u8> tmp_buffer(16);
109 for (u32 offset = 0; offset < regs.line_length_in; ++offset) { 109 for (u32 offset = 0; offset < regs.line_length_in; offset += 16) {
110 dst_buffer[offset] = 110 memory_manager.ReadBlockUnsafe(
111 tmp_buffer[convert_linear_2_blocklinear_addr(regs.offset_in + offset) - 111 convert_linear_2_blocklinear_addr(regs.offset_in + offset),
112 regs.offset_in]; 112 tmp_buffer.data(), tmp_buffer.size());
113 memory_manager.WriteBlock(regs.offset_out + offset, tmp_buffer.data(),
114 tmp_buffer.size());
113 } 115 }
114 memory_manager.WriteBlock(regs.offset_out, dst_buffer.data(), regs.line_length_in);
115 } else if (is_src_pitch && !is_dst_pitch) { 116 } else if (is_src_pitch && !is_dst_pitch) {
116 std::vector<u8> tmp_buffer(regs.line_length_in); 117 UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0);
117 std::vector<u8> dst_buffer(regs.line_length_in); 118 UNIMPLEMENTED_IF(regs.offset_in % 16 != 0);
118 memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(), 119 UNIMPLEMENTED_IF(regs.offset_out % 16 != 0);
119 regs.line_length_in); 120 std::vector<u8> tmp_buffer(16);
120 for (u32 offset = 0; offset < regs.line_length_in; ++offset) { 121 for (u32 offset = 0; offset < regs.line_length_in; offset += 16) {
121 dst_buffer[convert_linear_2_blocklinear_addr(regs.offset_out + offset) - 122 memory_manager.ReadBlockUnsafe(regs.offset_in + offset, tmp_buffer.data(),
122 regs.offset_out] = tmp_buffer[offset]; 123 tmp_buffer.size());
124 memory_manager.WriteBlock(
125 convert_linear_2_blocklinear_addr(regs.offset_out + offset),
126 tmp_buffer.data(), tmp_buffer.size());
123 } 127 }
124 memory_manager.WriteBlock(regs.offset_out, dst_buffer.data(), regs.line_length_in);
125 } else { 128 } else {
126 if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) { 129 if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) {
127 std::vector<u8> tmp_buffer(regs.line_length_in); 130 std::vector<u8> tmp_buffer(regs.line_length_in);
diff --git a/src/video_core/macro/macro.cpp b/src/video_core/macro/macro.cpp
index f61d5998e..505d81c1e 100644
--- a/src/video_core/macro/macro.cpp
+++ b/src/video_core/macro/macro.cpp
@@ -16,7 +16,10 @@
16#include "video_core/macro/macro.h" 16#include "video_core/macro/macro.h"
17#include "video_core/macro/macro_hle.h" 17#include "video_core/macro/macro_hle.h"
18#include "video_core/macro/macro_interpreter.h" 18#include "video_core/macro/macro_interpreter.h"
19
20#ifdef ARCHITECTURE_x86_64
19#include "video_core/macro/macro_jit_x64.h" 21#include "video_core/macro/macro_jit_x64.h"
22#endif
20 23
21namespace Tegra { 24namespace Tegra {
22 25
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 72e314d39..8a8b5ce54 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -618,7 +618,7 @@ void RasterizerOpenGL::SyncViewport() {
618 } 618 }
619 flags[Dirty::Viewport0 + index] = false; 619 flags[Dirty::Viewport0 + index] = false;
620 620
621 if (!regs.viewport_scale_offset_enbled) { 621 if (!regs.viewport_scale_offset_enabled) {
622 const auto x = static_cast<GLfloat>(regs.surface_clip.x); 622 const auto x = static_cast<GLfloat>(regs.surface_clip.x);
623 const auto y = static_cast<GLfloat>(regs.surface_clip.y); 623 const auto y = static_cast<GLfloat>(regs.surface_clip.y);
624 const auto width = static_cast<GLfloat>(regs.surface_clip.width); 624 const auto width = static_cast<GLfloat>(regs.surface_clip.width);
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 977709518..3fe04a115 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -76,7 +76,8 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key,
76 } 76 }
77 break; 77 break;
78 case Shader::Stage::TessellationEval: 78 case Shader::Stage::TessellationEval:
79 info.tess_clockwise = key.tessellation_clockwise != 0; 79 // Flip the face, as OpenGL's drawing is flipped.
80 info.tess_clockwise = key.tessellation_clockwise == 0;
80 info.tess_primitive = [&key] { 81 info.tess_primitive = [&key] {
81 switch (key.tessellation_primitive) { 82 switch (key.tessellation_primitive) {
82 case Maxwell::Tessellation::DomainType::Isolines: 83 case Maxwell::Tessellation::DomainType::Isolines:
@@ -218,6 +219,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
218 .support_float16 = false, 219 .support_float16 = false,
219 .support_int64 = device.HasShaderInt64(), 220 .support_int64 = device.HasShaderInt64(),
220 .needs_demote_reorder = device.IsAmd(), 221 .needs_demote_reorder = device.IsAmd(),
222 .support_snorm_render_buffer = false,
221 } { 223 } {
222 if (use_asynchronous_shaders) { 224 if (use_asynchronous_shaders) {
223 workers = CreateWorkers(); 225 workers = CreateWorkers();
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index a359f96f1..d53b422ca 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -70,8 +70,8 @@ void SetupDirtyViewports(Tables& tables) {
70 FillBlock(tables[1], OFF(viewport_transform), NUM(viewport_transform), Viewports); 70 FillBlock(tables[1], OFF(viewport_transform), NUM(viewport_transform), Viewports);
71 FillBlock(tables[1], OFF(viewports), NUM(viewports), Viewports); 71 FillBlock(tables[1], OFF(viewports), NUM(viewports), Viewports);
72 72
73 tables[0][OFF(viewport_scale_offset_enbled)] = ViewportTransform; 73 tables[0][OFF(viewport_scale_offset_enabled)] = ViewportTransform;
74 tables[1][OFF(viewport_scale_offset_enbled)] = Viewports; 74 tables[1][OFF(viewport_scale_offset_enabled)] = Viewports;
75} 75}
76 76
77void SetupDirtyScissors(Tables& tables) { 77void SetupDirtyScissors(Tables& tables) {
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index b42e5be1e..d4b0a542a 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -166,6 +166,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
166 } 166 }
167 break; 167 break;
168 case Shader::Stage::TessellationEval: 168 case Shader::Stage::TessellationEval:
169 info.tess_clockwise = key.state.tessellation_clockwise != 0;
169 info.tess_primitive = [&key] { 170 info.tess_primitive = [&key] {
170 const u32 raw{key.state.tessellation_primitive.Value()}; 171 const u32 raw{key.state.tessellation_primitive.Value()};
171 switch (static_cast<Maxwell::Tessellation::DomainType>(raw)) { 172 switch (static_cast<Maxwell::Tessellation::DomainType>(raw)) {
@@ -325,6 +326,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
325 .support_int64 = device.IsShaderInt64Supported(), 326 .support_int64 = device.IsShaderInt64Supported(),
326 .needs_demote_reorder = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY_KHR || 327 .needs_demote_reorder = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY_KHR ||
327 driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR, 328 driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR,
329 .support_snorm_render_buffer = true,
328 }; 330 };
329} 331}
330 332
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index f79fa8313..f69c0c50f 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -683,7 +683,7 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg
683 if (!state_tracker.TouchViewports()) { 683 if (!state_tracker.TouchViewports()) {
684 return; 684 return;
685 } 685 }
686 if (!regs.viewport_scale_offset_enbled) { 686 if (!regs.viewport_scale_offset_enabled) {
687 const auto x = static_cast<float>(regs.surface_clip.x); 687 const auto x = static_cast<float>(regs.surface_clip.x);
688 const auto y = static_cast<float>(regs.surface_clip.y); 688 const auto y = static_cast<float>(regs.surface_clip.y);
689 const auto width = static_cast<float>(regs.surface_clip.width); 689 const auto width = static_cast<float>(regs.surface_clip.width);
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
index b87c3be66..edb41b171 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
@@ -51,7 +51,7 @@ Flags MakeInvalidationFlags() {
51void SetupDirtyViewports(Tables& tables) { 51void SetupDirtyViewports(Tables& tables) {
52 FillBlock(tables[0], OFF(viewport_transform), NUM(viewport_transform), Viewports); 52 FillBlock(tables[0], OFF(viewport_transform), NUM(viewport_transform), Viewports);
53 FillBlock(tables[0], OFF(viewports), NUM(viewports), Viewports); 53 FillBlock(tables[0], OFF(viewports), NUM(viewports), Viewports);
54 tables[0][OFF(viewport_scale_offset_enbled)] = Viewports; 54 tables[0][OFF(viewport_scale_offset_enabled)] = Viewports;
55 tables[1][OFF(window_origin)] = Viewports; 55 tables[1][OFF(window_origin)] = Viewports;
56} 56}
57 57
diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp
index 37bb76b72..f24f320b6 100644
--- a/src/video_core/shader_environment.cpp
+++ b/src/video_core/shader_environment.cpp
@@ -352,7 +352,7 @@ Shader::TexturePixelFormat GraphicsEnvironment::ReadTexturePixelFormat(u32 handl
352 352
353u32 GraphicsEnvironment::ReadViewportTransformState() { 353u32 GraphicsEnvironment::ReadViewportTransformState() {
354 const auto& regs{maxwell3d->regs}; 354 const auto& regs{maxwell3d->regs};
355 viewport_transform_state = regs.viewport_scale_offset_enbled; 355 viewport_transform_state = regs.viewport_scale_offset_enabled;
356 return viewport_transform_state; 356 return viewport_transform_state;
357} 357}
358 358
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 239f12382..5cc1fbf32 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -385,6 +385,6 @@ if (NOT APPLE)
385 target_compile_definitions(yuzu PRIVATE HAS_OPENGL) 385 target_compile_definitions(yuzu PRIVATE HAS_OPENGL)
386endif() 386endif()
387 387
388if (ARCHITECTURE_x86_64) 388if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
389 target_link_libraries(yuzu PRIVATE dynarmic) 389 target_link_libraries(yuzu PRIVATE dynarmic)
390endif() 390endif()
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 6acfb7b06..d88efacd7 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -401,224 +401,127 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
401} 401}
402 402
403int GRenderWindow::QtKeyToSwitchKey(Qt::Key qt_key) { 403int GRenderWindow::QtKeyToSwitchKey(Qt::Key qt_key) {
404 switch (qt_key) { 404 static constexpr std::array<std::pair<Qt::Key, Settings::NativeKeyboard::Keys>, 106> key_map = {
405 case Qt::Key_A: 405 std::pair<Qt::Key, Settings::NativeKeyboard::Keys>{Qt::Key_A, Settings::NativeKeyboard::A},
406 return Settings::NativeKeyboard::A; 406 {Qt::Key_A, Settings::NativeKeyboard::A},
407 case Qt::Key_B: 407 {Qt::Key_B, Settings::NativeKeyboard::B},
408 return Settings::NativeKeyboard::B; 408 {Qt::Key_C, Settings::NativeKeyboard::C},
409 case Qt::Key_C: 409 {Qt::Key_D, Settings::NativeKeyboard::D},
410 return Settings::NativeKeyboard::C; 410 {Qt::Key_E, Settings::NativeKeyboard::E},
411 case Qt::Key_D: 411 {Qt::Key_F, Settings::NativeKeyboard::F},
412 return Settings::NativeKeyboard::D; 412 {Qt::Key_G, Settings::NativeKeyboard::G},
413 case Qt::Key_E: 413 {Qt::Key_H, Settings::NativeKeyboard::H},
414 return Settings::NativeKeyboard::E; 414 {Qt::Key_I, Settings::NativeKeyboard::I},
415 case Qt::Key_F: 415 {Qt::Key_J, Settings::NativeKeyboard::J},
416 return Settings::NativeKeyboard::F; 416 {Qt::Key_K, Settings::NativeKeyboard::K},
417 case Qt::Key_G: 417 {Qt::Key_L, Settings::NativeKeyboard::L},
418 return Settings::NativeKeyboard::G; 418 {Qt::Key_M, Settings::NativeKeyboard::M},
419 case Qt::Key_H: 419 {Qt::Key_N, Settings::NativeKeyboard::N},
420 return Settings::NativeKeyboard::H; 420 {Qt::Key_O, Settings::NativeKeyboard::O},
421 case Qt::Key_I: 421 {Qt::Key_P, Settings::NativeKeyboard::P},
422 return Settings::NativeKeyboard::I; 422 {Qt::Key_Q, Settings::NativeKeyboard::Q},
423 case Qt::Key_J: 423 {Qt::Key_R, Settings::NativeKeyboard::R},
424 return Settings::NativeKeyboard::J; 424 {Qt::Key_S, Settings::NativeKeyboard::S},
425 case Qt::Key_K: 425 {Qt::Key_T, Settings::NativeKeyboard::T},
426 return Settings::NativeKeyboard::K; 426 {Qt::Key_U, Settings::NativeKeyboard::U},
427 case Qt::Key_L: 427 {Qt::Key_V, Settings::NativeKeyboard::V},
428 return Settings::NativeKeyboard::L; 428 {Qt::Key_W, Settings::NativeKeyboard::W},
429 case Qt::Key_M: 429 {Qt::Key_X, Settings::NativeKeyboard::X},
430 return Settings::NativeKeyboard::M; 430 {Qt::Key_Y, Settings::NativeKeyboard::Y},
431 case Qt::Key_N: 431 {Qt::Key_Z, Settings::NativeKeyboard::Z},
432 return Settings::NativeKeyboard::N; 432 {Qt::Key_1, Settings::NativeKeyboard::N1},
433 case Qt::Key_O: 433 {Qt::Key_2, Settings::NativeKeyboard::N2},
434 return Settings::NativeKeyboard::O; 434 {Qt::Key_3, Settings::NativeKeyboard::N3},
435 case Qt::Key_P: 435 {Qt::Key_4, Settings::NativeKeyboard::N4},
436 return Settings::NativeKeyboard::P; 436 {Qt::Key_5, Settings::NativeKeyboard::N5},
437 case Qt::Key_Q: 437 {Qt::Key_6, Settings::NativeKeyboard::N6},
438 return Settings::NativeKeyboard::Q; 438 {Qt::Key_7, Settings::NativeKeyboard::N7},
439 case Qt::Key_R: 439 {Qt::Key_8, Settings::NativeKeyboard::N8},
440 return Settings::NativeKeyboard::R; 440 {Qt::Key_9, Settings::NativeKeyboard::N9},
441 case Qt::Key_S: 441 {Qt::Key_0, Settings::NativeKeyboard::N0},
442 return Settings::NativeKeyboard::S; 442 {Qt::Key_Return, Settings::NativeKeyboard::Return},
443 case Qt::Key_T: 443 {Qt::Key_Escape, Settings::NativeKeyboard::Escape},
444 return Settings::NativeKeyboard::T; 444 {Qt::Key_Backspace, Settings::NativeKeyboard::Backspace},
445 case Qt::Key_U: 445 {Qt::Key_Tab, Settings::NativeKeyboard::Tab},
446 return Settings::NativeKeyboard::U; 446 {Qt::Key_Space, Settings::NativeKeyboard::Space},
447 case Qt::Key_V: 447 {Qt::Key_Minus, Settings::NativeKeyboard::Minus},
448 return Settings::NativeKeyboard::V; 448 {Qt::Key_Plus, Settings::NativeKeyboard::Plus},
449 case Qt::Key_W: 449 {Qt::Key_questiondown, Settings::NativeKeyboard::Plus},
450 return Settings::NativeKeyboard::W; 450 {Qt::Key_BracketLeft, Settings::NativeKeyboard::OpenBracket},
451 case Qt::Key_X: 451 {Qt::Key_BraceLeft, Settings::NativeKeyboard::OpenBracket},
452 return Settings::NativeKeyboard::X; 452 {Qt::Key_BracketRight, Settings::NativeKeyboard::CloseBracket},
453 case Qt::Key_Y: 453 {Qt::Key_BraceRight, Settings::NativeKeyboard::CloseBracket},
454 return Settings::NativeKeyboard::Y; 454 {Qt::Key_Bar, Settings::NativeKeyboard::Pipe},
455 case Qt::Key_Z: 455 {Qt::Key_Dead_Tilde, Settings::NativeKeyboard::Tilde},
456 return Settings::NativeKeyboard::Z; 456 {Qt::Key_Ntilde, Settings::NativeKeyboard::Semicolon},
457 case Qt::Key_1: 457 {Qt::Key_Semicolon, Settings::NativeKeyboard::Semicolon},
458 return Settings::NativeKeyboard::N1; 458 {Qt::Key_Apostrophe, Settings::NativeKeyboard::Quote},
459 case Qt::Key_2: 459 {Qt::Key_Dead_Grave, Settings::NativeKeyboard::Backquote},
460 return Settings::NativeKeyboard::N2; 460 {Qt::Key_Comma, Settings::NativeKeyboard::Comma},
461 case Qt::Key_3: 461 {Qt::Key_Period, Settings::NativeKeyboard::Period},
462 return Settings::NativeKeyboard::N3; 462 {Qt::Key_Slash, Settings::NativeKeyboard::Slash},
463 case Qt::Key_4: 463 {Qt::Key_CapsLock, Settings::NativeKeyboard::CapsLockKey},
464 return Settings::NativeKeyboard::N4; 464 {Qt::Key_F1, Settings::NativeKeyboard::F1},
465 case Qt::Key_5: 465 {Qt::Key_F2, Settings::NativeKeyboard::F2},
466 return Settings::NativeKeyboard::N5; 466 {Qt::Key_F3, Settings::NativeKeyboard::F3},
467 case Qt::Key_6: 467 {Qt::Key_F4, Settings::NativeKeyboard::F4},
468 return Settings::NativeKeyboard::N6; 468 {Qt::Key_F5, Settings::NativeKeyboard::F5},
469 case Qt::Key_7: 469 {Qt::Key_F6, Settings::NativeKeyboard::F6},
470 return Settings::NativeKeyboard::N7; 470 {Qt::Key_F7, Settings::NativeKeyboard::F7},
471 case Qt::Key_8: 471 {Qt::Key_F8, Settings::NativeKeyboard::F8},
472 return Settings::NativeKeyboard::N8; 472 {Qt::Key_F9, Settings::NativeKeyboard::F9},
473 case Qt::Key_9: 473 {Qt::Key_F10, Settings::NativeKeyboard::F10},
474 return Settings::NativeKeyboard::N9; 474 {Qt::Key_F11, Settings::NativeKeyboard::F11},
475 case Qt::Key_0: 475 {Qt::Key_F12, Settings::NativeKeyboard::F12},
476 return Settings::NativeKeyboard::N0; 476 {Qt::Key_Print, Settings::NativeKeyboard::PrintScreen},
477 case Qt::Key_Return: 477 {Qt::Key_ScrollLock, Settings::NativeKeyboard::ScrollLockKey},
478 return Settings::NativeKeyboard::Return; 478 {Qt::Key_Pause, Settings::NativeKeyboard::Pause},
479 case Qt::Key_Escape: 479 {Qt::Key_Insert, Settings::NativeKeyboard::Insert},
480 return Settings::NativeKeyboard::Escape; 480 {Qt::Key_Home, Settings::NativeKeyboard::Home},
481 case Qt::Key_Backspace: 481 {Qt::Key_PageUp, Settings::NativeKeyboard::PageUp},
482 return Settings::NativeKeyboard::Backspace; 482 {Qt::Key_Delete, Settings::NativeKeyboard::Delete},
483 case Qt::Key_Tab: 483 {Qt::Key_End, Settings::NativeKeyboard::End},
484 return Settings::NativeKeyboard::Tab; 484 {Qt::Key_PageDown, Settings::NativeKeyboard::PageDown},
485 case Qt::Key_Space: 485 {Qt::Key_Right, Settings::NativeKeyboard::Right},
486 return Settings::NativeKeyboard::Space; 486 {Qt::Key_Left, Settings::NativeKeyboard::Left},
487 case Qt::Key_Minus: 487 {Qt::Key_Down, Settings::NativeKeyboard::Down},
488 return Settings::NativeKeyboard::Minus; 488 {Qt::Key_Up, Settings::NativeKeyboard::Up},
489 case Qt::Key_Plus: 489 {Qt::Key_NumLock, Settings::NativeKeyboard::NumLockKey},
490 case Qt::Key_questiondown: 490 // Numpad keys are missing here
491 return Settings::NativeKeyboard::Plus; 491 {Qt::Key_F13, Settings::NativeKeyboard::F13},
492 case Qt::Key_BracketLeft: 492 {Qt::Key_F14, Settings::NativeKeyboard::F14},
493 case Qt::Key_BraceLeft: 493 {Qt::Key_F15, Settings::NativeKeyboard::F15},
494 return Settings::NativeKeyboard::OpenBracket; 494 {Qt::Key_F16, Settings::NativeKeyboard::F16},
495 case Qt::Key_BracketRight: 495 {Qt::Key_F17, Settings::NativeKeyboard::F17},
496 case Qt::Key_BraceRight: 496 {Qt::Key_F18, Settings::NativeKeyboard::F18},
497 return Settings::NativeKeyboard::CloseBracket; 497 {Qt::Key_F19, Settings::NativeKeyboard::F19},
498 case Qt::Key_Bar: 498 {Qt::Key_F20, Settings::NativeKeyboard::F20},
499 return Settings::NativeKeyboard::Pipe; 499 {Qt::Key_F21, Settings::NativeKeyboard::F21},
500 case Qt::Key_Dead_Tilde: 500 {Qt::Key_F22, Settings::NativeKeyboard::F22},
501 return Settings::NativeKeyboard::Tilde; 501 {Qt::Key_F23, Settings::NativeKeyboard::F23},
502 case Qt::Key_Ntilde: 502 {Qt::Key_F24, Settings::NativeKeyboard::F24},
503 case Qt::Key_Semicolon: 503 // {Qt::..., Settings::NativeKeyboard::KPComma},
504 return Settings::NativeKeyboard::Semicolon; 504 // {Qt::..., Settings::NativeKeyboard::Ro},
505 case Qt::Key_Apostrophe: 505 {Qt::Key_Hiragana_Katakana, Settings::NativeKeyboard::KatakanaHiragana},
506 return Settings::NativeKeyboard::Quote; 506 {Qt::Key_yen, Settings::NativeKeyboard::Yen},
507 case Qt::Key_Dead_Grave: 507 {Qt::Key_Henkan, Settings::NativeKeyboard::Henkan},
508 return Settings::NativeKeyboard::Backquote; 508 {Qt::Key_Muhenkan, Settings::NativeKeyboard::Muhenkan},
509 case Qt::Key_Comma: 509 // {Qt::..., Settings::NativeKeyboard::NumPadCommaPc98},
510 return Settings::NativeKeyboard::Comma; 510 {Qt::Key_Hangul, Settings::NativeKeyboard::HangulEnglish},
511 case Qt::Key_Period: 511 {Qt::Key_Hangul_Hanja, Settings::NativeKeyboard::Hanja},
512 return Settings::NativeKeyboard::Period; 512 {Qt::Key_Katakana, Settings::NativeKeyboard::KatakanaKey},
513 case Qt::Key_Slash: 513 {Qt::Key_Hiragana, Settings::NativeKeyboard::HiraganaKey},
514 return Settings::NativeKeyboard::Slash; 514 {Qt::Key_Zenkaku_Hankaku, Settings::NativeKeyboard::ZenkakuHankaku},
515 case Qt::Key_CapsLock: 515 // Modifier keys are handled by the modifier property
516 return Settings::NativeKeyboard::CapsLock; 516 };
517 case Qt::Key_F1: 517
518 return Settings::NativeKeyboard::F1; 518 for (const auto& [qkey, nkey] : key_map) {
519 case Qt::Key_F2: 519 if (qt_key == qkey) {
520 return Settings::NativeKeyboard::F2; 520 return nkey;
521 case Qt::Key_F3: 521 }
522 return Settings::NativeKeyboard::F3;
523 case Qt::Key_F4:
524 return Settings::NativeKeyboard::F4;
525 case Qt::Key_F5:
526 return Settings::NativeKeyboard::F5;
527 case Qt::Key_F6:
528 return Settings::NativeKeyboard::F6;
529 case Qt::Key_F7:
530 return Settings::NativeKeyboard::F7;
531 case Qt::Key_F8:
532 return Settings::NativeKeyboard::F8;
533 case Qt::Key_F9:
534 return Settings::NativeKeyboard::F9;
535 case Qt::Key_F10:
536 return Settings::NativeKeyboard::F10;
537 case Qt::Key_F11:
538 return Settings::NativeKeyboard::F11;
539 case Qt::Key_F12:
540 return Settings::NativeKeyboard::F12;
541 case Qt::Key_Print:
542 return Settings::NativeKeyboard::PrintScreen;
543 case Qt::Key_ScrollLock:
544 return Settings::NativeKeyboard::ScrollLock;
545 case Qt::Key_Pause:
546 return Settings::NativeKeyboard::Pause;
547 case Qt::Key_Insert:
548 return Settings::NativeKeyboard::Insert;
549 case Qt::Key_Home:
550 return Settings::NativeKeyboard::Home;
551 case Qt::Key_PageUp:
552 return Settings::NativeKeyboard::PageUp;
553 case Qt::Key_Delete:
554 return Settings::NativeKeyboard::Delete;
555 case Qt::Key_End:
556 return Settings::NativeKeyboard::End;
557 case Qt::Key_PageDown:
558 return Settings::NativeKeyboard::PageDown;
559 case Qt::Key_Right:
560 return Settings::NativeKeyboard::Right;
561 case Qt::Key_Left:
562 return Settings::NativeKeyboard::Left;
563 case Qt::Key_Down:
564 return Settings::NativeKeyboard::Down;
565 case Qt::Key_Up:
566 return Settings::NativeKeyboard::Up;
567 case Qt::Key_NumLock:
568 return Settings::NativeKeyboard::NumLock;
569 // Numpad keys are missing here
570 case Qt::Key_F13:
571 return Settings::NativeKeyboard::F13;
572 case Qt::Key_F14:
573 return Settings::NativeKeyboard::F14;
574 case Qt::Key_F15:
575 return Settings::NativeKeyboard::F15;
576 case Qt::Key_F16:
577 return Settings::NativeKeyboard::F16;
578 case Qt::Key_F17:
579 return Settings::NativeKeyboard::F17;
580 case Qt::Key_F18:
581 return Settings::NativeKeyboard::F18;
582 case Qt::Key_F19:
583 return Settings::NativeKeyboard::F19;
584 case Qt::Key_F20:
585 return Settings::NativeKeyboard::F20;
586 case Qt::Key_F21:
587 return Settings::NativeKeyboard::F21;
588 case Qt::Key_F22:
589 return Settings::NativeKeyboard::F22;
590 case Qt::Key_F23:
591 return Settings::NativeKeyboard::F23;
592 case Qt::Key_F24:
593 return Settings::NativeKeyboard::F24;
594 // case Qt:::
595 // return Settings::NativeKeyboard::KPComma;
596 // case Qt:::
597 // return Settings::NativeKeyboard::Ro;
598 case Qt::Key_Hiragana_Katakana:
599 return Settings::NativeKeyboard::KatakanaHiragana;
600 case Qt::Key_yen:
601 return Settings::NativeKeyboard::Yen;
602 case Qt::Key_Henkan:
603 return Settings::NativeKeyboard::Henkan;
604 case Qt::Key_Muhenkan:
605 return Settings::NativeKeyboard::Muhenkan;
606 // case Qt:::
607 // return Settings::NativeKeyboard::NumPadCommaPc98;
608 case Qt::Key_Hangul:
609 return Settings::NativeKeyboard::HangulEnglish;
610 case Qt::Key_Hangul_Hanja:
611 return Settings::NativeKeyboard::Hanja;
612 case Qt::Key_Katakana:
613 return Settings::NativeKeyboard::KatakanaKey;
614 case Qt::Key_Hiragana:
615 return Settings::NativeKeyboard::HiraganaKey;
616 case Qt::Key_Zenkaku_Hankaku:
617 return Settings::NativeKeyboard::ZenkakuHankaku;
618 // Modifier keys are handled by the modifier property
619 default:
620 return Settings::NativeKeyboard::None;
621 } 522 }
523
524 return Settings::NativeKeyboard::None;
622} 525}
623 526
624int GRenderWindow::QtModifierToSwitchModifier(Qt::KeyboardModifiers qt_modifiers) { 527int GRenderWindow::QtModifierToSwitchModifier(Qt::KeyboardModifiers qt_modifiers) {
diff --git a/src/yuzu/compatdb.cpp b/src/yuzu/compatdb.cpp
index f46fff340..b03e71248 100644
--- a/src/yuzu/compatdb.cpp
+++ b/src/yuzu/compatdb.cpp
@@ -15,12 +15,22 @@ CompatDB::CompatDB(Core::TelemetrySession& telemetry_session_, QWidget* parent)
15 : QWizard(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), 15 : QWizard(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint),
16 ui{std::make_unique<Ui::CompatDB>()}, telemetry_session{telemetry_session_} { 16 ui{std::make_unique<Ui::CompatDB>()}, telemetry_session{telemetry_session_} {
17 ui->setupUi(this); 17 ui->setupUi(this);
18 connect(ui->radioButton_Perfect, &QRadioButton::clicked, this, &CompatDB::EnableNext); 18
19 connect(ui->radioButton_Great, &QRadioButton::clicked, this, &CompatDB::EnableNext); 19 connect(ui->radioButton_GameBoot_Yes, &QRadioButton::clicked, this, &CompatDB::EnableNext);
20 connect(ui->radioButton_Okay, &QRadioButton::clicked, this, &CompatDB::EnableNext); 20 connect(ui->radioButton_GameBoot_No, &QRadioButton::clicked, this, &CompatDB::EnableNext);
21 connect(ui->radioButton_Bad, &QRadioButton::clicked, this, &CompatDB::EnableNext); 21 connect(ui->radioButton_Gameplay_Yes, &QRadioButton::clicked, this, &CompatDB::EnableNext);
22 connect(ui->radioButton_IntroMenu, &QRadioButton::clicked, this, &CompatDB::EnableNext); 22 connect(ui->radioButton_Gameplay_No, &QRadioButton::clicked, this, &CompatDB::EnableNext);
23 connect(ui->radioButton_WontBoot, &QRadioButton::clicked, this, &CompatDB::EnableNext); 23 connect(ui->radioButton_NoFreeze_Yes, &QRadioButton::clicked, this, &CompatDB::EnableNext);
24 connect(ui->radioButton_NoFreeze_No, &QRadioButton::clicked, this, &CompatDB::EnableNext);
25 connect(ui->radioButton_Complete_Yes, &QRadioButton::clicked, this, &CompatDB::EnableNext);
26 connect(ui->radioButton_Complete_No, &QRadioButton::clicked, this, &CompatDB::EnableNext);
27 connect(ui->radioButton_Graphical_Major, &QRadioButton::clicked, this, &CompatDB::EnableNext);
28 connect(ui->radioButton_Graphical_Minor, &QRadioButton::clicked, this, &CompatDB::EnableNext);
29 connect(ui->radioButton_Graphical_No, &QRadioButton::clicked, this, &CompatDB::EnableNext);
30 connect(ui->radioButton_Audio_Major, &QRadioButton::clicked, this, &CompatDB::EnableNext);
31 connect(ui->radioButton_Audio_Minor, &QRadioButton::clicked, this, &CompatDB::EnableNext);
32 connect(ui->radioButton_Audio_No, &QRadioButton::clicked, this, &CompatDB::EnableNext);
33
24 connect(button(NextButton), &QPushButton::clicked, this, &CompatDB::Submit); 34 connect(button(NextButton), &QPushButton::clicked, this, &CompatDB::Submit);
25 connect(&testcase_watcher, &QFutureWatcher<bool>::finished, this, 35 connect(&testcase_watcher, &QFutureWatcher<bool>::finished, this,
26 &CompatDB::OnTestcaseSubmitted); 36 &CompatDB::OnTestcaseSubmitted);
@@ -30,29 +40,82 @@ CompatDB::~CompatDB() = default;
30 40
31enum class CompatDBPage { 41enum class CompatDBPage {
32 Intro = 0, 42 Intro = 0,
33 Selection = 1, 43 GameBoot = 1,
34 Final = 2, 44 GamePlay = 2,
45 Freeze = 3,
46 Completion = 4,
47 Graphical = 5,
48 Audio = 6,
49 Final = 7,
35}; 50};
36 51
37void CompatDB::Submit() { 52void CompatDB::Submit() {
38 QButtonGroup* compatibility = new QButtonGroup(this); 53 QButtonGroup* compatibility_GameBoot = new QButtonGroup(this);
39 compatibility->addButton(ui->radioButton_Perfect, 0); 54 compatibility_GameBoot->addButton(ui->radioButton_GameBoot_Yes, 0);
40 compatibility->addButton(ui->radioButton_Great, 1); 55 compatibility_GameBoot->addButton(ui->radioButton_GameBoot_No, 1);
41 compatibility->addButton(ui->radioButton_Okay, 2); 56
42 compatibility->addButton(ui->radioButton_Bad, 3); 57 QButtonGroup* compatibility_Gameplay = new QButtonGroup(this);
43 compatibility->addButton(ui->radioButton_IntroMenu, 4); 58 compatibility_Gameplay->addButton(ui->radioButton_Gameplay_Yes, 0);
44 compatibility->addButton(ui->radioButton_WontBoot, 5); 59 compatibility_Gameplay->addButton(ui->radioButton_Gameplay_No, 1);
60
61 QButtonGroup* compatibility_NoFreeze = new QButtonGroup(this);
62 compatibility_NoFreeze->addButton(ui->radioButton_NoFreeze_Yes, 0);
63 compatibility_NoFreeze->addButton(ui->radioButton_NoFreeze_No, 1);
64
65 QButtonGroup* compatibility_Complete = new QButtonGroup(this);
66 compatibility_Complete->addButton(ui->radioButton_Complete_Yes, 0);
67 compatibility_Complete->addButton(ui->radioButton_Complete_No, 1);
68
69 QButtonGroup* compatibility_Graphical = new QButtonGroup(this);
70 compatibility_Graphical->addButton(ui->radioButton_Graphical_Major, 0);
71 compatibility_Graphical->addButton(ui->radioButton_Graphical_Minor, 1);
72 compatibility_Graphical->addButton(ui->radioButton_Graphical_No, 2);
73
74 QButtonGroup* compatibility_Audio = new QButtonGroup(this);
75 compatibility_Audio->addButton(ui->radioButton_Audio_Major, 0);
76 compatibility_Graphical->addButton(ui->radioButton_Audio_Minor, 1);
77 compatibility_Audio->addButton(ui->radioButton_Audio_No, 2);
78
79 const int compatiblity = static_cast<int>(CalculateCompatibility());
80
45 switch ((static_cast<CompatDBPage>(currentId()))) { 81 switch ((static_cast<CompatDBPage>(currentId()))) {
46 case CompatDBPage::Selection: 82 case CompatDBPage::Intro:
47 if (compatibility->checkedId() == -1) { 83 break;
84 case CompatDBPage::GameBoot:
85 if (compatibility_GameBoot->checkedId() == -1) {
86 button(NextButton)->setEnabled(false);
87 }
88 break;
89 case CompatDBPage::GamePlay:
90 if (compatibility_Gameplay->checkedId() == -1) {
91 button(NextButton)->setEnabled(false);
92 }
93 break;
94 case CompatDBPage::Freeze:
95 if (compatibility_NoFreeze->checkedId() == -1) {
96 button(NextButton)->setEnabled(false);
97 }
98 break;
99 case CompatDBPage::Completion:
100 if (compatibility_Complete->checkedId() == -1) {
101 button(NextButton)->setEnabled(false);
102 }
103 break;
104 case CompatDBPage::Graphical:
105 if (compatibility_Graphical->checkedId() == -1) {
106 button(NextButton)->setEnabled(false);
107 }
108 break;
109 case CompatDBPage::Audio:
110 if (compatibility_Audio->checkedId() == -1) {
48 button(NextButton)->setEnabled(false); 111 button(NextButton)->setEnabled(false);
49 } 112 }
50 break; 113 break;
51 case CompatDBPage::Final: 114 case CompatDBPage::Final:
52 back(); 115 back();
53 LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId()); 116 LOG_INFO(Frontend, "Compatibility Rating: {}", compatiblity);
54 telemetry_session.AddField(Common::Telemetry::FieldType::UserFeedback, "Compatibility", 117 telemetry_session.AddField(Common::Telemetry::FieldType::UserFeedback, "Compatibility",
55 compatibility->checkedId()); 118 compatiblity);
56 119
57 button(NextButton)->setEnabled(false); 120 button(NextButton)->setEnabled(false);
58 button(NextButton)->setText(tr("Submitting")); 121 button(NextButton)->setText(tr("Submitting"));
@@ -66,6 +129,66 @@ void CompatDB::Submit() {
66 } 129 }
67} 130}
68 131
132int CompatDB::nextId() const {
133 switch ((static_cast<CompatDBPage>(currentId()))) {
134 case CompatDBPage::Intro:
135 return static_cast<int>(CompatDBPage::GameBoot);
136 case CompatDBPage::GameBoot:
137 if (ui->radioButton_GameBoot_No->isChecked()) {
138 return static_cast<int>(CompatDBPage::Final);
139 }
140 return static_cast<int>(CompatDBPage::GamePlay);
141 case CompatDBPage::GamePlay:
142 if (ui->radioButton_Gameplay_No->isChecked()) {
143 return static_cast<int>(CompatDBPage::Final);
144 }
145 return static_cast<int>(CompatDBPage::Freeze);
146 case CompatDBPage::Freeze:
147 if (ui->radioButton_NoFreeze_No->isChecked()) {
148 return static_cast<int>(CompatDBPage::Final);
149 }
150 return static_cast<int>(CompatDBPage::Completion);
151 case CompatDBPage::Completion:
152 if (ui->radioButton_Complete_No->isChecked()) {
153 return static_cast<int>(CompatDBPage::Final);
154 }
155 return static_cast<int>(CompatDBPage::Graphical);
156 case CompatDBPage::Graphical:
157 return static_cast<int>(CompatDBPage::Audio);
158 case CompatDBPage::Audio:
159 return static_cast<int>(CompatDBPage::Final);
160 case CompatDBPage::Final:
161 return -1;
162 default:
163 LOG_ERROR(Frontend, "Unexpected page: {}", currentId());
164 return static_cast<int>(CompatDBPage::Intro);
165 }
166}
167
168CompatibilityStatus CompatDB::CalculateCompatibility() const {
169 if (ui->radioButton_GameBoot_No->isChecked()) {
170 return CompatibilityStatus::WontBoot;
171 }
172
173 if (ui->radioButton_Gameplay_No->isChecked()) {
174 return CompatibilityStatus::IntroMenu;
175 }
176
177 if (ui->radioButton_NoFreeze_No->isChecked() || ui->radioButton_Complete_No->isChecked()) {
178 return CompatibilityStatus::Ingame;
179 }
180
181 if (ui->radioButton_Graphical_Major->isChecked() || ui->radioButton_Audio_Major->isChecked()) {
182 return CompatibilityStatus::Ingame;
183 }
184
185 if (ui->radioButton_Graphical_Minor->isChecked() || ui->radioButton_Audio_Minor->isChecked()) {
186 return CompatibilityStatus::Playable;
187 }
188
189 return CompatibilityStatus::Perfect;
190}
191
69void CompatDB::OnTestcaseSubmitted() { 192void CompatDB::OnTestcaseSubmitted() {
70 if (!testcase_watcher.result()) { 193 if (!testcase_watcher.result()) {
71 QMessageBox::critical(this, tr("Communication error"), 194 QMessageBox::critical(this, tr("Communication error"),
diff --git a/src/yuzu/compatdb.h b/src/yuzu/compatdb.h
index 3252fc47a..37e11278b 100644
--- a/src/yuzu/compatdb.h
+++ b/src/yuzu/compatdb.h
@@ -12,12 +12,22 @@ namespace Ui {
12class CompatDB; 12class CompatDB;
13} 13}
14 14
15enum class CompatibilityStatus {
16 Perfect = 0,
17 Playable = 1,
18 // Unused: Okay = 2,
19 Ingame = 3,
20 IntroMenu = 4,
21 WontBoot = 5,
22};
23
15class CompatDB : public QWizard { 24class CompatDB : public QWizard {
16 Q_OBJECT 25 Q_OBJECT
17 26
18public: 27public:
19 explicit CompatDB(Core::TelemetrySession& telemetry_session_, QWidget* parent = nullptr); 28 explicit CompatDB(Core::TelemetrySession& telemetry_session_, QWidget* parent = nullptr);
20 ~CompatDB(); 29 ~CompatDB();
30 int nextId() const override;
21 31
22private: 32private:
23 QFutureWatcher<bool> testcase_watcher; 33 QFutureWatcher<bool> testcase_watcher;
@@ -25,6 +35,7 @@ private:
25 std::unique_ptr<Ui::CompatDB> ui; 35 std::unique_ptr<Ui::CompatDB> ui;
26 36
27 void Submit(); 37 void Submit();
38 CompatibilityStatus CalculateCompatibility() const;
28 void OnTestcaseSubmitted(); 39 void OnTestcaseSubmitted();
29 void EnableNext(); 40 void EnableNext();
30 41
diff --git a/src/yuzu/compatdb.ui b/src/yuzu/compatdb.ui
index 3ca55eda6..d11669df2 100644
--- a/src/yuzu/compatdb.ui
+++ b/src/yuzu/compatdb.ui
@@ -58,128 +58,311 @@
58 </item> 58 </item>
59 </layout> 59 </layout>
60 </widget> 60 </widget>
61 <widget class="QWizardPage" name="wizard_Report"> 61 <widget class="QWizardPage" name="wizard_GameBoot">
62 <property name="title"> 62 <property name="title">
63 <string>Report Game Compatibility</string> 63 <string>Report Game Compatibility</string>
64 </property> 64 </property>
65 <attribute name="pageId"> 65 <attribute name="pageId">
66 <string notr="true">1</string> 66 <string notr="true">1</string>
67 </attribute> 67 </attribute>
68 <layout class="QFormLayout" name="formLayout"> 68 <layout class="QFormLayout" name="formLayout1">
69 <item row="0" column="0" colspan="2">
70 <widget class="QLabel" name="lbl_Independent1">
71 <property name="font">
72 <font>
73 <pointsize>10</pointsize>
74 </font>
75 </property>
76 <property name="text">
77 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Does the game boot?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
78 </property>
79 <property name="wordWrap">
80 <bool>true</bool>
81 </property>
82 </widget>
83 </item>
84 <item row="1" column="0" colspan="2">
85 <spacer name="verticalSpacer1">
86 <property name="orientation">
87 <enum>Qt::Vertical</enum>
88 </property>
89 <property name="sizeHint" stdset="0">
90 <size>
91 <width>20</width>
92 <height>0</height>
93 </size>
94 </property>
95 </spacer>
96 </item>
69 <item row="2" column="0"> 97 <item row="2" column="0">
70 <widget class="QRadioButton" name="radioButton_Perfect"> 98 <widget class="QRadioButton" name="radioButton_GameBoot_Yes">
71 <property name="text"> 99 <property name="text">
72 <string>Perfect</string> 100 <string>Yes The game starts to output video or audio</string>
73 </property> 101 </property>
74 </widget> 102 </widget>
75 </item> 103 </item>
76 <item row="2" column="1"> 104 <item row="4" column="0">
77 <widget class="QLabel" name="lbl_Perfect"> 105 <widget class="QRadioButton" name="radioButton_GameBoot_No">
78 <property name="text"> 106 <property name="text">
79 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions flawlessly with no audio or graphical glitches.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> 107 <string>No The game doesn't get past the &quot;Launching...&quot; screen</string>
80 </property> 108 </property>
81 <property name="wordWrap"> 109 </widget>
82 <bool>true</bool> 110 </item>
111 </layout>
112 </widget>
113 <widget class="QWizardPage" name="wizard_GamePlay">
114 <property name="title">
115 <string>Report Game Compatibility</string>
116 </property>
117 <attribute name="pageId">
118 <string notr="true">2</string>
119 </attribute>
120 <layout class="QFormLayout" name="formLayout2">
121 <item row="2" column="0">
122 <widget class="QRadioButton" name="radioButton_Gameplay_Yes">
123 <property name="text">
124 <string>Yes The game gets past the intro/menu and into gameplay</string>
83 </property> 125 </property>
84 </widget> 126 </widget>
85 </item> 127 </item>
86 <item row="4" column="0"> 128 <item row="4" column="0">
87 <widget class="QRadioButton" name="radioButton_Great"> 129 <widget class="QRadioButton" name="radioButton_Gameplay_No">
88 <property name="text"> 130 <property name="text">
89 <string>Great</string> 131 <string>No The game crashes or freezes while loading or using the menu</string>
90 </property> 132 </property>
91 </widget> 133 </widget>
92 </item> 134 </item>
93 <item row="4" column="1"> 135 <item row="0" column="0" colspan="2">
94 <widget class="QLabel" name="lbl_Great"> 136 <widget class="QLabel" name="lbl_Independent2">
137 <property name="font">
138 <font>
139 <pointsize>10</pointsize>
140 </font>
141 </property>
95 <property name="text"> 142 <property name="text">
96 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> 143 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Does the game reach gameplay?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
97 </property> 144 </property>
98 <property name="wordWrap"> 145 <property name="wordWrap">
99 <bool>true</bool> 146 <bool>true</bool>
100 </property> 147 </property>
101 </widget> 148 </widget>
102 </item> 149 </item>
103 <item row="5" column="0"> 150 <item row="1" column="0" colspan="2">
104 <widget class="QRadioButton" name="radioButton_Okay"> 151 <spacer name="verticalSpacer2">
152 <property name="orientation">
153 <enum>Qt::Vertical</enum>
154 </property>
155 <property name="sizeHint" stdset="0">
156 <size>
157 <width>20</width>
158 <height>0</height>
159 </size>
160 </property>
161 </spacer>
162 </item>
163 </layout>
164 </widget>
165 <widget class="QWizardPage" name="wizard_NoFreeze">
166 <property name="title">
167 <string>Report Game Compatibility</string>
168 </property>
169 <attribute name="pageId">
170 <string notr="true">3</string>
171 </attribute>
172 <layout class="QFormLayout" name="formLayout3">
173 <item row="2" column="0">
174 <widget class="QRadioButton" name="radioButton_NoFreeze_Yes">
105 <property name="text"> 175 <property name="text">
106 <string>Okay</string> 176 <string>Yes The game works without crashes</string>
107 </property> 177 </property>
108 </widget> 178 </widget>
109 </item> 179 </item>
110 <item row="5" column="1"> 180 <item row="4" column="0">
111 <widget class="QLabel" name="lbl_Okay"> 181 <widget class="QRadioButton" name="radioButton_NoFreeze_No">
182 <property name="text">
183 <string>No The game crashes or freezes during gameplay</string>
184 </property>
185 </widget>
186 </item>
187 <item row="0" column="0" colspan="2">
188 <widget class="QLabel" name="lbl_Independent3">
189 <property name="font">
190 <font>
191 <pointsize>10</pointsize>
192 </font>
193 </property>
112 <property name="text"> 194 <property name="text">
113 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> 195 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Does the game work without crashing, freezing or locking up during gameplay?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
114 </property> 196 </property>
115 <property name="wordWrap"> 197 <property name="wordWrap">
116 <bool>true</bool> 198 <bool>true</bool>
117 </property> 199 </property>
118 </widget> 200 </widget>
119 </item> 201 </item>
120 <item row="6" column="0"> 202 <item row="1" column="0" colspan="2">
121 <widget class="QRadioButton" name="radioButton_Bad"> 203 <spacer name="verticalSpacer3">
204 <property name="orientation">
205 <enum>Qt::Vertical</enum>
206 </property>
207 <property name="sizeHint" stdset="0">
208 <size>
209 <width>20</width>
210 <height>0</height>
211 </size>
212 </property>
213 </spacer>
214 </item>
215 </layout>
216 </widget>
217 <widget class="QWizardPage" name="wizard_Complete">
218 <property name="title">
219 <string>Report Game Compatibility</string>
220 </property>
221 <attribute name="pageId">
222 <string notr="true">4</string>
223 </attribute>
224 <layout class="QFormLayout" name="formLayout4">
225 <item row="2" column="0">
226 <widget class="QRadioButton" name="radioButton_Complete_Yes">
122 <property name="text"> 227 <property name="text">
123 <string>Bad</string> 228 <string>Yes The game can be finished without any workarounds</string>
124 </property> 229 </property>
125 </widget> 230 </widget>
126 </item> 231 </item>
127 <item row="6" column="1"> 232 <item row="4" column="0">
128 <widget class="QLabel" name="lbl_Bad"> 233 <widget class="QRadioButton" name="radioButton_Complete_No">
234 <property name="text">
235 <string>No The game can't progress past a certain area</string>
236 </property>
237 </widget>
238 </item>
239 <item row="0" column="0" colspan="2">
240 <widget class="QLabel" name="lbl_Independent4">
241 <property name="font">
242 <font>
243 <pointsize>10</pointsize>
244 </font>
245 </property>
129 <property name="text"> 246 <property name="text">
130 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> 247 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Is the game completely playable from start to finish?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
131 </property> 248 </property>
132 <property name="wordWrap"> 249 <property name="wordWrap">
133 <bool>true</bool> 250 <bool>true</bool>
134 </property> 251 </property>
135 </widget> 252 </widget>
136 </item> 253 </item>
137 <item row="7" column="0"> 254 <item row="1" column="0" colspan="2">
138 <widget class="QRadioButton" name="radioButton_IntroMenu"> 255 <spacer name="verticalSpacer4">
256 <property name="orientation">
257 <enum>Qt::Vertical</enum>
258 </property>
259 <property name="sizeHint" stdset="0">
260 <size>
261 <width>20</width>
262 <height>0</height>
263 </size>
264 </property>
265 </spacer>
266 </item>
267 </layout>
268 </widget>
269 <widget class="QWizardPage" name="wizard_Graphical">
270 <property name="title">
271 <string>Report Game Compatibility</string>
272 </property>
273 <attribute name="pageId">
274 <string notr="true">5</string>
275 </attribute>
276 <layout class="QFormLayout" name="formLayout5">
277 <item row="2" column="0">
278 <widget class="QRadioButton" name="radioButton_Graphical_Major">
279 <property name="text">
280 <string>Major The game has major graphical errors</string>
281 </property>
282 </widget>
283 </item>
284 <item row="4" column="0">
285 <widget class="QRadioButton" name="radioButton_Graphical_Minor">
139 <property name="text"> 286 <property name="text">
140 <string>Intro/Menu</string> 287 <string>Minor The game has minor graphical errors</string>
141 </property> 288 </property>
142 </widget> 289 </widget>
143 </item> 290 </item>
144 <item row="7" column="1"> 291 <item row="6" column="0">
145 <widget class="QLabel" name="lbl_IntroMenu"> 292 <widget class="QRadioButton" name="radioButton_Graphical_No">
293 <property name="text">
294 <string>None Everything is rendered as it looks on the Nintendo Switch</string>
295 </property>
296 </widget>
297 </item>
298 <item row="0" column="0" colspan="2">
299 <widget class="QLabel" name="lbl_Independent5">
300 <property name="font">
301 <font>
302 <pointsize>10</pointsize>
303 </font>
304 </property>
146 <property name="text"> 305 <property name="text">
147 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> 306 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Does the game have any graphical glitches?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
148 </property> 307 </property>
149 <property name="wordWrap"> 308 <property name="wordWrap">
150 <bool>true</bool> 309 <bool>true</bool>
151 </property> 310 </property>
152 </widget> 311 </widget>
153 </item> 312 </item>
154 <item row="8" column="0"> 313 <item row="1" column="0" colspan="2">
155 <widget class="QRadioButton" name="radioButton_WontBoot"> 314 <spacer name="verticalSpacer5">
156 <property name="text"> 315 <property name="orientation">
157 <string>Won't Boot</string> 316 <enum>Qt::Vertical</enum>
158 </property> 317 </property>
159 <property name="checkable"> 318 <property name="sizeHint" stdset="0">
160 <bool>true</bool> 319 <size>
320 <width>20</width>
321 <height>0</height>
322 </size>
161 </property> 323 </property>
162 <property name="checked"> 324 </spacer>
163 <bool>false</bool> 325 </item>
326 </layout>
327 </widget>
328 <widget class="QWizardPage" name="wizard_Audio">
329 <property name="title">
330 <string>Report Game Compatibility</string>
331 </property>
332 <attribute name="pageId">
333 <string notr="true">6</string>
334 </attribute>
335 <layout class="QFormLayout" name="formLayout6">
336 <item row="2" column="0">
337 <widget class="QRadioButton" name="radioButton_Audio_Major">
338 <property name="text">
339 <string>Major The game has major audio errors</string>
340 </property>
341 </widget>
342 </item>
343 <item row="4" column="0">
344 <widget class="QRadioButton" name="radioButton_Audio_Minor">
345 <property name="text">
346 <string>Minor The game has minor audio errors</string>
164 </property> 347 </property>
165 </widget> 348 </widget>
166 </item> 349 </item>
167 <item row="8" column="1"> 350 <item row="6" column="0">
168 <widget class="QLabel" name="lbl_WontBoot"> 351 <widget class="QRadioButton" name="radioButton_Audio_No">
169 <property name="text"> 352 <property name="text">
170 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The game crashes when attempting to startup.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> 353 <string>None Audio is played perfectly</string>
171 </property> 354 </property>
172 </widget> 355 </widget>
173 </item> 356 </item>
174 <item row="0" column="0" colspan="2"> 357 <item row="0" column="0" colspan="2">
175 <widget class="QLabel" name="lbl_Independent"> 358 <widget class="QLabel" name="lbl_Independent6">
176 <property name="font"> 359 <property name="font">
177 <font> 360 <font>
178 <pointsize>10</pointsize> 361 <pointsize>10</pointsize>
179 </font> 362 </font>
180 </property> 363 </property>
181 <property name="text"> 364 <property name="text">
182 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> 365 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Does the game have any audio glitches / missing effects?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
183 </property> 366 </property>
184 <property name="wordWrap"> 367 <property name="wordWrap">
185 <bool>true</bool> 368 <bool>true</bool>
@@ -187,7 +370,7 @@
187 </widget> 370 </widget>
188 </item> 371 </item>
189 <item row="1" column="0" colspan="2"> 372 <item row="1" column="0" colspan="2">
190 <spacer name="verticalSpacer"> 373 <spacer name="verticalSpacer6">
191 <property name="orientation"> 374 <property name="orientation">
192 <enum>Qt::Vertical</enum> 375 <enum>Qt::Vertical</enum>
193 </property> 376 </property>
@@ -206,7 +389,7 @@
206 <string>Thank you for your submission!</string> 389 <string>Thank you for your submission!</string>
207 </property> 390 </property>
208 <attribute name="pageId"> 391 <attribute name="pageId">
209 <string notr="true">2</string> 392 <string notr="true">7</string>
210 </attribute> 393 </attribute>
211 </widget> 394 </widget>
212 </widget> 395 </widget>
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h
index 6198d1e4e..1800f090f 100644
--- a/src/yuzu/game_list_p.h
+++ b/src/yuzu/game_list_p.h
@@ -145,12 +145,14 @@ public:
145 const char* tooltip; 145 const char* tooltip;
146 }; 146 };
147 // clang-format off 147 // clang-format off
148 const auto ingame_status =
149 CompatStatus{QStringLiteral("#f2d624"), QT_TR_NOOP("Ingame"), QT_TR_NOOP("Game starts, but crashes or major glitches prevent it from being completed.")};
148 static const std::map<QString, CompatStatus> status_data = { 150 static const std::map<QString, CompatStatus> status_data = {
149 {QStringLiteral("0"), {QStringLiteral("#5c93ed"), QT_TR_NOOP("Perfect"), QT_TR_NOOP("Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without\nany workarounds needed.")}}, 151 {QStringLiteral("0"), {QStringLiteral("#5c93ed"), QT_TR_NOOP("Perfect"), QT_TR_NOOP("Game can be played without issues.")}},
150 {QStringLiteral("1"), {QStringLiteral("#47d35c"), QT_TR_NOOP("Great"), QT_TR_NOOP("Game functions with minor graphical or audio glitches and is playable from start to finish. May require some\nworkarounds.")}}, 152 {QStringLiteral("1"), {QStringLiteral("#47d35c"), QT_TR_NOOP("Playable"), QT_TR_NOOP("Game functions with minor graphical or audio glitches and is playable from start to finish.")}},
151 {QStringLiteral("2"), {QStringLiteral("#94b242"), QT_TR_NOOP("Okay"), QT_TR_NOOP("Game functions with major graphical or audio glitches, but game is playable from start to finish with\nworkarounds.")}}, 153 {QStringLiteral("2"), ingame_status},
152 {QStringLiteral("3"), {QStringLiteral("#f2d624"), QT_TR_NOOP("Bad"), QT_TR_NOOP("Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches\neven with workarounds.")}}, 154 {QStringLiteral("3"), ingame_status}, // Fallback for the removed "Okay" category
153 {QStringLiteral("4"), {QStringLiteral("#FF0000"), QT_TR_NOOP("Intro/Menu"), QT_TR_NOOP("Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start\nScreen.")}}, 155 {QStringLiteral("4"), {QStringLiteral("#FF0000"), QT_TR_NOOP("Intro/Menu"), QT_TR_NOOP("Game loads, but is unable to progress past the Start Screen.")}},
154 {QStringLiteral("5"), {QStringLiteral("#828282"), QT_TR_NOOP("Won't Boot"), QT_TR_NOOP("The game crashes when attempting to startup.")}}, 156 {QStringLiteral("5"), {QStringLiteral("#828282"), QT_TR_NOOP("Won't Boot"), QT_TR_NOOP("The game crashes when attempting to startup.")}},
155 {QStringLiteral("99"), {QStringLiteral("#000000"), QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}}, 157 {QStringLiteral("99"), {QStringLiteral("#000000"), QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}},
156 }; 158 };
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 59e56633a..032ff1cbc 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -342,6 +342,7 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan
342 const auto override_build = 342 const auto override_build =
343 fmt::format(fmt::runtime(std::string(Common::g_title_bar_format_idle)), build_id); 343 fmt::format(fmt::runtime(std::string(Common::g_title_bar_format_idle)), build_id);
344 const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build; 344 const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build;
345 const auto processor_count = std::thread::hardware_concurrency();
345 346
346 LOG_INFO(Frontend, "yuzu Version: {}", yuzu_build_version); 347 LOG_INFO(Frontend, "yuzu Version: {}", yuzu_build_version);
347 LogRuntimes(); 348 LogRuntimes();
@@ -361,6 +362,11 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan
361 } 362 }
362 LOG_INFO(Frontend, "Host CPU: {}", cpu_string); 363 LOG_INFO(Frontend, "Host CPU: {}", cpu_string);
363#endif 364#endif
365
366 if (std::optional<int> processor_core = Common::GetProcessorCount()) {
367 LOG_INFO(Frontend, "Host CPU Cores: {}", *processor_core);
368 }
369 LOG_INFO(Frontend, "Host CPU Threads: {}", processor_count);
364 LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString()); 370 LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString());
365 LOG_INFO(Frontend, "Host RAM: {:.2f} GiB", 371 LOG_INFO(Frontend, "Host RAM: {:.2f} GiB",
366 Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB}); 372 Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB});
@@ -2018,38 +2024,50 @@ static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src
2018 return true; 2024 return true;
2019} 2025}
2020 2026
2027QString GMainWindow::GetGameListErrorRemoving(InstalledEntryType type) const {
2028 switch (type) {
2029 case InstalledEntryType::Game:
2030 return tr("Error Removing Contents");
2031 case InstalledEntryType::Update:
2032 return tr("Error Removing Update");
2033 case InstalledEntryType::AddOnContent:
2034 return tr("Error Removing DLC");
2035 default:
2036 return QStringLiteral("Error Removing <Invalid Type>");
2037 }
2038}
2021void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type) { 2039void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type) {
2022 const QString entry_type = [type] { 2040 const QString entry_question = [type] {
2023 switch (type) { 2041 switch (type) {
2024 case InstalledEntryType::Game: 2042 case InstalledEntryType::Game:
2025 return tr("Contents"); 2043 return tr("Remove Installed Game Contents?");
2026 case InstalledEntryType::Update: 2044 case InstalledEntryType::Update:
2027 return tr("Update"); 2045 return tr("Remove Installed Game Update?");
2028 case InstalledEntryType::AddOnContent: 2046 case InstalledEntryType::AddOnContent:
2029 return tr("DLC"); 2047 return tr("Remove Installed Game DLC?");
2030 default: 2048 default:
2031 return QString{}; 2049 return QStringLiteral("Remove Installed Game <Invalid Type>?");
2032 } 2050 }
2033 }(); 2051 }();
2034 2052
2035 if (QMessageBox::question( 2053 if (QMessageBox::question(this, tr("Remove Entry"), entry_question,
2036 this, tr("Remove Entry"), tr("Remove Installed Game %1?").arg(entry_type), 2054 QMessageBox::Yes | QMessageBox::No,
2037 QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) { 2055 QMessageBox::No) != QMessageBox::Yes) {
2038 return; 2056 return;
2039 } 2057 }
2040 2058
2041 switch (type) { 2059 switch (type) {
2042 case InstalledEntryType::Game: 2060 case InstalledEntryType::Game:
2043 RemoveBaseContent(program_id, entry_type); 2061 RemoveBaseContent(program_id, type);
2044 [[fallthrough]]; 2062 [[fallthrough]];
2045 case InstalledEntryType::Update: 2063 case InstalledEntryType::Update:
2046 RemoveUpdateContent(program_id, entry_type); 2064 RemoveUpdateContent(program_id, type);
2047 if (type != InstalledEntryType::Game) { 2065 if (type != InstalledEntryType::Game) {
2048 break; 2066 break;
2049 } 2067 }
2050 [[fallthrough]]; 2068 [[fallthrough]];
2051 case InstalledEntryType::AddOnContent: 2069 case InstalledEntryType::AddOnContent:
2052 RemoveAddOnContent(program_id, entry_type); 2070 RemoveAddOnContent(program_id, type);
2053 break; 2071 break;
2054 } 2072 }
2055 Common::FS::RemoveDirRecursively(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / 2073 Common::FS::RemoveDirRecursively(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) /
@@ -2057,7 +2075,7 @@ void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryT
2057 game_list->PopulateAsync(UISettings::values.game_dirs); 2075 game_list->PopulateAsync(UISettings::values.game_dirs);
2058} 2076}
2059 2077
2060void GMainWindow::RemoveBaseContent(u64 program_id, const QString& entry_type) { 2078void GMainWindow::RemoveBaseContent(u64 program_id, InstalledEntryType type) {
2061 const auto& fs_controller = system->GetFileSystemController(); 2079 const auto& fs_controller = system->GetFileSystemController();
2062 const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(program_id) || 2080 const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(program_id) ||
2063 fs_controller.GetSDMCContents()->RemoveExistingEntry(program_id); 2081 fs_controller.GetSDMCContents()->RemoveExistingEntry(program_id);
@@ -2067,12 +2085,12 @@ void GMainWindow::RemoveBaseContent(u64 program_id, const QString& entry_type) {
2067 tr("Successfully removed the installed base game.")); 2085 tr("Successfully removed the installed base game."));
2068 } else { 2086 } else {
2069 QMessageBox::warning( 2087 QMessageBox::warning(
2070 this, tr("Error Removing %1").arg(entry_type), 2088 this, GetGameListErrorRemoving(type),
2071 tr("The base game is not installed in the NAND and cannot be removed.")); 2089 tr("The base game is not installed in the NAND and cannot be removed."));
2072 } 2090 }
2073} 2091}
2074 2092
2075void GMainWindow::RemoveUpdateContent(u64 program_id, const QString& entry_type) { 2093void GMainWindow::RemoveUpdateContent(u64 program_id, InstalledEntryType type) {
2076 const auto update_id = program_id | 0x800; 2094 const auto update_id = program_id | 0x800;
2077 const auto& fs_controller = system->GetFileSystemController(); 2095 const auto& fs_controller = system->GetFileSystemController();
2078 const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(update_id) || 2096 const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(update_id) ||
@@ -2082,12 +2100,12 @@ void GMainWindow::RemoveUpdateContent(u64 program_id, const QString& entry_type)
2082 QMessageBox::information(this, tr("Successfully Removed"), 2100 QMessageBox::information(this, tr("Successfully Removed"),
2083 tr("Successfully removed the installed update.")); 2101 tr("Successfully removed the installed update."));
2084 } else { 2102 } else {
2085 QMessageBox::warning(this, tr("Error Removing %1").arg(entry_type), 2103 QMessageBox::warning(this, GetGameListErrorRemoving(type),
2086 tr("There is no update installed for this title.")); 2104 tr("There is no update installed for this title."));
2087 } 2105 }
2088} 2106}
2089 2107
2090void GMainWindow::RemoveAddOnContent(u64 program_id, const QString& entry_type) { 2108void GMainWindow::RemoveAddOnContent(u64 program_id, InstalledEntryType type) {
2091 u32 count{}; 2109 u32 count{};
2092 const auto& fs_controller = system->GetFileSystemController(); 2110 const auto& fs_controller = system->GetFileSystemController();
2093 const auto dlc_entries = system->GetContentProvider().ListEntriesFilter( 2111 const auto dlc_entries = system->GetContentProvider().ListEntriesFilter(
@@ -2105,7 +2123,7 @@ void GMainWindow::RemoveAddOnContent(u64 program_id, const QString& entry_type)
2105 } 2123 }
2106 2124
2107 if (count == 0) { 2125 if (count == 0) {
2108 QMessageBox::warning(this, tr("Error Removing %1").arg(entry_type), 2126 QMessageBox::warning(this, GetGameListErrorRemoving(type),
2109 tr("There are no DLC installed for this title.")); 2127 tr("There are no DLC installed for this title."));
2110 return; 2128 return;
2111 } 2129 }
@@ -2803,6 +2821,20 @@ void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_tex
2803} 2821}
2804 2822
2805void GMainWindow::OnMenuReportCompatibility() { 2823void GMainWindow::OnMenuReportCompatibility() {
2824 const auto& caps = Common::GetCPUCaps();
2825 const bool has_fma = caps.fma || caps.fma4;
2826 const auto processor_count = std::thread::hardware_concurrency();
2827 const bool has_4threads = processor_count == 0 || processor_count >= 4;
2828 const bool has_8gb_ram = Common::GetMemInfo().TotalPhysicalMemory >= 8_GiB;
2829 const bool has_broken_vulkan = UISettings::values.has_broken_vulkan;
2830
2831 if (!has_fma || !has_4threads || !has_8gb_ram || has_broken_vulkan) {
2832 QMessageBox::critical(this, tr("Hardware requirements not met"),
2833 tr("Your system does not meet the recommended hardware requirements. "
2834 "Compatibility reporting has been disabled."));
2835 return;
2836 }
2837
2806 if (!Settings::values.yuzu_token.GetValue().empty() && 2838 if (!Settings::values.yuzu_token.GetValue().empty() &&
2807 !Settings::values.yuzu_username.GetValue().empty()) { 2839 !Settings::values.yuzu_username.GetValue().empty()) {
2808 CompatDB compatdb{system->TelemetrySession(), this}; 2840 CompatDB compatdb{system->TelemetrySession(), this};
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 150ada84c..b73f550dd 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -324,9 +324,10 @@ private slots:
324 void OnMouseActivity(); 324 void OnMouseActivity();
325 325
326private: 326private:
327 void RemoveBaseContent(u64 program_id, const QString& entry_type); 327 QString GetGameListErrorRemoving(InstalledEntryType type) const;
328 void RemoveUpdateContent(u64 program_id, const QString& entry_type); 328 void RemoveBaseContent(u64 program_id, InstalledEntryType type);
329 void RemoveAddOnContent(u64 program_id, const QString& entry_type); 329 void RemoveUpdateContent(u64 program_id, InstalledEntryType type);
330 void RemoveAddOnContent(u64 program_id, InstalledEntryType type);
330 void RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target); 331 void RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target);
331 void RemoveAllTransferableShaderCaches(u64 program_id); 332 void RemoveAllTransferableShaderCaches(u64 program_id);
332 void RemoveCustomConfiguration(u64 program_id, const std::string& game_path); 333 void RemoveCustomConfiguration(u64 program_id, const std::string& game_path);