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/audio_core/renderer/performance/performance_manager.cpp1
-rw-r--r--src/common/CMakeLists.txt2
-rw-r--r--src/common/atomic_helpers.h1
-rw-r--r--src/common/cache_management.cpp60
-rw-r--r--src/common/cache_management.h27
-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/debugger/debugger.cpp161
-rw-r--r--src/core/debugger/gdbstub.cpp151
-rw-r--r--src/core/debugger/gdbstub.h1
-rw-r--r--src/core/hle/ipc_helpers.h2
-rw-r--r--src/core/hle/kernel/init/init_slab_setup.cpp4
-rw-r--r--src/core/hle/kernel/k_client_port.cpp2
-rw-r--r--src/core/hle/kernel/k_event.cpp15
-rw-r--r--src/core/hle/kernel/k_memory_block.h12
-rw-r--r--src/core/hle/kernel/k_page_table.cpp17
-rw-r--r--src/core/hle/kernel/k_page_table.h3
-rw-r--r--src/core/hle/kernel/k_process.cpp15
-rw-r--r--src/core/hle/kernel/k_resource_limit.cpp11
-rw-r--r--src/core/hle/kernel/k_resource_limit.h11
-rw-r--r--src/core/hle/kernel/k_scheduler.cpp9
-rw-r--r--src/core/hle/kernel/k_scheduler_lock.h3
-rw-r--r--src/core/hle/kernel/k_session.cpp2
-rw-r--r--src/core/hle/kernel/k_shared_memory.cpp6
-rw-r--r--src/core/hle/kernel/k_thread.cpp8
-rw-r--r--src/core/hle/kernel/k_thread.h2
-rw-r--r--src/core/hle/kernel/k_transfer_memory.cpp2
-rw-r--r--src/core/hle/kernel/kernel.cpp67
-rw-r--r--src/core/hle/kernel/kernel.h2
-rw-r--r--src/core/hle/kernel/physical_core.cpp4
-rw-r--r--src/core/hle/kernel/service_thread.cpp33
-rw-r--r--src/core/hle/kernel/svc.cpp143
-rw-r--r--src/core/hle/kernel/svc_types.h470
-rw-r--r--src/core/hle/kernel/svc_wrap.h8
-rw-r--r--src/core/hle/result.h76
-rw-r--r--src/core/hle/service/am/applets/applet_error.cpp1
-rw-r--r--src/core/hle/service/am/applets/applet_general_backend.cpp2
-rw-r--r--src/core/hle/service/kernel_helpers.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp5
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_core.cpp6
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_core.h5
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_producer.cpp14
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_producer.h3
-rw-r--r--src/core/hle/service/service.cpp1
-rw-r--r--src/core/hle/service/sm/sm_controller.cpp4
-rw-r--r--src/core/hle/service/time/time_zone_manager.cpp1
-rw-r--r--src/core/memory.cpp65
-rw-r--r--src/core/memory.h34
-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.cpp5
-rw-r--r--src/video_core/engines/maxwell_3d.h20
-rw-r--r--src/video_core/engines/maxwell_dma.cpp38
-rw-r--r--src/video_core/engines/puller.cpp4
-rw-r--r--src/video_core/macro/macro.cpp3
-rw-r--r--src/video_core/macro/macro_interpreter.cpp2
-rw-r--r--src/video_core/macro/macro_jit_x64.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp4
-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_opengl/gl_texture_cache.cpp2
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp1
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.cpp1
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.h1
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp12
-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_scheduler.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp1
-rw-r--r--src/video_core/shader_environment.cpp2
-rw-r--r--src/video_core/textures/decoders.cpp3
-rw-r--r--src/yuzu/CMakeLists.txt2
-rw-r--r--src/yuzu/bootmanager.cpp337
-rw-r--r--src/yuzu/compatdb.cpp162
-rw-r--r--src/yuzu/compatdb.h11
-rw-r--r--src/yuzu/compatdb.ui277
-rw-r--r--src/yuzu/configuration/configure_profile_manager.cpp67
-rw-r--r--src/yuzu/configuration/configure_profile_manager.h27
-rw-r--r--src/yuzu/configuration/configure_profile_manager.ui6
-rw-r--r--src/yuzu/game_list_p.h12
-rw-r--r--src/yuzu/main.cpp68
-rw-r--r--src/yuzu/main.h7
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp1
-rw-r--r--src/yuzu_cmd/yuzu.cpp1
113 files changed, 2127 insertions, 667 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/audio_core/renderer/performance/performance_manager.cpp b/src/audio_core/renderer/performance/performance_manager.cpp
index fd5873e1e..8aa0f5ed0 100644
--- a/src/audio_core/renderer/performance/performance_manager.cpp
+++ b/src/audio_core/renderer/performance/performance_manager.cpp
@@ -26,6 +26,7 @@ void PerformanceManager::CreateImpl(const size_t version) {
26 impl = std::make_unique< 26 impl = std::make_unique<
27 PerformanceManagerImpl<PerformanceVersion::Version1, PerformanceFrameHeaderVersion1, 27 PerformanceManagerImpl<PerformanceVersion::Version1, PerformanceFrameHeaderVersion1,
28 PerformanceEntryVersion1, PerformanceDetailVersion1>>(); 28 PerformanceEntryVersion1, PerformanceDetailVersion1>>();
29 break;
29 } 30 }
30} 31}
31 32
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index c0555f840..b7c15c191 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -34,6 +34,8 @@ add_library(common STATIC
34 bit_util.h 34 bit_util.h
35 cityhash.cpp 35 cityhash.cpp
36 cityhash.h 36 cityhash.h
37 cache_management.cpp
38 cache_management.h
37 common_funcs.h 39 common_funcs.h
38 common_types.h 40 common_types.h
39 concepts.h 41 concepts.h
diff --git a/src/common/atomic_helpers.h b/src/common/atomic_helpers.h
index bef5015c1..aef3b66a4 100644
--- a/src/common/atomic_helpers.h
+++ b/src/common/atomic_helpers.h
@@ -156,6 +156,7 @@ AE_FORCEINLINE void compiler_fence(memory_order order) AE_NO_TSAN {
156 break; 156 break;
157 default: 157 default:
158 assert(false); 158 assert(false);
159 break;
159 } 160 }
160} 161}
161 162
diff --git a/src/common/cache_management.cpp b/src/common/cache_management.cpp
new file mode 100644
index 000000000..57810b76a
--- /dev/null
+++ b/src/common/cache_management.cpp
@@ -0,0 +1,60 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <cstring>
5
6#include "alignment.h"
7#include "cache_management.h"
8#include "common_types.h"
9
10namespace Common {
11
12#if defined(ARCHITECTURE_x86_64)
13
14// Most cache operations are no-ops on x86
15
16void DataCacheLineCleanByVAToPoU(void* start, size_t size) {}
17void DataCacheLineCleanAndInvalidateByVAToPoC(void* start, size_t size) {}
18void DataCacheLineCleanByVAToPoC(void* start, size_t size) {}
19void DataCacheZeroByVA(void* start, size_t size) {
20 std::memset(start, 0, size);
21}
22
23#elif defined(ARCHITECTURE_arm64)
24
25// BS/DminLine is log2(cache size in words), we want size in bytes
26#define EXTRACT_DMINLINE(ctr_el0) (1 << ((((ctr_el0) >> 16) & 0xf) + 2))
27#define EXTRACT_BS(dczid_el0) (1 << (((dczid_el0)&0xf) + 2))
28
29#define DEFINE_DC_OP(op_name, function_name) \
30 void function_name(void* start, size_t size) { \
31 size_t ctr_el0; \
32 asm volatile("mrs %[ctr_el0], ctr_el0\n\t" : [ctr_el0] "=r"(ctr_el0)); \
33 size_t cacheline_size = EXTRACT_DMINLINE(ctr_el0); \
34 uintptr_t va_start = reinterpret_cast<uintptr_t>(start); \
35 uintptr_t va_end = va_start + size; \
36 for (uintptr_t va = va_start; va < va_end; va += cacheline_size) { \
37 asm volatile("dc " #op_name ", %[va]\n\t" : : [va] "r"(va) : "memory"); \
38 } \
39 }
40
41#define DEFINE_DC_OP_DCZID(op_name, function_name) \
42 void function_name(void* start, size_t size) { \
43 size_t dczid_el0; \
44 asm volatile("mrs %[dczid_el0], dczid_el0\n\t" : [dczid_el0] "=r"(dczid_el0)); \
45 size_t cacheline_size = EXTRACT_BS(dczid_el0); \
46 uintptr_t va_start = reinterpret_cast<uintptr_t>(start); \
47 uintptr_t va_end = va_start + size; \
48 for (uintptr_t va = va_start; va < va_end; va += cacheline_size) { \
49 asm volatile("dc " #op_name ", %[va]\n\t" : : [va] "r"(va) : "memory"); \
50 } \
51 }
52
53DEFINE_DC_OP(cvau, DataCacheLineCleanByVAToPoU);
54DEFINE_DC_OP(civac, DataCacheLineCleanAndInvalidateByVAToPoC);
55DEFINE_DC_OP(cvac, DataCacheLineCleanByVAToPoC);
56DEFINE_DC_OP_DCZID(zva, DataCacheZeroByVA);
57
58#endif
59
60} // namespace Common
diff --git a/src/common/cache_management.h b/src/common/cache_management.h
new file mode 100644
index 000000000..e467b87e4
--- /dev/null
+++ b/src/common/cache_management.h
@@ -0,0 +1,27 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "stdlib.h"
7
8namespace Common {
9
10// Data cache instructions enabled at EL0 by SCTLR_EL1.UCI.
11// VA = virtual address
12// PoC = point of coherency
13// PoU = point of unification
14
15// dc cvau
16void DataCacheLineCleanByVAToPoU(void* start, size_t size);
17
18// dc civac
19void DataCacheLineCleanAndInvalidateByVAToPoC(void* start, size_t size);
20
21// dc cvac
22void DataCacheLineCleanByVAToPoC(void* start, size_t size);
23
24// dc zva
25void DataCacheZeroByVA(void* start, size_t size);
26
27} // namespace Common
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/debugger/debugger.cpp b/src/core/debugger/debugger.cpp
index 339f971e6..1a8e02e6a 100644
--- a/src/core/debugger/debugger.cpp
+++ b/src/core/debugger/debugger.cpp
@@ -27,12 +27,21 @@ static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) {
27 const u8* buffer_start = reinterpret_cast<const u8*>(&buffer); 27 const u8* buffer_start = reinterpret_cast<const u8*>(&buffer);
28 std::span<const u8> received_data{buffer_start, buffer_start + bytes_read}; 28 std::span<const u8> received_data{buffer_start, buffer_start + bytes_read};
29 c(received_data); 29 c(received_data);
30 AsyncReceiveInto(r, buffer, c);
30 } 31 }
31
32 AsyncReceiveInto(r, buffer, c);
33 }); 32 });
34} 33}
35 34
35template <typename Callback>
36static void AsyncAccept(boost::asio::ip::tcp::acceptor& acceptor, Callback&& c) {
37 acceptor.async_accept([&, c](const boost::system::error_code& error, auto&& peer_socket) {
38 if (!error.failed()) {
39 c(peer_socket);
40 AsyncAccept(acceptor, c);
41 }
42 });
43}
44
36template <typename Readable, typename Buffer> 45template <typename Readable, typename Buffer>
37static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) { 46static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) {
38 static_assert(std::is_trivial_v<Buffer>); 47 static_assert(std::is_trivial_v<Buffer>);
@@ -59,9 +68,7 @@ namespace Core {
59 68
60class DebuggerImpl : public DebuggerBackend { 69class DebuggerImpl : public DebuggerBackend {
61public: 70public:
62 explicit DebuggerImpl(Core::System& system_, u16 port) 71 explicit DebuggerImpl(Core::System& system_, u16 port) : system{system_} {
63 : system{system_}, signal_pipe{io_context}, client_socket{io_context} {
64 frontend = std::make_unique<GDBStub>(*this, system);
65 InitializeServer(port); 72 InitializeServer(port);
66 } 73 }
67 74
@@ -70,39 +77,42 @@ public:
70 } 77 }
71 78
72 bool SignalDebugger(SignalInfo signal_info) { 79 bool SignalDebugger(SignalInfo signal_info) {
73 { 80 std::scoped_lock lk{connection_lock};
74 std::scoped_lock lk{connection_lock};
75 81
76 if (stopped) { 82 if (stopped || !state) {
77 // Do not notify the debugger about another event. 83 // Do not notify the debugger about another event.
78 // It should be ignored. 84 // It should be ignored.
79 return false; 85 return false;
80 }
81
82 // Set up the state.
83 stopped = true;
84 info = signal_info;
85 } 86 }
86 87
88 // Set up the state.
89 stopped = true;
90 state->info = signal_info;
91
87 // Write a single byte into the pipe to wake up the debug interface. 92 // Write a single byte into the pipe to wake up the debug interface.
88 boost::asio::write(signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped))); 93 boost::asio::write(state->signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped)));
94
89 return true; 95 return true;
90 } 96 }
91 97
98 // These functions are callbacks from the frontend, and the lock will be held.
99 // There is no need to relock it.
100
92 std::span<const u8> ReadFromClient() override { 101 std::span<const u8> ReadFromClient() override {
93 return ReceiveInto(client_socket, client_data); 102 return ReceiveInto(state->client_socket, state->client_data);
94 } 103 }
95 104
96 void WriteToClient(std::span<const u8> data) override { 105 void WriteToClient(std::span<const u8> data) override {
97 boost::asio::write(client_socket, boost::asio::buffer(data.data(), data.size_bytes())); 106 boost::asio::write(state->client_socket,
107 boost::asio::buffer(data.data(), data.size_bytes()));
98 } 108 }
99 109
100 void SetActiveThread(Kernel::KThread* thread) override { 110 void SetActiveThread(Kernel::KThread* thread) override {
101 active_thread = thread; 111 state->active_thread = thread;
102 } 112 }
103 113
104 Kernel::KThread* GetActiveThread() override { 114 Kernel::KThread* GetActiveThread() override {
105 return active_thread; 115 return state->active_thread;
106 } 116 }
107 117
108private: 118private:
@@ -113,65 +123,78 @@ private:
113 123
114 // Run the connection thread. 124 // Run the connection thread.
115 connection_thread = std::jthread([&, port](std::stop_token stop_token) { 125 connection_thread = std::jthread([&, port](std::stop_token stop_token) {
126 Common::SetCurrentThreadName("Debugger");
127
116 try { 128 try {
117 // Initialize the listening socket and accept a new client. 129 // Initialize the listening socket and accept a new client.
118 tcp::endpoint endpoint{boost::asio::ip::address_v4::any(), port}; 130 tcp::endpoint endpoint{boost::asio::ip::address_v4::any(), port};
119 tcp::acceptor acceptor{io_context, endpoint}; 131 tcp::acceptor acceptor{io_context, endpoint};
120 132
121 acceptor.async_accept(client_socket, [](const auto&) {}); 133 AsyncAccept(acceptor, [&](auto&& peer) { AcceptConnection(std::move(peer)); });
122 io_context.run_one();
123 io_context.restart();
124 134
125 if (stop_token.stop_requested()) { 135 while (!stop_token.stop_requested() && io_context.run()) {
126 return;
127 } 136 }
128
129 ThreadLoop(stop_token);
130 } catch (const std::exception& ex) { 137 } catch (const std::exception& ex) {
131 LOG_CRITICAL(Debug_GDBStub, "Stopping server: {}", ex.what()); 138 LOG_CRITICAL(Debug_GDBStub, "Stopping server: {}", ex.what());
132 } 139 }
133 }); 140 });
134 } 141 }
135 142
136 void ShutdownServer() { 143 void AcceptConnection(boost::asio::ip::tcp::socket&& peer) {
137 connection_thread.request_stop(); 144 LOG_INFO(Debug_GDBStub, "Accepting new peer connection");
138 io_context.stop();
139 connection_thread.join();
140 }
141 145
142 void ThreadLoop(std::stop_token stop_token) { 146 std::scoped_lock lk{connection_lock};
143 Common::SetCurrentThreadName("Debugger"); 147
148 // Ensure everything is stopped.
149 PauseEmulation();
150
151 // Set up the new frontend.
152 frontend = std::make_unique<GDBStub>(*this, system);
153
154 // Set the new state. This will tear down any existing state.
155 state = ConnectionState{
156 .client_socket{std::move(peer)},
157 .signal_pipe{io_context},
158 .info{},
159 .active_thread{},
160 .client_data{},
161 .pipe_data{},
162 };
144 163
145 // Set up the client signals for new data. 164 // Set up the client signals for new data.
146 AsyncReceiveInto(signal_pipe, pipe_data, [&](auto d) { PipeData(d); }); 165 AsyncReceiveInto(state->signal_pipe, state->pipe_data, [&](auto d) { PipeData(d); });
147 AsyncReceiveInto(client_socket, client_data, [&](auto d) { ClientData(d); }); 166 AsyncReceiveInto(state->client_socket, state->client_data, [&](auto d) { ClientData(d); });
148 167
149 // Set the active thread. 168 // Set the active thread.
150 UpdateActiveThread(); 169 UpdateActiveThread();
151 170
152 // Set up the frontend. 171 // Set up the frontend.
153 frontend->Connected(); 172 frontend->Connected();
173 }
154 174
155 // Main event loop. 175 void ShutdownServer() {
156 while (!stop_token.stop_requested() && io_context.run()) { 176 connection_thread.request_stop();
157 } 177 io_context.stop();
178 connection_thread.join();
158 } 179 }
159 180
160 void PipeData(std::span<const u8> data) { 181 void PipeData(std::span<const u8> data) {
161 switch (info.type) { 182 std::scoped_lock lk{connection_lock};
183
184 switch (state->info.type) {
162 case SignalType::Stopped: 185 case SignalType::Stopped:
163 case SignalType::Watchpoint: 186 case SignalType::Watchpoint:
164 // Stop emulation. 187 // Stop emulation.
165 PauseEmulation(); 188 PauseEmulation();
166 189
167 // Notify the client. 190 // Notify the client.
168 active_thread = info.thread; 191 state->active_thread = state->info.thread;
169 UpdateActiveThread(); 192 UpdateActiveThread();
170 193
171 if (info.type == SignalType::Watchpoint) { 194 if (state->info.type == SignalType::Watchpoint) {
172 frontend->Watchpoint(active_thread, *info.watchpoint); 195 frontend->Watchpoint(state->active_thread, *state->info.watchpoint);
173 } else { 196 } else {
174 frontend->Stopped(active_thread); 197 frontend->Stopped(state->active_thread);
175 } 198 }
176 199
177 break; 200 break;
@@ -179,8 +202,8 @@ private:
179 frontend->ShuttingDown(); 202 frontend->ShuttingDown();
180 203
181 // Wait for emulation to shut down gracefully now. 204 // Wait for emulation to shut down gracefully now.
182 signal_pipe.close(); 205 state->signal_pipe.close();
183 client_socket.shutdown(boost::asio::socket_base::shutdown_both); 206 state->client_socket.shutdown(boost::asio::socket_base::shutdown_both);
184 LOG_INFO(Debug_GDBStub, "Shut down server"); 207 LOG_INFO(Debug_GDBStub, "Shut down server");
185 208
186 break; 209 break;
@@ -188,17 +211,16 @@ private:
188 } 211 }
189 212
190 void ClientData(std::span<const u8> data) { 213 void ClientData(std::span<const u8> data) {
214 std::scoped_lock lk{connection_lock};
215
191 const auto actions{frontend->ClientData(data)}; 216 const auto actions{frontend->ClientData(data)};
192 for (const auto action : actions) { 217 for (const auto action : actions) {
193 switch (action) { 218 switch (action) {
194 case DebuggerAction::Interrupt: { 219 case DebuggerAction::Interrupt: {
195 { 220 stopped = true;
196 std::scoped_lock lk{connection_lock};
197 stopped = true;
198 }
199 PauseEmulation(); 221 PauseEmulation();
200 UpdateActiveThread(); 222 UpdateActiveThread();
201 frontend->Stopped(active_thread); 223 frontend->Stopped(state->active_thread);
202 break; 224 break;
203 } 225 }
204 case DebuggerAction::Continue: 226 case DebuggerAction::Continue:
@@ -206,15 +228,15 @@ private:
206 break; 228 break;
207 case DebuggerAction::StepThreadUnlocked: 229 case DebuggerAction::StepThreadUnlocked:
208 MarkResumed([&] { 230 MarkResumed([&] {
209 active_thread->SetStepState(Kernel::StepState::StepPending); 231 state->active_thread->SetStepState(Kernel::StepState::StepPending);
210 active_thread->Resume(Kernel::SuspendType::Debug); 232 state->active_thread->Resume(Kernel::SuspendType::Debug);
211 ResumeEmulation(active_thread); 233 ResumeEmulation(state->active_thread);
212 }); 234 });
213 break; 235 break;
214 case DebuggerAction::StepThreadLocked: { 236 case DebuggerAction::StepThreadLocked: {
215 MarkResumed([&] { 237 MarkResumed([&] {
216 active_thread->SetStepState(Kernel::StepState::StepPending); 238 state->active_thread->SetStepState(Kernel::StepState::StepPending);
217 active_thread->Resume(Kernel::SuspendType::Debug); 239 state->active_thread->Resume(Kernel::SuspendType::Debug);
218 }); 240 });
219 break; 241 break;
220 } 242 }
@@ -254,15 +276,14 @@ private:
254 template <typename Callback> 276 template <typename Callback>
255 void MarkResumed(Callback&& cb) { 277 void MarkResumed(Callback&& cb) {
256 Kernel::KScopedSchedulerLock sl{system.Kernel()}; 278 Kernel::KScopedSchedulerLock sl{system.Kernel()};
257 std::scoped_lock cl{connection_lock};
258 stopped = false; 279 stopped = false;
259 cb(); 280 cb();
260 } 281 }
261 282
262 void UpdateActiveThread() { 283 void UpdateActiveThread() {
263 const auto& threads{ThreadList()}; 284 const auto& threads{ThreadList()};
264 if (std::find(threads.begin(), threads.end(), active_thread) == threads.end()) { 285 if (std::find(threads.begin(), threads.end(), state->active_thread) == threads.end()) {
265 active_thread = threads[0]; 286 state->active_thread = threads[0];
266 } 287 }
267 } 288 }
268 289
@@ -274,18 +295,22 @@ private:
274 System& system; 295 System& system;
275 std::unique_ptr<DebuggerFrontend> frontend; 296 std::unique_ptr<DebuggerFrontend> frontend;
276 297
298 boost::asio::io_context io_context;
277 std::jthread connection_thread; 299 std::jthread connection_thread;
278 std::mutex connection_lock; 300 std::mutex connection_lock;
279 boost::asio::io_context io_context;
280 boost::process::async_pipe signal_pipe;
281 boost::asio::ip::tcp::socket client_socket;
282 301
283 SignalInfo info; 302 struct ConnectionState {
284 Kernel::KThread* active_thread; 303 boost::asio::ip::tcp::socket client_socket;
285 bool pipe_data; 304 boost::process::async_pipe signal_pipe;
286 bool stopped; 305
306 SignalInfo info;
307 Kernel::KThread* active_thread;
308 std::array<u8, 4096> client_data;
309 bool pipe_data;
310 };
287 311
288 std::array<u8, 4096> client_data; 312 std::optional<ConnectionState> state{};
313 bool stopped{};
289}; 314};
290 315
291Debugger::Debugger(Core::System& system, u16 port) { 316Debugger::Debugger(Core::System& system, u16 port) {
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp
index 884229c77..a64a9ac64 100644
--- a/src/core/debugger/gdbstub.cpp
+++ b/src/core/debugger/gdbstub.cpp
@@ -606,6 +606,8 @@ void GDBStub::HandleQuery(std::string_view command) {
606 } else if (command.starts_with("StartNoAckMode")) { 606 } else if (command.starts_with("StartNoAckMode")) {
607 no_ack = true; 607 no_ack = true;
608 SendReply(GDB_STUB_REPLY_OK); 608 SendReply(GDB_STUB_REPLY_OK);
609 } else if (command.starts_with("Rcmd,")) {
610 HandleRcmd(Common::HexStringToVector(command.substr(5), false));
609 } else { 611 } else {
610 SendReply(GDB_STUB_REPLY_EMPTY); 612 SendReply(GDB_STUB_REPLY_EMPTY);
611 } 613 }
@@ -645,6 +647,155 @@ void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>&
645 } 647 }
646} 648}
647 649
650constexpr std::array<std::pair<const char*, Kernel::Svc::MemoryState>, 22> MemoryStateNames{{
651 {"----- Free -----", Kernel::Svc::MemoryState::Free},
652 {"Io ", Kernel::Svc::MemoryState::Io},
653 {"Static ", Kernel::Svc::MemoryState::Static},
654 {"Code ", Kernel::Svc::MemoryState::Code},
655 {"CodeData ", Kernel::Svc::MemoryState::CodeData},
656 {"Normal ", Kernel::Svc::MemoryState::Normal},
657 {"Shared ", Kernel::Svc::MemoryState::Shared},
658 {"AliasCode ", Kernel::Svc::MemoryState::AliasCode},
659 {"AliasCodeData ", Kernel::Svc::MemoryState::AliasCodeData},
660 {"Ipc ", Kernel::Svc::MemoryState::Ipc},
661 {"Stack ", Kernel::Svc::MemoryState::Stack},
662 {"ThreadLocal ", Kernel::Svc::MemoryState::ThreadLocal},
663 {"Transfered ", Kernel::Svc::MemoryState::Transfered},
664 {"SharedTransfered", Kernel::Svc::MemoryState::SharedTransfered},
665 {"SharedCode ", Kernel::Svc::MemoryState::SharedCode},
666 {"Inaccessible ", Kernel::Svc::MemoryState::Inaccessible},
667 {"NonSecureIpc ", Kernel::Svc::MemoryState::NonSecureIpc},
668 {"NonDeviceIpc ", Kernel::Svc::MemoryState::NonDeviceIpc},
669 {"Kernel ", Kernel::Svc::MemoryState::Kernel},
670 {"GeneratedCode ", Kernel::Svc::MemoryState::GeneratedCode},
671 {"CodeOut ", Kernel::Svc::MemoryState::CodeOut},
672 {"Coverage ", Kernel::Svc::MemoryState::Coverage},
673}};
674
675static constexpr const char* GetMemoryStateName(Kernel::Svc::MemoryState state) {
676 for (size_t i = 0; i < MemoryStateNames.size(); i++) {
677 if (std::get<1>(MemoryStateNames[i]) == state) {
678 return std::get<0>(MemoryStateNames[i]);
679 }
680 }
681 return "Unknown ";
682}
683
684static constexpr const char* GetMemoryPermissionString(const Kernel::Svc::MemoryInfo& info) {
685 if (info.state == Kernel::Svc::MemoryState::Free) {
686 return " ";
687 }
688
689 switch (info.permission) {
690 case Kernel::Svc::MemoryPermission::ReadExecute:
691 return "r-x";
692 case Kernel::Svc::MemoryPermission::Read:
693 return "r--";
694 case Kernel::Svc::MemoryPermission::ReadWrite:
695 return "rw-";
696 default:
697 return "---";
698 }
699}
700
701static VAddr GetModuleEnd(Kernel::KPageTable& page_table, VAddr base) {
702 Kernel::Svc::MemoryInfo mem_info;
703 VAddr cur_addr{base};
704
705 // Expect: r-x Code (.text)
706 mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
707 cur_addr = mem_info.base_address + mem_info.size;
708 if (mem_info.state != Kernel::Svc::MemoryState::Code ||
709 mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) {
710 return cur_addr - 1;
711 }
712
713 // Expect: r-- Code (.rodata)
714 mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
715 cur_addr = mem_info.base_address + mem_info.size;
716 if (mem_info.state != Kernel::Svc::MemoryState::Code ||
717 mem_info.permission != Kernel::Svc::MemoryPermission::Read) {
718 return cur_addr - 1;
719 }
720
721 // Expect: rw- CodeData (.data)
722 mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
723 cur_addr = mem_info.base_address + mem_info.size;
724 return cur_addr - 1;
725}
726
727void GDBStub::HandleRcmd(const std::vector<u8>& command) {
728 std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()};
729 std::string reply;
730
731 auto* process = system.CurrentProcess();
732 auto& page_table = process->PageTable();
733
734 if (command_str == "get info") {
735 Loader::AppLoader::Modules modules;
736 system.GetAppLoader().ReadNSOModules(modules);
737
738 reply = fmt::format("Process: {:#x} ({})\n"
739 "Program Id: {:#018x}\n",
740 process->GetProcessID(), process->GetName(), process->GetProgramID());
741 reply +=
742 fmt::format("Layout:\n"
743 " Alias: {:#012x} - {:#012x}\n"
744 " Heap: {:#012x} - {:#012x}\n"
745 " Aslr: {:#012x} - {:#012x}\n"
746 " Stack: {:#012x} - {:#012x}\n"
747 "Modules:\n",
748 page_table.GetAliasRegionStart(), page_table.GetAliasRegionEnd(),
749 page_table.GetHeapRegionStart(), page_table.GetHeapRegionEnd(),
750 page_table.GetAliasCodeRegionStart(), page_table.GetAliasCodeRegionEnd(),
751 page_table.GetStackRegionStart(), page_table.GetStackRegionEnd());
752
753 for (const auto& [vaddr, name] : modules) {
754 reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr,
755 GetModuleEnd(page_table, vaddr), name);
756 }
757 } else if (command_str == "get mappings") {
758 reply = "Mappings:\n";
759 VAddr cur_addr = 0;
760
761 while (true) {
762 using MemoryAttribute = Kernel::Svc::MemoryAttribute;
763
764 auto mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
765
766 if (mem_info.state != Kernel::Svc::MemoryState::Inaccessible ||
767 mem_info.base_address + mem_info.size - 1 != std::numeric_limits<u64>::max()) {
768 const char* state = GetMemoryStateName(mem_info.state);
769 const char* perm = GetMemoryPermissionString(mem_info);
770
771 const char l = True(mem_info.attribute & MemoryAttribute::Locked) ? 'L' : '-';
772 const char i = True(mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-';
773 const char d = True(mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-';
774 const char u = True(mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-';
775
776 reply +=
777 fmt::format(" {:#012x} - {:#012x} {} {} {}{}{}{} [{}, {}]\n",
778 mem_info.base_address, mem_info.base_address + mem_info.size - 1,
779 perm, state, l, i, d, u, mem_info.ipc_count, mem_info.device_count);
780 }
781
782 const uintptr_t next_address = mem_info.base_address + mem_info.size;
783 if (next_address <= cur_addr) {
784 break;
785 }
786
787 cur_addr = next_address;
788 }
789 } else if (command_str == "help") {
790 reply = "Commands:\n get info\n get mappings\n";
791 } else {
792 reply = "Unknown command.\nCommands:\n get info\n get mappings\n";
793 }
794
795 std::span<const u8> reply_span{reinterpret_cast<u8*>(&reply.front()), reply.size()};
796 SendReply(Common::HexToString(reply_span, false));
797}
798
648Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) { 799Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
649 const auto& threads{system.GlobalSchedulerContext().GetThreadList()}; 800 const auto& threads{system.GlobalSchedulerContext().GetThreadList()};
650 for (auto* thread : threads) { 801 for (auto* thread : threads) {
diff --git a/src/core/debugger/gdbstub.h b/src/core/debugger/gdbstub.h
index 0b0f56e4b..368197920 100644
--- a/src/core/debugger/gdbstub.h
+++ b/src/core/debugger/gdbstub.h
@@ -32,6 +32,7 @@ private:
32 void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions); 32 void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions);
33 void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions); 33 void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions);
34 void HandleQuery(std::string_view command); 34 void HandleQuery(std::string_view command);
35 void HandleRcmd(const std::vector<u8>& command);
35 void HandleBreakpointInsert(std::string_view command); 36 void HandleBreakpointInsert(std::string_view command);
36 void HandleBreakpointRemove(std::string_view command); 37 void HandleBreakpointRemove(std::string_view command);
37 std::vector<char>::const_iterator CommandEnd() const; 38 std::vector<char>::const_iterator CommandEnd() const;
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index 3bb111748..a86bec252 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -149,7 +149,7 @@ public:
149 context->AddDomainObject(std::move(iface)); 149 context->AddDomainObject(std::move(iface));
150 } else { 150 } else {
151 kernel.CurrentProcess()->GetResourceLimit()->Reserve( 151 kernel.CurrentProcess()->GetResourceLimit()->Reserve(
152 Kernel::LimitableResource::Sessions, 1); 152 Kernel::LimitableResource::SessionCountMax, 1);
153 153
154 auto* session = Kernel::KSession::Create(kernel); 154 auto* session = Kernel::KSession::Create(kernel);
155 session->Initialize(nullptr, iface->GetServiceName()); 155 session->Initialize(nullptr, iface->GetServiceName());
diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp
index aa2dddcc6..7b363eb1e 100644
--- a/src/core/hle/kernel/init/init_slab_setup.cpp
+++ b/src/core/hle/kernel/init/init_slab_setup.cpp
@@ -243,6 +243,7 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
243 // If we somehow get an invalid type, abort. 243 // If we somehow get an invalid type, abort.
244 default: 244 default:
245 ASSERT_MSG(false, "Unknown slab type: {}", slab_types[i]); 245 ASSERT_MSG(false, "Unknown slab type: {}", slab_types[i]);
246 break;
246 } 247 }
247 248
248 // If we've hit the end of a gap, free it. 249 // If we've hit the end of a gap, free it.
@@ -265,7 +266,8 @@ void KPageBufferSlabHeap::Initialize(Core::System& system) {
265 const size_t slab_size = num_pages * PageSize; 266 const size_t slab_size = num_pages * PageSize;
266 267
267 // Reserve memory from the system resource limit. 268 // Reserve memory from the system resource limit.
268 ASSERT(kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemory, slab_size)); 269 ASSERT(
270 kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemoryMax, slab_size));
269 271
270 // Allocate memory for the slab. 272 // Allocate memory for the slab.
271 constexpr auto AllocateOption = KMemoryManager::EncodeOption( 273 constexpr auto AllocateOption = KMemoryManager::EncodeOption(
diff --git a/src/core/hle/kernel/k_client_port.cpp b/src/core/hle/kernel/k_client_port.cpp
index eaa2e094c..2ec623a58 100644
--- a/src/core/hle/kernel/k_client_port.cpp
+++ b/src/core/hle/kernel/k_client_port.cpp
@@ -61,7 +61,7 @@ bool KClientPort::IsSignaled() const {
61Result KClientPort::CreateSession(KClientSession** out) { 61Result KClientPort::CreateSession(KClientSession** out) {
62 // Reserve a new session from the resource limit. 62 // Reserve a new session from the resource limit.
63 KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(), 63 KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(),
64 LimitableResource::Sessions); 64 LimitableResource::SessionCountMax);
65 R_UNLESS(session_reservation.Succeeded(), ResultLimitReached); 65 R_UNLESS(session_reservation.Succeeded(), ResultLimitReached);
66 66
67 // Update the session counts. 67 // Update the session counts.
diff --git a/src/core/hle/kernel/k_event.cpp b/src/core/hle/kernel/k_event.cpp
index 78ca59463..d973853ab 100644
--- a/src/core/hle/kernel/k_event.cpp
+++ b/src/core/hle/kernel/k_event.cpp
@@ -20,8 +20,12 @@ void KEvent::Initialize(KProcess* owner) {
20 m_readable_event.Initialize(this); 20 m_readable_event.Initialize(this);
21 21
22 // Set our owner process. 22 // Set our owner process.
23 m_owner = owner; 23 // HACK: this should never be nullptr, but service threads don't have a
24 m_owner->Open(); 24 // proper parent process yet.
25 if (owner != nullptr) {
26 m_owner = owner;
27 m_owner->Open();
28 }
25 29
26 // Mark initialized. 30 // Mark initialized.
27 m_initialized = true; 31 m_initialized = true;
@@ -50,8 +54,11 @@ Result KEvent::Clear() {
50void KEvent::PostDestroy(uintptr_t arg) { 54void KEvent::PostDestroy(uintptr_t arg) {
51 // Release the event count resource the owner process holds. 55 // Release the event count resource the owner process holds.
52 KProcess* owner = reinterpret_cast<KProcess*>(arg); 56 KProcess* owner = reinterpret_cast<KProcess*>(arg);
53 owner->GetResourceLimit()->Release(LimitableResource::Events, 1); 57
54 owner->Close(); 58 if (owner != nullptr) {
59 owner->GetResourceLimit()->Release(LimitableResource::EventCountMax, 1);
60 owner->Close();
61 }
55} 62}
56 63
57} // namespace Kernel 64} // namespace Kernel
diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h
index 6f845d675..3b6e7baff 100644
--- a/src/core/hle/kernel/k_memory_block.h
+++ b/src/core/hle/kernel/k_memory_block.h
@@ -216,13 +216,15 @@ struct KMemoryInfo {
216 216
217 constexpr Svc::MemoryInfo GetSvcMemoryInfo() const { 217 constexpr Svc::MemoryInfo GetSvcMemoryInfo() const {
218 return { 218 return {
219 .addr = m_address, 219 .base_address = m_address,
220 .size = m_size, 220 .size = m_size,
221 .state = static_cast<Svc::MemoryState>(m_state & KMemoryState::Mask), 221 .state = static_cast<Svc::MemoryState>(m_state & KMemoryState::Mask),
222 .attr = static_cast<Svc::MemoryAttribute>(m_attribute & KMemoryAttribute::UserMask), 222 .attribute =
223 .perm = static_cast<Svc::MemoryPermission>(m_permission & KMemoryPermission::UserMask), 223 static_cast<Svc::MemoryAttribute>(m_attribute & KMemoryAttribute::UserMask),
224 .ipc_refcount = m_ipc_lock_count, 224 .permission =
225 .device_refcount = m_device_use_count, 225 static_cast<Svc::MemoryPermission>(m_permission & KMemoryPermission::UserMask),
226 .ipc_count = m_ipc_lock_count,
227 .device_count = m_device_use_count,
226 .padding = {}, 228 .padding = {},
227 }; 229 };
228 } 230 }
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index fab55a057..612fc76fa 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -920,8 +920,8 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add
920 920
921 // Reserve space for any partial pages we allocate. 921 // Reserve space for any partial pages we allocate.
922 const size_t unmapped_size = aligned_src_size - mapping_src_size; 922 const size_t unmapped_size = aligned_src_size - mapping_src_size;
923 KScopedResourceReservation memory_reservation(m_resource_limit, 923 KScopedResourceReservation memory_reservation(
924 LimitableResource::PhysicalMemory, unmapped_size); 924 m_resource_limit, LimitableResource::PhysicalMemoryMax, unmapped_size);
925 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); 925 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
926 926
927 // Ensure that we manage page references correctly. 927 // Ensure that we manage page references correctly.
@@ -1227,7 +1227,7 @@ Result KPageTable::CleanupForIpcServer(VAddr address, size_t size, KMemoryState
1227 const VAddr mapping_start = Common::AlignUp((address), PageSize); 1227 const VAddr mapping_start = Common::AlignUp((address), PageSize);
1228 const VAddr mapping_end = Common::AlignDown((address) + size, PageSize); 1228 const VAddr mapping_end = Common::AlignDown((address) + size, PageSize);
1229 const size_t mapping_size = (mapping_start < mapping_end) ? mapping_end - mapping_start : 0; 1229 const size_t mapping_size = (mapping_start < mapping_end) ? mapping_end - mapping_start : 0;
1230 m_resource_limit->Release(LimitableResource::PhysicalMemory, aligned_size - mapping_size); 1230 m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, aligned_size - mapping_size);
1231 1231
1232 R_SUCCEED(); 1232 R_SUCCEED();
1233} 1233}
@@ -1568,7 +1568,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
1568 { 1568 {
1569 // Reserve the memory from the process resource limit. 1569 // Reserve the memory from the process resource limit.
1570 KScopedResourceReservation memory_reservation( 1570 KScopedResourceReservation memory_reservation(
1571 m_resource_limit, LimitableResource::PhysicalMemory, size - mapped_size); 1571 m_resource_limit, LimitableResource::PhysicalMemoryMax, size - mapped_size);
1572 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); 1572 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
1573 1573
1574 // Allocate pages for the new memory. 1574 // Allocate pages for the new memory.
@@ -1908,7 +1908,7 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) {
1908 1908
1909 // Release the memory resource. 1909 // Release the memory resource.
1910 m_mapped_physical_memory_size -= mapped_size; 1910 m_mapped_physical_memory_size -= mapped_size;
1911 m_resource_limit->Release(LimitableResource::PhysicalMemory, mapped_size); 1911 m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, mapped_size);
1912 1912
1913 // Update memory blocks. 1913 // Update memory blocks.
1914 m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize, 1914 m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize,
@@ -2301,6 +2301,7 @@ Result KPageTable::SetProcessMemoryPermission(VAddr addr, size_t size,
2301 break; 2301 break;
2302 default: 2302 default:
2303 ASSERT(false); 2303 ASSERT(false);
2304 break;
2304 } 2305 }
2305 } 2306 }
2306 2307
@@ -2492,7 +2493,7 @@ Result KPageTable::SetHeapSize(VAddr* out, size_t size) {
2492 OperationType::Unmap)); 2493 OperationType::Unmap));
2493 2494
2494 // Release the memory from the resource limit. 2495 // Release the memory from the resource limit.
2495 m_resource_limit->Release(LimitableResource::PhysicalMemory, num_pages * PageSize); 2496 m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, num_pages * PageSize);
2496 2497
2497 // Apply the memory block update. 2498 // Apply the memory block update.
2498 m_memory_block_manager.Update(std::addressof(allocator), m_heap_region_start + size, 2499 m_memory_block_manager.Update(std::addressof(allocator), m_heap_region_start + size,
@@ -2522,7 +2523,7 @@ Result KPageTable::SetHeapSize(VAddr* out, size_t size) {
2522 2523
2523 // Reserve memory for the heap extension. 2524 // Reserve memory for the heap extension.
2524 KScopedResourceReservation memory_reservation( 2525 KScopedResourceReservation memory_reservation(
2525 m_resource_limit, LimitableResource::PhysicalMemory, allocation_size); 2526 m_resource_limit, LimitableResource::PhysicalMemoryMax, allocation_size);
2526 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); 2527 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
2527 2528
2528 // Allocate pages for the heap extension. 2529 // Allocate pages for the heap extension.
@@ -2803,6 +2804,7 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, const KPageGroup& page_
2803 break; 2804 break;
2804 default: 2805 default:
2805 ASSERT(false); 2806 ASSERT(false);
2807 break;
2806 } 2808 }
2807 2809
2808 addr += size; 2810 addr += size;
@@ -2838,6 +2840,7 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, KMemoryPermission perm,
2838 break; 2840 break;
2839 default: 2841 default:
2840 ASSERT(false); 2842 ASSERT(false);
2843 break;
2841 } 2844 }
2842 R_SUCCEED(); 2845 R_SUCCEED();
2843} 2846}
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index 950850291..f1ca785d7 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -320,6 +320,9 @@ public:
320 constexpr VAddr GetAliasCodeRegionStart() const { 320 constexpr VAddr GetAliasCodeRegionStart() const {
321 return m_alias_code_region_start; 321 return m_alias_code_region_start;
322 } 322 }
323 constexpr VAddr GetAliasCodeRegionEnd() const {
324 return m_alias_code_region_end;
325 }
323 constexpr VAddr GetAliasCodeRegionSize() const { 326 constexpr VAddr GetAliasCodeRegionSize() const {
324 return m_alias_code_region_end - m_alias_code_region_start; 327 return m_alias_code_region_end - m_alias_code_region_start;
325 } 328 }
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 4ddeea73b..d1dc62401 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -38,7 +38,7 @@ namespace {
38 */ 38 */
39void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority, VAddr stack_top) { 39void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority, VAddr stack_top) {
40 const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart(); 40 const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart();
41 ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::Threads, 1)); 41 ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::ThreadCountMax, 1));
42 42
43 KThread* thread = KThread::Create(system.Kernel()); 43 KThread* thread = KThread::Create(system.Kernel());
44 SCOPE_EXIT({ thread->Close(); }); 44 SCOPE_EXIT({ thread->Close(); });
@@ -124,7 +124,7 @@ void KProcess::DecrementRunningThreadCount() {
124} 124}
125 125
126u64 KProcess::GetTotalPhysicalMemoryAvailable() { 126u64 KProcess::GetTotalPhysicalMemoryAvailable() {
127 const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) + 127 const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemoryMax) +
128 page_table.GetNormalMemorySize() + GetSystemResourceSize() + image_size + 128 page_table.GetNormalMemorySize() + GetSystemResourceSize() + image_size +
129 main_thread_stack_size}; 129 main_thread_stack_size};
130 if (const auto pool_size = kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application); 130 if (const auto pool_size = kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application);
@@ -349,8 +349,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
349 // We currently do not support process-specific system resource 349 // We currently do not support process-specific system resource
350 UNIMPLEMENTED_IF(system_resource_size != 0); 350 UNIMPLEMENTED_IF(system_resource_size != 0);
351 351
352 KScopedResourceReservation memory_reservation(resource_limit, LimitableResource::PhysicalMemory, 352 KScopedResourceReservation memory_reservation(
353 code_size + system_resource_size); 353 resource_limit, LimitableResource::PhysicalMemoryMax, code_size + system_resource_size);
354 if (!memory_reservation.Succeeded()) { 354 if (!memory_reservation.Succeeded()) {
355 LOG_ERROR(Kernel, "Could not reserve process memory requirements of size {:X} bytes", 355 LOG_ERROR(Kernel, "Could not reserve process memory requirements of size {:X} bytes",
356 code_size + system_resource_size); 356 code_size + system_resource_size);
@@ -395,6 +395,7 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
395 395
396 default: 396 default:
397 ASSERT(false); 397 ASSERT(false);
398 break;
398 } 399 }
399 400
400 // Create TLS region 401 // Create TLS region
@@ -406,8 +407,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
406 407
407void KProcess::Run(s32 main_thread_priority, u64 stack_size) { 408void KProcess::Run(s32 main_thread_priority, u64 stack_size) {
408 AllocateMainThreadStack(stack_size); 409 AllocateMainThreadStack(stack_size);
409 resource_limit->Reserve(LimitableResource::Threads, 1); 410 resource_limit->Reserve(LimitableResource::ThreadCountMax, 1);
410 resource_limit->Reserve(LimitableResource::PhysicalMemory, main_thread_stack_size); 411 resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, main_thread_stack_size);
411 412
412 const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)}; 413 const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)};
413 ASSERT(!page_table.SetMaxHeapSize(heap_capacity).IsError()); 414 ASSERT(!page_table.SetMaxHeapSize(heap_capacity).IsError());
@@ -442,7 +443,7 @@ void KProcess::PrepareForTermination() {
442 plr_address = 0; 443 plr_address = 0;
443 444
444 if (resource_limit) { 445 if (resource_limit) {
445 resource_limit->Release(LimitableResource::PhysicalMemory, 446 resource_limit->Release(LimitableResource::PhysicalMemoryMax,
446 main_thread_stack_size + image_size); 447 main_thread_stack_size + image_size);
447 } 448 }
448 449
diff --git a/src/core/hle/kernel/k_resource_limit.cpp b/src/core/hle/kernel/k_resource_limit.cpp
index 010dcf99e..b9d22b414 100644
--- a/src/core/hle/kernel/k_resource_limit.cpp
+++ b/src/core/hle/kernel/k_resource_limit.cpp
@@ -159,12 +159,13 @@ KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical
159 // TODO(bunnei): These values are the system defaults, the limits for service processes are 159 // TODO(bunnei): These values are the system defaults, the limits for service processes are
160 // lower. These should use the correct limit values. 160 // lower. These should use the correct limit values.
161 161
162 ASSERT(resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, physical_memory_size) 162 ASSERT(resource_limit->SetLimitValue(LimitableResource::PhysicalMemoryMax, physical_memory_size)
163 .IsSuccess()); 163 .IsSuccess());
164 ASSERT(resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess()); 164 ASSERT(resource_limit->SetLimitValue(LimitableResource::ThreadCountMax, 800).IsSuccess());
165 ASSERT(resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess()); 165 ASSERT(resource_limit->SetLimitValue(LimitableResource::EventCountMax, 900).IsSuccess());
166 ASSERT(resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200).IsSuccess()); 166 ASSERT(
167 ASSERT(resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess()); 167 resource_limit->SetLimitValue(LimitableResource::TransferMemoryCountMax, 200).IsSuccess());
168 ASSERT(resource_limit->SetLimitValue(LimitableResource::SessionCountMax, 1133).IsSuccess());
168 169
169 return resource_limit; 170 return resource_limit;
170} 171}
diff --git a/src/core/hle/kernel/k_resource_limit.h b/src/core/hle/kernel/k_resource_limit.h
index 65c98c979..2573d1b7c 100644
--- a/src/core/hle/kernel/k_resource_limit.h
+++ b/src/core/hle/kernel/k_resource_limit.h
@@ -16,15 +16,8 @@ class CoreTiming;
16 16
17namespace Kernel { 17namespace Kernel {
18class KernelCore; 18class KernelCore;
19enum class LimitableResource : u32 { 19
20 PhysicalMemory = 0, 20using LimitableResource = Svc::LimitableResource;
21 Threads = 1,
22 Events = 2,
23 TransferMemory = 3,
24 Sessions = 4,
25
26 Count,
27};
28 21
29constexpr bool IsValidResourceType(LimitableResource type) { 22constexpr bool IsValidResourceType(LimitableResource type) {
30 return type < LimitableResource::Count; 23 return type < LimitableResource::Count;
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index b1cabbca0..d6676904b 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -384,7 +384,8 @@ void KScheduler::SwitchThread(KThread* next_thread) {
384 384
385void KScheduler::ScheduleImpl() { 385void KScheduler::ScheduleImpl() {
386 // First, clear the needs scheduling bool. 386 // First, clear the needs scheduling bool.
387 m_state.needs_scheduling.store(false, std::memory_order_seq_cst); 387 m_state.needs_scheduling.store(false, std::memory_order_relaxed);
388 std::atomic_thread_fence(std::memory_order_seq_cst);
388 389
389 // Load the appropriate thread pointers for scheduling. 390 // Load the appropriate thread pointers for scheduling.
390 KThread* const cur_thread{GetCurrentThreadPointer(kernel)}; 391 KThread* const cur_thread{GetCurrentThreadPointer(kernel)};
@@ -400,7 +401,8 @@ void KScheduler::ScheduleImpl() {
400 // If there aren't, we want to check if the highest priority thread is the same as the current 401 // If there aren't, we want to check if the highest priority thread is the same as the current
401 // thread. 402 // thread.
402 if (highest_priority_thread == cur_thread) { 403 if (highest_priority_thread == cur_thread) {
403 // If they're the same, then we can just return. 404 // If they're the same, then we can just issue a memory barrier and return.
405 std::atomic_thread_fence(std::memory_order_seq_cst);
404 return; 406 return;
405 } 407 }
406 408
@@ -476,7 +478,8 @@ void KScheduler::ScheduleImplFiber() {
476 478
477 // We failed to successfully do the context switch, and need to retry. 479 // We failed to successfully do the context switch, and need to retry.
478 // Clear needs_scheduling. 480 // Clear needs_scheduling.
479 m_state.needs_scheduling.store(false, std::memory_order_seq_cst); 481 m_state.needs_scheduling.store(false, std::memory_order_relaxed);
482 std::atomic_thread_fence(std::memory_order_seq_cst);
480 483
481 // Refresh the highest priority thread. 484 // Refresh the highest priority thread.
482 highest_priority_thread = m_state.highest_priority_thread; 485 highest_priority_thread = m_state.highest_priority_thread;
diff --git a/src/core/hle/kernel/k_scheduler_lock.h b/src/core/hle/kernel/k_scheduler_lock.h
index 73314b45e..129d60472 100644
--- a/src/core/hle/kernel/k_scheduler_lock.h
+++ b/src/core/hle/kernel/k_scheduler_lock.h
@@ -60,6 +60,9 @@ public:
60 60
61 // Release an instance of the lock. 61 // Release an instance of the lock.
62 if ((--lock_count) == 0) { 62 if ((--lock_count) == 0) {
63 // Perform a memory barrier here.
64 std::atomic_thread_fence(std::memory_order_seq_cst);
65
63 // We're no longer going to hold the lock. Take note of what cores need scheduling. 66 // We're no longer going to hold the lock. Take note of what cores need scheduling.
64 const u64 cores_needing_scheduling = 67 const u64 cores_needing_scheduling =
65 SchedulerType::UpdateHighestPriorityThreads(kernel); 68 SchedulerType::UpdateHighestPriorityThreads(kernel);
diff --git a/src/core/hle/kernel/k_session.cpp b/src/core/hle/kernel/k_session.cpp
index 7a6534ac3..b6f6fe9d9 100644
--- a/src/core/hle/kernel/k_session.cpp
+++ b/src/core/hle/kernel/k_session.cpp
@@ -76,7 +76,7 @@ void KSession::OnClientClosed() {
76void KSession::PostDestroy(uintptr_t arg) { 76void KSession::PostDestroy(uintptr_t arg) {
77 // Release the session count resource the owner process holds. 77 // Release the session count resource the owner process holds.
78 KProcess* owner = reinterpret_cast<KProcess*>(arg); 78 KProcess* owner = reinterpret_cast<KProcess*>(arg);
79 owner->GetResourceLimit()->Release(LimitableResource::Sessions, 1); 79 owner->GetResourceLimit()->Release(LimitableResource::SessionCountMax, 1);
80 owner->Close(); 80 owner->Close();
81} 81}
82 82
diff --git a/src/core/hle/kernel/k_shared_memory.cpp b/src/core/hle/kernel/k_shared_memory.cpp
index a039cc591..10cd4c43d 100644
--- a/src/core/hle/kernel/k_shared_memory.cpp
+++ b/src/core/hle/kernel/k_shared_memory.cpp
@@ -14,7 +14,7 @@ namespace Kernel {
14KSharedMemory::KSharedMemory(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_} {} 14KSharedMemory::KSharedMemory(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_} {}
15 15
16KSharedMemory::~KSharedMemory() { 16KSharedMemory::~KSharedMemory() {
17 kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemory, size); 17 kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemoryMax, size);
18} 18}
19 19
20Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_, 20Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_,
@@ -35,7 +35,7 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o
35 KResourceLimit* reslimit = kernel.GetSystemResourceLimit(); 35 KResourceLimit* reslimit = kernel.GetSystemResourceLimit();
36 36
37 // Reserve memory for ourselves. 37 // Reserve memory for ourselves.
38 KScopedResourceReservation memory_reservation(reslimit, LimitableResource::PhysicalMemory, 38 KScopedResourceReservation memory_reservation(reslimit, LimitableResource::PhysicalMemoryMax,
39 size_); 39 size_);
40 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); 40 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
41 41
@@ -57,7 +57,7 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o
57 57
58void KSharedMemory::Finalize() { 58void KSharedMemory::Finalize() {
59 // Release the memory reservation. 59 // Release the memory reservation.
60 resource_limit->Release(LimitableResource::PhysicalMemory, size); 60 resource_limit->Release(LimitableResource::PhysicalMemoryMax, size);
61 resource_limit->Close(); 61 resource_limit->Close();
62 62
63 // Perform inherited finalization. 63 // Perform inherited finalization.
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index cc88d08f0..21207fe99 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -263,9 +263,9 @@ Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_
263 R_SUCCEED(); 263 R_SUCCEED();
264} 264}
265 265
266Result KThread::InitializeDummyThread(KThread* thread) { 266Result KThread::InitializeDummyThread(KThread* thread, KProcess* owner) {
267 // Initialize the thread. 267 // Initialize the thread.
268 R_TRY(thread->Initialize({}, {}, {}, DummyThreadPriority, 3, {}, ThreadType::Dummy)); 268 R_TRY(thread->Initialize({}, {}, {}, DummyThreadPriority, 3, owner, ThreadType::Dummy));
269 269
270 // Initialize emulation parameters. 270 // Initialize emulation parameters.
271 thread->stack_parameters.disable_count = 0; 271 thread->stack_parameters.disable_count = 0;
@@ -303,7 +303,7 @@ void KThread::PostDestroy(uintptr_t arg) {
303 const bool resource_limit_release_hint = (arg & 1); 303 const bool resource_limit_release_hint = (arg & 1);
304 const s64 hint_value = (resource_limit_release_hint ? 0 : 1); 304 const s64 hint_value = (resource_limit_release_hint ? 0 : 1);
305 if (owner != nullptr) { 305 if (owner != nullptr) {
306 owner->GetResourceLimit()->Release(LimitableResource::Threads, 1, hint_value); 306 owner->GetResourceLimit()->Release(LimitableResource::ThreadCountMax, 1, hint_value);
307 owner->Close(); 307 owner->Close();
308 } 308 }
309} 309}
@@ -1054,7 +1054,7 @@ void KThread::Exit() {
1054 1054
1055 // Release the thread resource hint, running thread count from parent. 1055 // Release the thread resource hint, running thread count from parent.
1056 if (parent != nullptr) { 1056 if (parent != nullptr) {
1057 parent->GetResourceLimit()->Release(Kernel::LimitableResource::Threads, 0, 1); 1057 parent->GetResourceLimit()->Release(Kernel::LimitableResource::ThreadCountMax, 0, 1);
1058 resource_limit_release_hint = true; 1058 resource_limit_release_hint = true;
1059 parent->DecrementRunningThreadCount(); 1059 parent->DecrementRunningThreadCount();
1060 } 1060 }
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index 30aa10c9a..f38c92bff 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -415,7 +415,7 @@ public:
415 415
416 static void PostDestroy(uintptr_t arg); 416 static void PostDestroy(uintptr_t arg);
417 417
418 [[nodiscard]] static Result InitializeDummyThread(KThread* thread); 418 [[nodiscard]] static Result InitializeDummyThread(KThread* thread, KProcess* owner);
419 419
420 [[nodiscard]] static Result InitializeMainThread(Core::System& system, KThread* thread, 420 [[nodiscard]] static Result InitializeMainThread(Core::System& system, KThread* thread,
421 s32 virt_core); 421 s32 virt_core);
diff --git a/src/core/hle/kernel/k_transfer_memory.cpp b/src/core/hle/kernel/k_transfer_memory.cpp
index b0320eb73..9f34c2d46 100644
--- a/src/core/hle/kernel/k_transfer_memory.cpp
+++ b/src/core/hle/kernel/k_transfer_memory.cpp
@@ -37,7 +37,7 @@ void KTransferMemory::Finalize() {
37 37
38void KTransferMemory::PostDestroy(uintptr_t arg) { 38void KTransferMemory::PostDestroy(uintptr_t arg) {
39 KProcess* owner = reinterpret_cast<KProcess*>(arg); 39 KProcess* owner = reinterpret_cast<KProcess*>(arg);
40 owner->GetResourceLimit()->Release(LimitableResource::TransferMemory, 1); 40 owner->GetResourceLimit()->Release(LimitableResource::TransferMemoryCountMax, 1);
41 owner->Close(); 41 owner->Close();
42} 42}
43 43
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index abff14079..b77723503 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -91,7 +91,7 @@ struct KernelCore::Impl {
91 pt_heap_region.GetSize()); 91 pt_heap_region.GetSize());
92 } 92 }
93 93
94 RegisterHostThread(); 94 RegisterHostThread(nullptr);
95 95
96 default_service_thread = &CreateServiceThread(kernel, "DefaultServiceThread"); 96 default_service_thread = &CreateServiceThread(kernel, "DefaultServiceThread");
97 } 97 }
@@ -229,18 +229,22 @@ struct KernelCore::Impl {
229 const auto kernel_size{sizes.second}; 229 const auto kernel_size{sizes.second};
230 230
231 // If setting the default system values fails, then something seriously wrong has occurred. 231 // If setting the default system values fails, then something seriously wrong has occurred.
232 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, total_size) 232 ASSERT(
233 system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemoryMax, total_size)
234 .IsSuccess());
235 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::ThreadCountMax, 800)
233 .IsSuccess()); 236 .IsSuccess());
234 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess()); 237 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::EventCountMax, 900)
235 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess());
236 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200)
237 .IsSuccess()); 238 .IsSuccess());
238 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess()); 239 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::TransferMemoryCountMax, 200)
239 system_resource_limit->Reserve(LimitableResource::PhysicalMemory, kernel_size); 240 .IsSuccess());
241 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::SessionCountMax, 1133)
242 .IsSuccess());
243 system_resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, kernel_size);
240 244
241 // Reserve secure applet memory, introduced in firmware 5.0.0 245 // Reserve secure applet memory, introduced in firmware 5.0.0
242 constexpr u64 secure_applet_memory_size{4_MiB}; 246 constexpr u64 secure_applet_memory_size{4_MiB};
243 ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory, 247 ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemoryMax,
244 secure_applet_memory_size)); 248 secure_applet_memory_size));
245 } 249 }
246 250
@@ -373,15 +377,18 @@ struct KernelCore::Impl {
373 } 377 }
374 378
375 // Gets the dummy KThread for the caller, allocating a new one if this is the first time 379 // Gets the dummy KThread for the caller, allocating a new one if this is the first time
376 KThread* GetHostDummyThread() { 380 KThread* GetHostDummyThread(KThread* existing_thread) {
377 auto initialize = [this](KThread* thread) { 381 auto initialize = [this](KThread* thread) {
378 ASSERT(KThread::InitializeDummyThread(thread).IsSuccess()); 382 ASSERT(KThread::InitializeDummyThread(thread, nullptr).IsSuccess());
379 thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId())); 383 thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId()));
380 return thread; 384 return thread;
381 }; 385 };
382 386
383 thread_local auto raw_thread = KThread(system.Kernel()); 387 thread_local KThread raw_thread{system.Kernel()};
384 thread_local auto thread = initialize(&raw_thread); 388 thread_local KThread* thread = nullptr;
389 if (thread == nullptr) {
390 thread = (existing_thread == nullptr) ? initialize(&raw_thread) : existing_thread;
391 }
385 392
386 return thread; 393 return thread;
387 } 394 }
@@ -396,9 +403,9 @@ struct KernelCore::Impl {
396 } 403 }
397 404
398 /// Registers a new host thread by allocating a host thread ID for it 405 /// Registers a new host thread by allocating a host thread ID for it
399 void RegisterHostThread() { 406 void RegisterHostThread(KThread* existing_thread) {
400 [[maybe_unused]] const auto this_id = GetHostThreadId(); 407 [[maybe_unused]] const auto this_id = GetHostThreadId();
401 [[maybe_unused]] const auto dummy_thread = GetHostDummyThread(); 408 [[maybe_unused]] const auto dummy_thread = GetHostDummyThread(existing_thread);
402 } 409 }
403 410
404 [[nodiscard]] u32 GetCurrentHostThreadID() { 411 [[nodiscard]] u32 GetCurrentHostThreadID() {
@@ -429,7 +436,7 @@ struct KernelCore::Impl {
429 KThread* GetCurrentEmuThread() { 436 KThread* GetCurrentEmuThread() {
430 const auto thread_id = GetCurrentHostThreadID(); 437 const auto thread_id = GetCurrentHostThreadID();
431 if (thread_id >= Core::Hardware::NUM_CPU_CORES) { 438 if (thread_id >= Core::Hardware::NUM_CPU_CORES) {
432 return GetHostDummyThread(); 439 return GetHostDummyThread(nullptr);
433 } 440 }
434 441
435 return current_thread; 442 return current_thread;
@@ -1120,8 +1127,12 @@ void KernelCore::RegisterCoreThread(std::size_t core_id) {
1120 impl->RegisterCoreThread(core_id); 1127 impl->RegisterCoreThread(core_id);
1121} 1128}
1122 1129
1123void KernelCore::RegisterHostThread() { 1130void KernelCore::RegisterHostThread(KThread* existing_thread) {
1124 impl->RegisterHostThread(); 1131 impl->RegisterHostThread(existing_thread);
1132
1133 if (existing_thread != nullptr) {
1134 ASSERT(GetCurrentEmuThread() == existing_thread);
1135 }
1125} 1136}
1126 1137
1127u32 KernelCore::GetCurrentHostThreadID() const { 1138u32 KernelCore::GetCurrentHostThreadID() const {
@@ -1196,16 +1207,28 @@ void KernelCore::Suspend(bool suspended) {
1196 const bool should_suspend{exception_exited || suspended}; 1207 const bool should_suspend{exception_exited || suspended};
1197 const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable; 1208 const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable;
1198 1209
1199 for (auto* process : GetProcessList()) { 1210 std::vector<KScopedAutoObject<KThread>> process_threads;
1200 process->SetActivity(activity); 1211 {
1212 KScopedSchedulerLock sl{*this};
1213
1214 if (auto* process = CurrentProcess(); process != nullptr) {
1215 process->SetActivity(activity);
1216
1217 if (!should_suspend) {
1218 // Runnable now; no need to wait.
1219 return;
1220 }
1201 1221
1202 if (should_suspend) {
1203 // Wait for execution to stop
1204 for (auto* thread : process->GetThreadList()) { 1222 for (auto* thread : process->GetThreadList()) {
1205 thread->WaitUntilSuspended(); 1223 process_threads.emplace_back(thread);
1206 } 1224 }
1207 } 1225 }
1208 } 1226 }
1227
1228 // Wait for execution to stop.
1229 for (auto& thread : process_threads) {
1230 thread->WaitUntilSuspended();
1231 }
1209} 1232}
1210 1233
1211void KernelCore::ShutdownCores() { 1234void KernelCore::ShutdownCores() {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 29617d736..2e22fe0f6 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -240,7 +240,7 @@ public:
240 void RegisterCoreThread(std::size_t core_id); 240 void RegisterCoreThread(std::size_t core_id);
241 241
242 /// Register the current thread as a non CPU core thread. 242 /// Register the current thread as a non CPU core thread.
243 void RegisterHostThread(); 243 void RegisterHostThread(KThread* existing_thread = nullptr);
244 244
245 /// Gets the virtual memory manager for the kernel. 245 /// Gets the virtual memory manager for the kernel.
246 KMemoryManager& MemoryManager(); 246 KMemoryManager& MemoryManager();
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/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp
index c8fe42537..e6e41ac34 100644
--- a/src/core/hle/kernel/service_thread.cpp
+++ b/src/core/hle/kernel/service_thread.cpp
@@ -36,11 +36,11 @@ public:
36private: 36private:
37 KernelCore& kernel; 37 KernelCore& kernel;
38 38
39 std::jthread m_thread; 39 std::jthread m_host_thread;
40 std::mutex m_session_mutex; 40 std::mutex m_session_mutex;
41 std::map<KServerSession*, std::shared_ptr<SessionRequestManager>> m_sessions; 41 std::map<KServerSession*, std::shared_ptr<SessionRequestManager>> m_sessions;
42 KEvent* m_wakeup_event; 42 KEvent* m_wakeup_event;
43 KProcess* m_process; 43 KThread* m_thread;
44 std::atomic<bool> m_shutdown_requested; 44 std::atomic<bool> m_shutdown_requested;
45 const std::string m_service_name; 45 const std::string m_service_name;
46}; 46};
@@ -132,7 +132,7 @@ void ServiceThread::Impl::SessionClosed(KServerSession* server_session,
132void ServiceThread::Impl::LoopProcess() { 132void ServiceThread::Impl::LoopProcess() {
133 Common::SetCurrentThreadName(m_service_name.c_str()); 133 Common::SetCurrentThreadName(m_service_name.c_str());
134 134
135 kernel.RegisterHostThread(); 135 kernel.RegisterHostThread(m_thread);
136 136
137 while (!m_shutdown_requested.load()) { 137 while (!m_shutdown_requested.load()) {
138 WaitAndProcessImpl(); 138 WaitAndProcessImpl();
@@ -160,7 +160,7 @@ ServiceThread::Impl::~Impl() {
160 // Shut down the processing thread. 160 // Shut down the processing thread.
161 m_shutdown_requested.store(true); 161 m_shutdown_requested.store(true);
162 m_wakeup_event->Signal(); 162 m_wakeup_event->Signal();
163 m_thread.join(); 163 m_host_thread.join();
164 164
165 // Lock mutex. 165 // Lock mutex.
166 m_session_mutex.lock(); 166 m_session_mutex.lock();
@@ -177,33 +177,22 @@ ServiceThread::Impl::~Impl() {
177 m_wakeup_event->GetReadableEvent().Close(); 177 m_wakeup_event->GetReadableEvent().Close();
178 m_wakeup_event->Close(); 178 m_wakeup_event->Close();
179 179
180 // Close process. 180 // Close thread.
181 m_process->Close(); 181 m_thread->Close();
182} 182}
183 183
184ServiceThread::Impl::Impl(KernelCore& kernel_, const std::string& service_name) 184ServiceThread::Impl::Impl(KernelCore& kernel_, const std::string& service_name)
185 : kernel{kernel_}, m_service_name{service_name} { 185 : kernel{kernel_}, m_service_name{service_name} {
186 // Initialize process.
187 m_process = KProcess::Create(kernel);
188 KProcess::Initialize(m_process, kernel.System(), service_name,
189 KProcess::ProcessType::KernelInternal, kernel.GetSystemResourceLimit());
190
191 // Reserve a new event from the process resource limit
192 KScopedResourceReservation event_reservation(m_process, LimitableResource::Events);
193 ASSERT(event_reservation.Succeeded());
194
195 // Initialize event. 186 // Initialize event.
196 m_wakeup_event = KEvent::Create(kernel); 187 m_wakeup_event = KEvent::Create(kernel);
197 m_wakeup_event->Initialize(m_process); 188 m_wakeup_event->Initialize(nullptr);
198
199 // Commit the event reservation.
200 event_reservation.Commit();
201 189
202 // Register the event. 190 // Initialize thread.
203 KEvent::Register(kernel, m_wakeup_event); 191 m_thread = KThread::Create(kernel);
192 ASSERT(KThread::InitializeDummyThread(m_thread, nullptr).IsSuccess());
204 193
205 // Start thread. 194 // Start thread.
206 m_thread = std::jthread([this] { LoopProcess(); }); 195 m_host_thread = std::jthread([this] { LoopProcess(); });
207} 196}
208 197
209ServiceThread::ServiceThread(KernelCore& kernel, const std::string& name) 198ServiceThread::ServiceThread(KernelCore& kernel, const std::string& name)
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index ecac97a52..e520cab47 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -267,7 +267,7 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien
267 267
268 // Reserve a new session from the process resource limit. 268 // Reserve a new session from the process resource limit.
269 // FIXME: LimitableResource_SessionCountMax 269 // FIXME: LimitableResource_SessionCountMax
270 KScopedResourceReservation session_reservation(&process, LimitableResource::Sessions); 270 KScopedResourceReservation session_reservation(&process, LimitableResource::SessionCountMax);
271 if (session_reservation.Succeeded()) { 271 if (session_reservation.Succeeded()) {
272 session = T::Create(system.Kernel()); 272 session = T::Create(system.Kernel());
273 } else { 273 } else {
@@ -298,7 +298,7 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien
298 298
299 // We successfully allocated a session, so add the object we allocated to the resource 299 // We successfully allocated a session, so add the object we allocated to the resource
300 // limit. 300 // limit.
301 // system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::Sessions, 1); 301 // system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::SessionCountMax, 1);
302 } 302 }
303 303
304 // Check that we successfully created a session. 304 // Check that we successfully created a session.
@@ -656,27 +656,12 @@ static Result ArbitrateUnlock32(Core::System& system, u32 address) {
656 return ArbitrateUnlock(system, address); 656 return ArbitrateUnlock(system, address);
657} 657}
658 658
659enum class BreakType : u32 {
660 Panic = 0,
661 AssertionFailed = 1,
662 PreNROLoad = 3,
663 PostNROLoad = 4,
664 PreNROUnload = 5,
665 PostNROUnload = 6,
666 CppException = 7,
667};
668
669struct BreakReason {
670 union {
671 u32 raw;
672 BitField<0, 30, BreakType> break_type;
673 BitField<31, 1, u32> signal_debugger;
674 };
675};
676
677/// Break program execution 659/// Break program execution
678static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { 660static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
679 BreakReason break_reason{reason}; 661 BreakReason break_reason =
662 static_cast<BreakReason>(reason & ~static_cast<u32>(BreakReason::NotificationOnlyFlag));
663 bool notification_only = (reason & static_cast<u32>(BreakReason::NotificationOnlyFlag)) != 0;
664
680 bool has_dumped_buffer{}; 665 bool has_dumped_buffer{};
681 std::vector<u8> debug_buffer; 666 std::vector<u8> debug_buffer;
682 667
@@ -705,57 +690,56 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
705 } 690 }
706 has_dumped_buffer = true; 691 has_dumped_buffer = true;
707 }; 692 };
708 switch (break_reason.break_type) { 693 switch (break_reason) {
709 case BreakType::Panic: 694 case BreakReason::Panic:
710 LOG_CRITICAL(Debug_Emulated, "Signalling debugger, PANIC! info1=0x{:016X}, info2=0x{:016X}", 695 LOG_CRITICAL(Debug_Emulated, "Userspace PANIC! info1=0x{:016X}, info2=0x{:016X}", info1,
711 info1, info2); 696 info2);
712 handle_debug_buffer(info1, info2); 697 handle_debug_buffer(info1, info2);
713 break; 698 break;
714 case BreakType::AssertionFailed: 699 case BreakReason::Assert:
715 LOG_CRITICAL(Debug_Emulated, 700 LOG_CRITICAL(Debug_Emulated, "Userspace Assertion failed! info1=0x{:016X}, info2=0x{:016X}",
716 "Signalling debugger, Assertion failed! info1=0x{:016X}, info2=0x{:016X}",
717 info1, info2); 701 info1, info2);
718 handle_debug_buffer(info1, info2); 702 handle_debug_buffer(info1, info2);
719 break; 703 break;
720 case BreakType::PreNROLoad: 704 case BreakReason::User:
721 LOG_WARNING( 705 LOG_WARNING(Debug_Emulated, "Userspace Break! 0x{:016X} with size 0x{:016X}", info1, info2);
722 Debug_Emulated, 706 handle_debug_buffer(info1, info2);
723 "Signalling debugger, Attempting to load an NRO at 0x{:016X} with size 0x{:016X}",
724 info1, info2);
725 break; 707 break;
726 case BreakType::PostNROLoad: 708 case BreakReason::PreLoadDll:
727 LOG_WARNING(Debug_Emulated, 709 LOG_INFO(Debug_Emulated,
728 "Signalling debugger, Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1, 710 "Userspace Attempting to load an NRO at 0x{:016X} with size 0x{:016X}", info1,
729 info2); 711 info2);
730 break; 712 break;
731 case BreakType::PreNROUnload: 713 case BreakReason::PostLoadDll:
732 LOG_WARNING( 714 LOG_INFO(Debug_Emulated, "Userspace Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
733 Debug_Emulated, 715 info2);
734 "Signalling debugger, Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}",
735 info1, info2);
736 break; 716 break;
737 case BreakType::PostNROUnload: 717 case BreakReason::PreUnloadDll:
738 LOG_WARNING(Debug_Emulated, 718 LOG_INFO(Debug_Emulated,
739 "Signalling debugger, Unloaded an NRO at 0x{:016X} with size 0x{:016X}", info1, 719 "Userspace Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}", info1,
740 info2); 720 info2);
741 break; 721 break;
742 case BreakType::CppException: 722 case BreakReason::PostUnloadDll:
723 LOG_INFO(Debug_Emulated, "Userspace Unloaded an NRO at 0x{:016X} with size 0x{:016X}",
724 info1, info2);
725 break;
726 case BreakReason::CppException:
743 LOG_CRITICAL(Debug_Emulated, "Signalling debugger. Uncaught C++ exception encountered."); 727 LOG_CRITICAL(Debug_Emulated, "Signalling debugger. Uncaught C++ exception encountered.");
744 break; 728 break;
745 default: 729 default:
746 LOG_WARNING( 730 LOG_WARNING(
747 Debug_Emulated, 731 Debug_Emulated,
748 "Signalling debugger, Unknown break reason {}, info1=0x{:016X}, info2=0x{:016X}", 732 "Signalling debugger, Unknown break reason {:#X}, info1=0x{:016X}, info2=0x{:016X}",
749 static_cast<u32>(break_reason.break_type.Value()), info1, info2); 733 reason, info1, info2);
750 handle_debug_buffer(info1, info2); 734 handle_debug_buffer(info1, info2);
751 break; 735 break;
752 } 736 }
753 737
754 system.GetReporter().SaveSvcBreakReport( 738 system.GetReporter().SaveSvcBreakReport(reason, notification_only, info1, info2,
755 static_cast<u32>(break_reason.break_type.Value()), break_reason.signal_debugger.As<bool>(), 739 has_dumped_buffer ? std::make_optional(debug_buffer)
756 info1, info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt); 740 : std::nullopt);
757 741
758 if (!break_reason.signal_debugger) { 742 if (!notification_only) {
759 LOG_CRITICAL( 743 LOG_CRITICAL(
760 Debug_Emulated, 744 Debug_Emulated,
761 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", 745 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
@@ -1716,13 +1700,13 @@ static Result QueryProcessMemory(Core::System& system, VAddr memory_info_address
1716 auto& memory{system.Memory()}; 1700 auto& memory{system.Memory()};
1717 const auto memory_info{process->PageTable().QueryInfo(address).GetSvcMemoryInfo()}; 1701 const auto memory_info{process->PageTable().QueryInfo(address).GetSvcMemoryInfo()};
1718 1702
1719 memory.Write64(memory_info_address + 0x00, memory_info.addr); 1703 memory.Write64(memory_info_address + 0x00, memory_info.base_address);
1720 memory.Write64(memory_info_address + 0x08, memory_info.size); 1704 memory.Write64(memory_info_address + 0x08, memory_info.size);
1721 memory.Write32(memory_info_address + 0x10, static_cast<u32>(memory_info.state) & 0xff); 1705 memory.Write32(memory_info_address + 0x10, static_cast<u32>(memory_info.state) & 0xff);
1722 memory.Write32(memory_info_address + 0x14, static_cast<u32>(memory_info.attr)); 1706 memory.Write32(memory_info_address + 0x14, static_cast<u32>(memory_info.attribute));
1723 memory.Write32(memory_info_address + 0x18, static_cast<u32>(memory_info.perm)); 1707 memory.Write32(memory_info_address + 0x18, static_cast<u32>(memory_info.permission));
1724 memory.Write32(memory_info_address + 0x1c, memory_info.ipc_refcount); 1708 memory.Write32(memory_info_address + 0x1c, memory_info.ipc_count);
1725 memory.Write32(memory_info_address + 0x20, memory_info.device_refcount); 1709 memory.Write32(memory_info_address + 0x20, memory_info.device_count);
1726 memory.Write32(memory_info_address + 0x24, 0); 1710 memory.Write32(memory_info_address + 0x24, 0);
1727 1711
1728 // Page info appears to be currently unused by the kernel and is always set to zero. 1712 // Page info appears to be currently unused by the kernel and is always set to zero.
@@ -1943,7 +1927,7 @@ static Result CreateThread(Core::System& system, Handle* out_handle, VAddr entry
1943 1927
1944 // Reserve a new thread from the process resource limit (waiting up to 100ms). 1928 // Reserve a new thread from the process resource limit (waiting up to 100ms).
1945 KScopedResourceReservation thread_reservation( 1929 KScopedResourceReservation thread_reservation(
1946 kernel.CurrentProcess(), LimitableResource::Threads, 1, 1930 kernel.CurrentProcess(), LimitableResource::ThreadCountMax, 1,
1947 system.CoreTiming().GetGlobalTimeNs().count() + 100000000); 1931 system.CoreTiming().GetGlobalTimeNs().count() + 100000000);
1948 if (!thread_reservation.Succeeded()) { 1932 if (!thread_reservation.Succeeded()) {
1949 LOG_ERROR(Kernel_SVC, "Could not reserve a new thread"); 1933 LOG_ERROR(Kernel_SVC, "Could not reserve a new thread");
@@ -2344,7 +2328,7 @@ static Result CreateTransferMemory(Core::System& system, Handle* out, VAddr addr
2344 2328
2345 // Reserve a new transfer memory from the process resource limit. 2329 // Reserve a new transfer memory from the process resource limit.
2346 KScopedResourceReservation trmem_reservation(kernel.CurrentProcess(), 2330 KScopedResourceReservation trmem_reservation(kernel.CurrentProcess(),
2347 LimitableResource::TransferMemory); 2331 LimitableResource::TransferMemoryCountMax);
2348 R_UNLESS(trmem_reservation.Succeeded(), ResultLimitReached); 2332 R_UNLESS(trmem_reservation.Succeeded(), ResultLimitReached);
2349 2333
2350 // Create the transfer memory. 2334 // Create the transfer memory.
@@ -2496,7 +2480,7 @@ static Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_r
2496 2480
2497 // Reserve a new event from the process resource limit 2481 // Reserve a new event from the process resource limit
2498 KScopedResourceReservation event_reservation(kernel.CurrentProcess(), 2482 KScopedResourceReservation event_reservation(kernel.CurrentProcess(),
2499 LimitableResource::Events); 2483 LimitableResource::EventCountMax);
2500 R_UNLESS(event_reservation.Succeeded(), ResultLimitReached); 2484 R_UNLESS(event_reservation.Succeeded(), ResultLimitReached);
2501 2485
2502 // Create a new event. 2486 // Create a new event.
@@ -2539,11 +2523,6 @@ static Result CreateEvent32(Core::System& system, Handle* out_write, Handle* out
2539static Result GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) { 2523static Result GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) {
2540 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type); 2524 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type);
2541 2525
2542 // This function currently only allows retrieving a process' status.
2543 enum class InfoType {
2544 Status,
2545 };
2546
2547 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); 2526 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
2548 KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle); 2527 KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
2549 if (process.IsNull()) { 2528 if (process.IsNull()) {
@@ -2552,9 +2531,9 @@ static Result GetProcessInfo(Core::System& system, u64* out, Handle process_hand
2552 return ResultInvalidHandle; 2531 return ResultInvalidHandle;
2553 } 2532 }
2554 2533
2555 const auto info_type = static_cast<InfoType>(type); 2534 const auto info_type = static_cast<ProcessInfoType>(type);
2556 if (info_type != InfoType::Status) { 2535 if (info_type != ProcessInfoType::ProcessState) {
2557 LOG_ERROR(Kernel_SVC, "Expected info_type to be Status but got {} instead", type); 2536 LOG_ERROR(Kernel_SVC, "Expected info_type to be ProcessState but got {} instead", type);
2558 return ResultInvalidEnumValue; 2537 return ResultInvalidEnumValue;
2559 } 2538 }
2560 2539
@@ -2722,14 +2701,24 @@ static Result GetThreadList(Core::System& system, u32* out_num_threads, VAddr ou
2722 return ResultSuccess; 2701 return ResultSuccess;
2723} 2702}
2724 2703
2725static Result FlushProcessDataCache32([[maybe_unused]] Core::System& system, 2704static Result FlushProcessDataCache32(Core::System& system, Handle process_handle, u64 address,
2726 [[maybe_unused]] Handle handle, [[maybe_unused]] u32 address, 2705 u64 size) {
2727 [[maybe_unused]] u32 size) { 2706 // Validate address/size.
2728 // Note(Blinkhawk): For emulation purposes of the data cache this is mostly a no-op, 2707 R_UNLESS(size > 0, ResultInvalidSize);
2729 // as all emulation is done in the same cache level in host architecture, thus data cache 2708 R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory);
2730 // does not need flushing. 2709 R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory);
2731 LOG_DEBUG(Kernel_SVC, "called"); 2710
2732 return ResultSuccess; 2711 // Get the process from its handle.
2712 KScopedAutoObject process =
2713 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle);
2714 R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
2715
2716 // Verify the region is within range.
2717 auto& page_table = process->PageTable();
2718 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
2719
2720 // Perform the operation.
2721 R_RETURN(system.Memory().FlushDataCache(*process, address, size));
2733} 2722}
2734 2723
2735namespace { 2724namespace {
diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h
index 9b0305552..33eebcef6 100644
--- a/src/core/hle/kernel/svc_types.h
+++ b/src/core/hle/kernel/svc_types.h
@@ -8,6 +8,8 @@
8 8
9namespace Kernel::Svc { 9namespace Kernel::Svc {
10 10
11using Handle = u32;
12
11enum class MemoryState : u32 { 13enum class MemoryState : u32 {
12 Free = 0x00, 14 Free = 0x00,
13 Io = 0x01, 15 Io = 0x01,
@@ -55,17 +57,6 @@ enum class MemoryPermission : u32 {
55}; 57};
56DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission); 58DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission);
57 59
58struct MemoryInfo {
59 u64 addr{};
60 u64 size{};
61 MemoryState state{};
62 MemoryAttribute attr{};
63 MemoryPermission perm{};
64 u32 ipc_refcount{};
65 u32 device_refcount{};
66 u32 padding{};
67};
68
69enum class SignalType : u32 { 60enum class SignalType : u32 {
70 Signal = 0, 61 Signal = 0,
71 SignalAndIncrementIfEqual = 1, 62 SignalAndIncrementIfEqual = 1,
@@ -124,7 +115,57 @@ enum class ProcessExitReason : u32 {
124 115
125constexpr inline size_t ThreadLocalRegionSize = 0x200; 116constexpr inline size_t ThreadLocalRegionSize = 0x200;
126 117
127// Debug types. 118struct PageInfo {
119 u32 flags;
120};
121
122// Info Types.
123enum class InfoType : u32 {
124 CoreMask = 0,
125 PriorityMask = 1,
126 AliasRegionAddress = 2,
127 AliasRegionSize = 3,
128 HeapRegionAddress = 4,
129 HeapRegionSize = 5,
130 TotalMemorySize = 6,
131 UsedMemorySize = 7,
132 DebuggerAttached = 8,
133 ResourceLimit = 9,
134 IdleTickCount = 10,
135 RandomEntropy = 11,
136 AslrRegionAddress = 12,
137 AslrRegionSize = 13,
138 StackRegionAddress = 14,
139 StackRegionSize = 15,
140 SystemResourceSizeTotal = 16,
141 SystemResourceSizeUsed = 17,
142 ProgramId = 18,
143 InitialProcessIdRange = 19,
144 UserExceptionContextAddress = 20,
145 TotalNonSystemMemorySize = 21,
146 UsedNonSystemMemorySize = 22,
147 IsApplication = 23,
148 FreeThreadCount = 24,
149 ThreadTickCount = 25,
150 IsSvcPermitted = 26,
151
152 MesosphereMeta = 65000,
153 MesosphereCurrentProcess = 65001,
154};
155
156enum class BreakReason : u32 {
157 Panic = 0,
158 Assert = 1,
159 User = 2,
160 PreLoadDll = 3,
161 PostLoadDll = 4,
162 PreUnloadDll = 5,
163 PostUnloadDll = 6,
164 CppException = 7,
165
166 NotificationOnlyFlag = 0x80000000,
167};
168
128enum class DebugEvent : u32 { 169enum class DebugEvent : u32 {
129 CreateProcess = 0, 170 CreateProcess = 0,
130 CreateThread = 1, 171 CreateThread = 1,
@@ -133,6 +174,14 @@ enum class DebugEvent : u32 {
133 Exception = 4, 174 Exception = 4,
134}; 175};
135 176
177enum class DebugThreadParam : u32 {
178 Priority = 0,
179 State = 1,
180 IdealCore = 2,
181 CurrentCore = 3,
182 AffinityMask = 4,
183};
184
136enum class DebugException : u32 { 185enum class DebugException : u32 {
137 UndefinedInstruction = 0, 186 UndefinedInstruction = 0,
138 InstructionAbort = 1, 187 InstructionAbort = 1,
@@ -146,4 +195,401 @@ enum class DebugException : u32 {
146 MemorySystemError = 9, 195 MemorySystemError = 9,
147}; 196};
148 197
198enum class DebugEventFlag : u32 {
199 Stopped = (1u << 0),
200};
201
202enum class BreakPointType : u32 {
203 HardwareInstruction = 0,
204 HardwareData = 1,
205};
206
207enum class HardwareBreakPointRegisterName : u32 {
208 I0 = 0,
209 I1 = 1,
210 I2 = 2,
211 I3 = 3,
212 I4 = 4,
213 I5 = 5,
214 I6 = 6,
215 I7 = 7,
216 I8 = 8,
217 I9 = 9,
218 I10 = 10,
219 I11 = 11,
220 I12 = 12,
221 I13 = 13,
222 I14 = 14,
223 I15 = 15,
224 D0 = 16,
225 D1 = 17,
226 D2 = 18,
227 D3 = 19,
228 D4 = 20,
229 D5 = 21,
230 D6 = 22,
231 D7 = 23,
232 D8 = 24,
233 D9 = 25,
234 D10 = 26,
235 D11 = 27,
236 D12 = 28,
237 D13 = 29,
238 D14 = 30,
239 D15 = 31,
240};
241
242namespace lp64 {
243struct LastThreadContext {
244 u64 fp;
245 u64 sp;
246 u64 lr;
247 u64 pc;
248};
249
250struct PhysicalMemoryInfo {
251 PAddr physical_address;
252 u64 virtual_address;
253 u64 size;
254};
255
256struct DebugInfoCreateProcess {
257 u64 program_id;
258 u64 process_id;
259 std::array<char, 0xC> name;
260 u32 flags;
261 u64 user_exception_context_address; // 5.0.0+
262};
263
264struct DebugInfoCreateThread {
265 u64 thread_id;
266 u64 tls_address;
267 // Removed in 11.0.0 u64 entrypoint;
268};
269
270struct DebugInfoExitProcess {
271 ProcessExitReason reason;
272};
273
274struct DebugInfoExitThread {
275 ThreadExitReason reason;
276};
277
278struct DebugInfoUndefinedInstructionException {
279 u32 insn;
280};
281
282struct DebugInfoDataAbortException {
283 u64 address;
284};
285
286struct DebugInfoAlignmentFaultException {
287 u64 address;
288};
289
290struct DebugInfoBreakPointException {
291 BreakPointType type;
292 u64 address;
293};
294
295struct DebugInfoUserBreakException {
296 BreakReason break_reason;
297 u64 address;
298 u64 size;
299};
300
301struct DebugInfoDebuggerBreakException {
302 std::array<u64, 4> active_thread_ids;
303};
304
305struct DebugInfoUndefinedSystemCallException {
306 u32 id;
307};
308
309union DebugInfoSpecificException {
310 DebugInfoUndefinedInstructionException undefined_instruction;
311 DebugInfoDataAbortException data_abort;
312 DebugInfoAlignmentFaultException alignment_fault;
313 DebugInfoBreakPointException break_point;
314 DebugInfoUserBreakException user_break;
315 DebugInfoDebuggerBreakException debugger_break;
316 DebugInfoUndefinedSystemCallException undefined_system_call;
317 u64 raw;
318};
319
320struct DebugInfoException {
321 DebugException type;
322 u64 address;
323 DebugInfoSpecificException specific;
324};
325
326union DebugInfo {
327 DebugInfoCreateProcess create_process;
328 DebugInfoCreateThread create_thread;
329 DebugInfoExitProcess exit_process;
330 DebugInfoExitThread exit_thread;
331 DebugInfoException exception;
332};
333
334struct DebugEventInfo {
335 DebugEvent type;
336 u32 flags;
337 u64 thread_id;
338 DebugInfo info;
339};
340static_assert(sizeof(DebugEventInfo) >= 0x40);
341
342struct SecureMonitorArguments {
343 std::array<u64, 8> r;
344};
345static_assert(sizeof(SecureMonitorArguments) == 0x40);
346} // namespace lp64
347
348namespace ilp32 {
349struct LastThreadContext {
350 u32 fp;
351 u32 sp;
352 u32 lr;
353 u32 pc;
354};
355
356struct PhysicalMemoryInfo {
357 PAddr physical_address;
358 u32 virtual_address;
359 u32 size;
360};
361
362struct DebugInfoCreateProcess {
363 u64 program_id;
364 u64 process_id;
365 std::array<char, 0xC> name;
366 u32 flags;
367 u32 user_exception_context_address; // 5.0.0+
368};
369
370struct DebugInfoCreateThread {
371 u64 thread_id;
372 u32 tls_address;
373 // Removed in 11.0.0 u32 entrypoint;
374};
375
376struct DebugInfoExitProcess {
377 ProcessExitReason reason;
378};
379
380struct DebugInfoExitThread {
381 ThreadExitReason reason;
382};
383
384struct DebugInfoUndefinedInstructionException {
385 u32 insn;
386};
387
388struct DebugInfoDataAbortException {
389 u32 address;
390};
391
392struct DebugInfoAlignmentFaultException {
393 u32 address;
394};
395
396struct DebugInfoBreakPointException {
397 BreakPointType type;
398 u32 address;
399};
400
401struct DebugInfoUserBreakException {
402 BreakReason break_reason;
403 u32 address;
404 u32 size;
405};
406
407struct DebugInfoDebuggerBreakException {
408 std::array<u64, 4> active_thread_ids;
409};
410
411struct DebugInfoUndefinedSystemCallException {
412 u32 id;
413};
414
415union DebugInfoSpecificException {
416 DebugInfoUndefinedInstructionException undefined_instruction;
417 DebugInfoDataAbortException data_abort;
418 DebugInfoAlignmentFaultException alignment_fault;
419 DebugInfoBreakPointException break_point;
420 DebugInfoUserBreakException user_break;
421 DebugInfoDebuggerBreakException debugger_break;
422 DebugInfoUndefinedSystemCallException undefined_system_call;
423 u64 raw;
424};
425
426struct DebugInfoException {
427 DebugException type;
428 u32 address;
429 DebugInfoSpecificException specific;
430};
431
432union DebugInfo {
433 DebugInfoCreateProcess create_process;
434 DebugInfoCreateThread create_thread;
435 DebugInfoExitProcess exit_process;
436 DebugInfoExitThread exit_thread;
437 DebugInfoException exception;
438};
439
440struct DebugEventInfo {
441 DebugEvent type;
442 u32 flags;
443 u64 thread_id;
444 DebugInfo info;
445};
446
447struct SecureMonitorArguments {
448 std::array<u32, 8> r;
449};
450static_assert(sizeof(SecureMonitorArguments) == 0x20);
451} // namespace ilp32
452
453struct ThreadContext {
454 std::array<u64, 29> r;
455 u64 fp;
456 u64 lr;
457 u64 sp;
458 u64 pc;
459 u32 pstate;
460 u32 padding;
461 std::array<u128, 32> v;
462 u32 fpcr;
463 u32 fpsr;
464 u64 tpidr;
465};
466static_assert(sizeof(ThreadContext) == 0x320);
467
468struct MemoryInfo {
469 u64 base_address;
470 u64 size;
471 MemoryState state;
472 MemoryAttribute attribute;
473 MemoryPermission permission;
474 u32 ipc_count;
475 u32 device_count;
476 u32 padding;
477};
478
479enum class LimitableResource : u32 {
480 PhysicalMemoryMax = 0,
481 ThreadCountMax = 1,
482 EventCountMax = 2,
483 TransferMemoryCountMax = 3,
484 SessionCountMax = 4,
485 Count,
486};
487
488enum class IoPoolType : u32 {
489 // Not supported.
490 Count = 0,
491};
492
493enum class MemoryMapping : u32 {
494 IoRegister = 0,
495 Uncached = 1,
496 Memory = 2,
497};
498
499enum class KernelDebugType : u32 {
500 Thread = 0,
501 ThreadCallStack = 1,
502 KernelObject = 2,
503 Handle_ = 3,
504 Memory = 4,
505 PageTable = 5,
506 CpuUtilization = 6,
507 Process = 7,
508 SuspendProcess = 8,
509 ResumeProcess = 9,
510 Port = 10,
511};
512
513enum class KernelTraceState : u32 {
514 Disabled = 0,
515 Enabled = 1,
516};
517
518enum class CodeMemoryOperation : u32 {
519 Map = 0,
520 MapToOwner = 1,
521 Unmap = 2,
522 UnmapFromOwner = 3,
523};
524
525enum class InterruptType : u32 {
526 Edge = 0,
527 Level = 1,
528};
529
530enum class DeviceName {
531 Afi = 0,
532 Avpc = 1,
533 Dc = 2,
534 Dcb = 3,
535 Hc = 4,
536 Hda = 5,
537 Isp2 = 6,
538 MsencNvenc = 7,
539 Nv = 8,
540 Nv2 = 9,
541 Ppcs = 10,
542 Sata = 11,
543 Vi = 12,
544 Vic = 13,
545 XusbHost = 14,
546 XusbDev = 15,
547 Tsec = 16,
548 Ppcs1 = 17,
549 Dc1 = 18,
550 Sdmmc1a = 19,
551 Sdmmc2a = 20,
552 Sdmmc3a = 21,
553 Sdmmc4a = 22,
554 Isp2b = 23,
555 Gpu = 24,
556 Gpub = 25,
557 Ppcs2 = 26,
558 Nvdec = 27,
559 Ape = 28,
560 Se = 29,
561 Nvjpg = 30,
562 Hc1 = 31,
563 Se1 = 32,
564 Axiap = 33,
565 Etr = 34,
566 Tsecb = 35,
567 Tsec1 = 36,
568 Tsecb1 = 37,
569 Nvdec1 = 38,
570 Count,
571};
572
573enum class SystemInfoType : u32 {
574 TotalPhysicalMemorySize = 0,
575 UsedPhysicalMemorySize = 1,
576 InitialProcessIdRange = 2,
577};
578
579enum class ProcessInfoType : u32 {
580 ProcessState = 0,
581};
582
583struct CreateProcessParameter {
584 std::array<char, 12> name;
585 u32 version;
586 u64 program_id;
587 u64 code_address;
588 s32 code_num_pages;
589 u32 flags;
590 Handle reslimit;
591 s32 system_resource_num_pages;
592};
593static_assert(sizeof(CreateProcessParameter) == 0x30);
594
149} // namespace Kernel::Svc 595} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 272c54cf7..3730937fe 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -722,4 +722,12 @@ void SvcWrap32(Core::System& system) {
722 FuncReturn(system, retval); 722 FuncReturn(system, retval);
723} 723}
724 724
725// Used by Invalidate/Store/FlushProcessDataCache32
726template <Result func(Core::System&, Handle, u64, u64)>
727void SvcWrap32(Core::System& system) {
728 const u64 address = (Param(system, 3) << 32) | Param(system, 2);
729 const u64 size = (Param(system, 4) << 32) | Param(system, 1);
730 FuncReturn32(system, func(system, Param32(system, 0), address, size).raw);
731}
732
725} // namespace Kernel 733} // namespace Kernel
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 56c990728..240f06689 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -28,30 +28,49 @@ enum class ErrorModule : u32 {
28 Loader = 9, 28 Loader = 9,
29 CMIF = 10, 29 CMIF = 10,
30 HIPC = 11, 30 HIPC = 11,
31 TMA = 12,
32 DMNT = 13,
33 GDS = 14,
31 PM = 15, 34 PM = 15,
32 NS = 16, 35 NS = 16,
36 BSDSockets = 17,
33 HTC = 18, 37 HTC = 18,
38 TSC = 19,
34 NCMContent = 20, 39 NCMContent = 20,
35 SM = 21, 40 SM = 21,
36 RO = 22, 41 RO = 22,
42 GC = 23,
37 SDMMC = 24, 43 SDMMC = 24,
38 OVLN = 25, 44 OVLN = 25,
39 SPL = 26, 45 SPL = 26,
46 Socket = 27,
47 HTCLOW = 29,
48 DDSF = 30,
49 HTCFS = 31,
50 Async = 32,
51 Util = 33,
52 TIPC = 35,
53 ANIF = 37,
40 ETHC = 100, 54 ETHC = 100,
41 I2C = 101, 55 I2C = 101,
42 GPIO = 102, 56 GPIO = 102,
43 UART = 103, 57 UART = 103,
58 CPAD = 104,
44 Settings = 105, 59 Settings = 105,
60 FTM = 106,
45 WLAN = 107, 61 WLAN = 107,
46 XCD = 108, 62 XCD = 108,
63 TMP451 = 109,
47 NIFM = 110, 64 NIFM = 110,
48 Hwopus = 111, 65 Hwopus = 111,
66 LSM6DS3 = 112,
49 Bluetooth = 113, 67 Bluetooth = 113,
50 VI = 114, 68 VI = 114,
51 NFP = 115, 69 NFP = 115,
52 Time = 116, 70 Time = 116,
53 FGM = 117, 71 FGM = 117,
54 OE = 118, 72 OE = 118,
73 BH1730FVC = 119,
55 PCIe = 120, 74 PCIe = 120,
56 Friends = 121, 75 Friends = 121,
57 BCAT = 122, 76 BCAT = 122,
@@ -65,7 +84,7 @@ enum class ErrorModule : u32 {
65 AHID = 130, 84 AHID = 130,
66 Qlaunch = 132, 85 Qlaunch = 132,
67 PCV = 133, 86 PCV = 133,
68 OMM = 134, 87 USBPD = 134,
69 BPC = 135, 88 BPC = 135,
70 PSM = 136, 89 PSM = 136,
71 NIM = 137, 90 NIM = 137,
@@ -75,18 +94,22 @@ enum class ErrorModule : u32 {
75 NSD = 141, 94 NSD = 141,
76 PCTL = 142, 95 PCTL = 142,
77 BTM = 143, 96 BTM = 143,
97 LA = 144,
78 ETicket = 145, 98 ETicket = 145,
79 NGC = 146, 99 NGC = 146,
80 ERPT = 147, 100 ERPT = 147,
81 APM = 148, 101 APM = 148,
102 CEC = 149,
82 Profiler = 150, 103 Profiler = 150,
83 ErrorUpload = 151, 104 ErrorUpload = 151,
105 LIDBE = 152,
84 Audio = 153, 106 Audio = 153,
85 NPNS = 154, 107 NPNS = 154,
86 NPNSHTTPSTREAM = 155, 108 NPNSHTTPSTREAM = 155,
87 ARP = 157, 109 ARP = 157,
88 SWKBD = 158, 110 SWKBD = 158,
89 BOOT = 159, 111 BOOT = 159,
112 NetDiag = 160,
90 NFCMifare = 161, 113 NFCMifare = 161,
91 UserlandAssert = 162, 114 UserlandAssert = 162,
92 Fatal = 163, 115 Fatal = 163,
@@ -94,17 +117,68 @@ enum class ErrorModule : u32 {
94 SPSM = 165, 117 SPSM = 165,
95 BGTC = 167, 118 BGTC = 167,
96 UserlandCrash = 168, 119 UserlandCrash = 168,
120 SASBUS = 169,
121 PI = 170,
122 AudioCtrl = 172,
123 LBL = 173,
124 JIT = 175,
125 HDCP = 176,
126 OMM = 177,
127 PDM = 178,
128 OLSC = 179,
97 SREPO = 180, 129 SREPO = 180,
98 Dauth = 181, 130 Dauth = 181,
131 STDFU = 182,
132 DBG = 183,
133 DHCPS = 186,
134 SPI = 187,
135 AVM = 188,
136 PWM = 189,
137 RTC = 191,
138 Regulator = 192,
139 LED = 193,
140 SIO = 195,
141 PCM = 196,
142 CLKRST = 197,
143 POWCTL = 198,
144 AudioOld = 201,
99 HID = 202, 145 HID = 202,
100 LDN = 203, 146 LDN = 203,
147 CS = 204,
101 Irsensor = 205, 148 Irsensor = 205,
102 Capture = 206, 149 Capture = 206,
103 Manu = 208, 150 Manu = 208,
104 ATK = 209, 151 ATK = 209,
152 WEB = 210,
153 LCS = 211,
105 GRC = 212, 154 GRC = 212,
155 Repair = 213,
156 Album = 214,
157 RID = 215,
106 Migration = 216, 158 Migration = 216,
107 MigrationLdcServ = 217, 159 MigrationLdcServ = 217,
160 HIDBUS = 218,
161 ENS = 219,
162 WebSocket = 223,
163 DCDMTP = 227,
164 PGL = 228,
165 Notification = 229,
166 INS = 230,
167 LP2P = 231,
168 RCD = 232,
169 LCM40607 = 233,
170 PRC = 235,
171 TMAHTC = 237,
172 ECTX = 238,
173 MNPP = 239,
174 HSHL = 240,
175 CAPMTP = 242,
176 DP2HDMI = 244,
177 Cradle = 245,
178 SProfile = 246,
179 NDRM = 250,
180 TSPM = 499,
181 DevMenu = 500,
108 GeneralWebApplet = 800, 182 GeneralWebApplet = 800,
109 WifiWebAuthApplet = 809, 183 WifiWebAuthApplet = 809,
110 WhitelistedApplet = 810, 184 WhitelistedApplet = 810,
diff --git a/src/core/hle/service/am/applets/applet_error.cpp b/src/core/hle/service/am/applets/applet_error.cpp
index fcf34bf7e..bae0d99a6 100644
--- a/src/core/hle/service/am/applets/applet_error.cpp
+++ b/src/core/hle/service/am/applets/applet_error.cpp
@@ -144,6 +144,7 @@ void Error::Initialize() {
144 break; 144 break;
145 default: 145 default:
146 UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", mode); 146 UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", mode);
147 break;
147 } 148 }
148} 149}
149 150
diff --git a/src/core/hle/service/am/applets/applet_general_backend.cpp b/src/core/hle/service/am/applets/applet_general_backend.cpp
index c34ef08b3..e50acdaf6 100644
--- a/src/core/hle/service/am/applets/applet_general_backend.cpp
+++ b/src/core/hle/service/am/applets/applet_general_backend.cpp
@@ -129,6 +129,7 @@ void Auth::Execute() {
129 } 129 }
130 default: 130 default:
131 unimplemented_log(); 131 unimplemented_log();
132 break;
132 } 133 }
133} 134}
134 135
@@ -192,6 +193,7 @@ void PhotoViewer::Execute() {
192 break; 193 break;
193 default: 194 default:
194 UNIMPLEMENTED_MSG("Unimplemented PhotoViewer applet mode={:02X}!", mode); 195 UNIMPLEMENTED_MSG("Unimplemented PhotoViewer applet mode={:02X}!", mode);
196 break;
195 } 197 }
196} 198}
197 199
diff --git a/src/core/hle/service/kernel_helpers.cpp b/src/core/hle/service/kernel_helpers.cpp
index af133af93..42991928e 100644
--- a/src/core/hle/service/kernel_helpers.cpp
+++ b/src/core/hle/service/kernel_helpers.cpp
@@ -31,7 +31,7 @@ ServiceContext::~ServiceContext() {
31Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) { 31Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) {
32 // Reserve a new event from the process resource limit 32 // Reserve a new event from the process resource limit
33 Kernel::KScopedResourceReservation event_reservation(process, 33 Kernel::KScopedResourceReservation event_reservation(process,
34 Kernel::LimitableResource::Events); 34 Kernel::LimitableResource::EventCountMax);
35 if (!event_reservation.Succeeded()) { 35 if (!event_reservation.Succeeded()) {
36 LOG_CRITICAL(Service, "Resource limit reached!"); 36 LOG_CRITICAL(Service, "Resource limit reached!");
37 return {}; 37 return {};
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index ced57dfe6..b97813fbc 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -300,11 +300,10 @@ Kernel::KEvent* nvhost_ctrl_gpu::QueryEvent(u32 event_id) {
300 return error_notifier_event; 300 return error_notifier_event;
301 case 2: 301 case 2:
302 return unknown_event; 302 return unknown_event;
303 default: { 303 default:
304 LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id); 304 LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id);
305 return nullptr;
305 } 306 }
306 }
307 return nullptr;
308} 307}
309 308
310} // namespace Service::Nvidia::Devices 309} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 45a759fa8..e123564c6 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -364,11 +364,10 @@ Kernel::KEvent* nvhost_gpu::QueryEvent(u32 event_id) {
364 return sm_exception_breakpoint_pause_report_event; 364 return sm_exception_breakpoint_pause_report_event;
365 case 3: 365 case 3:
366 return error_notifier_event; 366 return error_notifier_event;
367 default: { 367 default:
368 LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id); 368 LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id);
369 return nullptr;
369 } 370 }
370 }
371 return nullptr;
372} 371}
373 372
374} // namespace Service::Nvidia::Devices 373} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.cpp b/src/core/hle/service/nvflinger/buffer_queue_core.cpp
index ea4a14ea4..3d1338e66 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_core.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue_core.cpp
@@ -23,15 +23,17 @@ void BufferQueueCore::NotifyShutdown() {
23} 23}
24 24
25void BufferQueueCore::SignalDequeueCondition() { 25void BufferQueueCore::SignalDequeueCondition() {
26 dequeue_possible.store(true);
26 dequeue_condition.notify_all(); 27 dequeue_condition.notify_all();
27} 28}
28 29
29bool BufferQueueCore::WaitForDequeueCondition() { 30bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock<std::mutex>& lk) {
30 if (is_shutting_down) { 31 if (is_shutting_down) {
31 return false; 32 return false;
32 } 33 }
33 34
34 dequeue_condition.wait(mutex); 35 dequeue_condition.wait(lk, [&] { return dequeue_possible.load(); });
36 dequeue_possible.store(false);
35 37
36 return true; 38 return true;
37} 39}
diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.h b/src/core/hle/service/nvflinger/buffer_queue_core.h
index ca6baefaf..85b3bc4c1 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_core.h
+++ b/src/core/hle/service/nvflinger/buffer_queue_core.h
@@ -38,7 +38,7 @@ public:
38 38
39private: 39private:
40 void SignalDequeueCondition(); 40 void SignalDequeueCondition();
41 bool WaitForDequeueCondition(); 41 bool WaitForDequeueCondition(std::unique_lock<std::mutex>& lk);
42 42
43 s32 GetMinUndequeuedBufferCountLocked(bool async) const; 43 s32 GetMinUndequeuedBufferCountLocked(bool async) const;
44 s32 GetMinMaxBufferCountLocked(bool async) const; 44 s32 GetMinMaxBufferCountLocked(bool async) const;
@@ -60,7 +60,8 @@ private:
60 BufferQueueDefs::SlotsType slots{}; 60 BufferQueueDefs::SlotsType slots{};
61 std::vector<BufferItem> queue; 61 std::vector<BufferItem> queue;
62 s32 override_max_buffer_count{}; 62 s32 override_max_buffer_count{};
63 mutable std::condition_variable_any dequeue_condition; 63 std::condition_variable dequeue_condition;
64 std::atomic<bool> dequeue_possible{};
64 const bool use_async_buffer{}; // This is always disabled on HOS 65 const bool use_async_buffer{}; // This is always disabled on HOS
65 bool dequeue_buffer_cannot_block{}; 66 bool dequeue_buffer_cannot_block{};
66 PixelFormat default_buffer_format{PixelFormat::Rgba8888}; 67 PixelFormat default_buffer_format{PixelFormat::Rgba8888};
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
index 41ba44b21..e601b5da1 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
@@ -121,8 +121,8 @@ Status BufferQueueProducer::SetBufferCount(s32 buffer_count) {
121 return Status::NoError; 121 return Status::NoError;
122} 122}
123 123
124Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, 124Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags,
125 Status* return_flags) const { 125 std::unique_lock<std::mutex>& lk) const {
126 bool try_again = true; 126 bool try_again = true;
127 127
128 while (try_again) { 128 while (try_again) {
@@ -214,7 +214,7 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found,
214 return Status::WouldBlock; 214 return Status::WouldBlock;
215 } 215 }
216 216
217 if (!core->WaitForDequeueCondition()) { 217 if (!core->WaitForDequeueCondition(lk)) {
218 // We are no longer running 218 // We are no longer running
219 return Status::NoError; 219 return Status::NoError;
220 } 220 }
@@ -237,7 +237,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool
237 Status return_flags = Status::NoError; 237 Status return_flags = Status::NoError;
238 bool attached_by_consumer = false; 238 bool attached_by_consumer = false;
239 { 239 {
240 std::scoped_lock lock{core->mutex}; 240 std::unique_lock lock{core->mutex};
241 core->WaitWhileAllocatingLocked(); 241 core->WaitWhileAllocatingLocked();
242 242
243 if (format == PixelFormat::NoFormat) { 243 if (format == PixelFormat::NoFormat) {
@@ -248,7 +248,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool
248 usage |= core->consumer_usage_bit; 248 usage |= core->consumer_usage_bit;
249 249
250 s32 found{}; 250 s32 found{};
251 Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags); 251 Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags, lock);
252 if (status != Status::NoError) { 252 if (status != Status::NoError) {
253 return status; 253 return status;
254 } 254 }
@@ -400,13 +400,13 @@ Status BufferQueueProducer::AttachBuffer(s32* out_slot,
400 return Status::BadValue; 400 return Status::BadValue;
401 } 401 }
402 402
403 std::scoped_lock lock{core->mutex}; 403 std::unique_lock lock{core->mutex};
404 core->WaitWhileAllocatingLocked(); 404 core->WaitWhileAllocatingLocked();
405 405
406 Status return_flags = Status::NoError; 406 Status return_flags = Status::NoError;
407 s32 found{}; 407 s32 found{};
408 408
409 const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags); 409 const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags, lock);
410 if (status != Status::NoError) { 410 if (status != Status::NoError) {
411 return status; 411 return status;
412 } 412 }
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.h b/src/core/hle/service/nvflinger/buffer_queue_producer.h
index 7526bf8ec..1d380480f 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_producer.h
+++ b/src/core/hle/service/nvflinger/buffer_queue_producer.h
@@ -70,7 +70,8 @@ public:
70private: 70private:
71 BufferQueueProducer(const BufferQueueProducer&) = delete; 71 BufferQueueProducer(const BufferQueueProducer&) = delete;
72 72
73 Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags) const; 73 Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags,
74 std::unique_lock<std::mutex>& lk) const;
74 75
75 Kernel::KEvent* buffer_wait_event{}; 76 Kernel::KEvent* buffer_wait_event{};
76 Service::KernelHelpers::ServiceContext& service_context; 77 Service::KernelHelpers::ServiceContext& service_context;
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 5ab41c0c4..0de67f1e1 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -228,6 +228,7 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session,
228 } 228 }
229 229
230 UNIMPLEMENTED_MSG("command_type={}", ctx.GetCommandType()); 230 UNIMPLEMENTED_MSG("command_type={}", ctx.GetCommandType());
231 break;
231 } 232 }
232 233
233 // If emulation was shutdown, we are closing service threads, do not write the response back to 234 // If emulation was shutdown, we are closing service threads, do not write the response back to
diff --git a/src/core/hle/service/sm/sm_controller.cpp b/src/core/hle/service/sm/sm_controller.cpp
index 69e0fe808..1cf9dd1c4 100644
--- a/src/core/hle/service/sm/sm_controller.cpp
+++ b/src/core/hle/service/sm/sm_controller.cpp
@@ -34,8 +34,8 @@ void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) {
34 // once this is a proper process 34 // once this is a proper process
35 35
36 // Reserve a new session from the process resource limit. 36 // Reserve a new session from the process resource limit.
37 Kernel::KScopedResourceReservation session_reservation(&process, 37 Kernel::KScopedResourceReservation session_reservation(
38 Kernel::LimitableResource::Sessions); 38 &process, Kernel::LimitableResource::SessionCountMax);
39 ASSERT(session_reservation.Succeeded()); 39 ASSERT(session_reservation.Succeeded());
40 40
41 // Create the session. 41 // Create the session.
diff --git a/src/core/hle/service/time/time_zone_manager.cpp b/src/core/hle/service/time/time_zone_manager.cpp
index 2aa675df9..f9ada7c93 100644
--- a/src/core/hle/service/time/time_zone_manager.cpp
+++ b/src/core/hle/service/time/time_zone_manager.cpp
@@ -280,6 +280,7 @@ static constexpr int TransitionTime(int year, Rule rule, int offset) {
280 } 280 }
281 default: 281 default:
282 ASSERT(false); 282 ASSERT(false);
283 break;
283 } 284 }
284 return value + rule.transition_time + offset; 285 return value + rule.transition_time + offset;
285} 286}
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 3ca80c8ff..3141122f1 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -6,6 +6,7 @@
6 6
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/atomic_ops.h" 8#include "common/atomic_ops.h"
9#include "common/cache_management.h"
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "common/logging/log.h" 11#include "common/logging/log.h"
11#include "common/page_table.h" 12#include "common/page_table.h"
@@ -329,6 +330,55 @@ struct Memory::Impl {
329 }); 330 });
330 } 331 }
331 332
333 template <typename Callback>
334 Result PerformCacheOperation(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size,
335 Callback&& cb) {
336 class InvalidMemoryException : public std::exception {};
337
338 try {
339 WalkBlock(
340 process, dest_addr, size,
341 [&](const std::size_t block_size, const VAddr current_vaddr) {
342 LOG_ERROR(HW_Memory, "Unmapped cache maintenance @ {:#018X}", current_vaddr);
343 throw InvalidMemoryException();
344 },
345 [&](const std::size_t block_size, u8* const host_ptr) { cb(block_size, host_ptr); },
346 [&](const VAddr current_vaddr, const std::size_t block_size, u8* const host_ptr) {
347 system.GPU().FlushRegion(current_vaddr, block_size);
348 cb(block_size, host_ptr);
349 },
350 [](const std::size_t block_size) {});
351 } catch (InvalidMemoryException&) {
352 return Kernel::ResultInvalidCurrentMemory;
353 }
354
355 return ResultSuccess;
356 }
357
358 Result InvalidateDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size) {
359 auto perform = [&](const std::size_t block_size, u8* const host_ptr) {
360 // Do nothing; this operation (dc ivac) cannot be supported
361 // from EL0
362 };
363 return PerformCacheOperation(process, dest_addr, size, perform);
364 }
365
366 Result StoreDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size) {
367 auto perform = [&](const std::size_t block_size, u8* const host_ptr) {
368 // dc cvac: Store to point of coherency
369 Common::DataCacheLineCleanByVAToPoC(host_ptr, block_size);
370 };
371 return PerformCacheOperation(process, dest_addr, size, perform);
372 }
373
374 Result FlushDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size) {
375 auto perform = [&](const std::size_t block_size, u8* const host_ptr) {
376 // dc civac: Store to point of coherency, and invalidate from cache
377 Common::DataCacheLineCleanAndInvalidateByVAToPoC(host_ptr, block_size);
378 };
379 return PerformCacheOperation(process, dest_addr, size, perform);
380 }
381
332 void MarkRegionDebug(VAddr vaddr, u64 size, bool debug) { 382 void MarkRegionDebug(VAddr vaddr, u64 size, bool debug) {
333 if (vaddr == 0) { 383 if (vaddr == 0) {
334 return; 384 return;
@@ -786,6 +836,21 @@ void Memory::ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, const s
786 impl->ZeroBlock(process, dest_addr, size); 836 impl->ZeroBlock(process, dest_addr, size);
787} 837}
788 838
839Result Memory::InvalidateDataCache(const Kernel::KProcess& process, VAddr dest_addr,
840 const std::size_t size) {
841 return impl->InvalidateDataCache(process, dest_addr, size);
842}
843
844Result Memory::StoreDataCache(const Kernel::KProcess& process, VAddr dest_addr,
845 const std::size_t size) {
846 return impl->StoreDataCache(process, dest_addr, size);
847}
848
849Result Memory::FlushDataCache(const Kernel::KProcess& process, VAddr dest_addr,
850 const std::size_t size) {
851 return impl->FlushDataCache(process, dest_addr, size);
852}
853
789void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) { 854void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
790 impl->RasterizerMarkRegionCached(vaddr, size, cached); 855 impl->RasterizerMarkRegionCached(vaddr, size, cached);
791} 856}
diff --git a/src/core/memory.h b/src/core/memory.h
index 81eac448b..31fe699d8 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -7,6 +7,7 @@
7#include <memory> 7#include <memory>
8#include <string> 8#include <string>
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/hle/result.h"
10 11
11namespace Common { 12namespace Common {
12struct PageTable; 13struct PageTable;
@@ -450,6 +451,39 @@ public:
450 void ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size); 451 void ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size);
451 452
452 /** 453 /**
454 * Invalidates a range of bytes within the current process' address space at the specified
455 * virtual address.
456 *
457 * @param process The process that will have data invalidated within its address space.
458 * @param dest_addr The destination virtual address to invalidate the data from.
459 * @param size The size of the range to invalidate, in bytes.
460 *
461 */
462 Result InvalidateDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size);
463
464 /**
465 * Stores a range of bytes within the current process' address space at the specified
466 * virtual address.
467 *
468 * @param process The process that will have data stored within its address space.
469 * @param dest_addr The destination virtual address to store the data from.
470 * @param size The size of the range to store, in bytes.
471 *
472 */
473 Result StoreDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size);
474
475 /**
476 * Flushes a range of bytes within the current process' address space at the specified
477 * virtual address.
478 *
479 * @param process The process that will have data flushed within its address space.
480 * @param dest_addr The destination virtual address to flush the data from.
481 * @param size The size of the range to flush, in bytes.
482 *
483 */
484 Result FlushDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size);
485
486 /**
453 * Marks each page within the specified address range as cached or uncached. 487 * Marks each page within the specified address range as cached or uncached.
454 * 488 *
455 * @param vaddr The virtual address indicating the start of the address range. 489 * @param vaddr The virtual address indicating the start of the address range.
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 9dbf9888d..f0bd84ab2 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
@@ -382,6 +382,18 @@ void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) {
382 ctx.Add("MOV.S {}.x,primitive_invocation.x;", inst); 382 ctx.Add("MOV.S {}.x,primitive_invocation.x;", inst);
383} 383}
384 384
385void EmitInvocationInfo(EmitContext& ctx, IR::Inst& inst) {
386 switch (ctx.stage) {
387 case Stage::TessellationControl:
388 case Stage::TessellationEval:
389 ctx.Add("SHL.U {}.x,primitive.vertexcount,16;", inst);
390 break;
391 default:
392 LOG_WARNING(Shader, "(STUBBED) called");
393 ctx.Add("MOV.S {}.x,0x00ff0000;", inst);
394 }
395}
396
385void EmitSampleId(EmitContext& ctx, IR::Inst& inst) { 397void EmitSampleId(EmitContext& ctx, IR::Inst& inst) {
386 ctx.Add("MOV.S {}.x,fragment.sampleid.x;", inst); 398 ctx.Add("MOV.S {}.x,fragment.sampleid.x;", inst);
387} 399}
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 02b84cbe6..39579cf5d 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
@@ -402,6 +402,18 @@ void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) {
402 ctx.AddU32("{}=uint(gl_InvocationID);", inst); 402 ctx.AddU32("{}=uint(gl_InvocationID);", inst);
403} 403}
404 404
405void EmitInvocationInfo(EmitContext& ctx, IR::Inst& inst) {
406 switch (ctx.stage) {
407 case Stage::TessellationControl:
408 case Stage::TessellationEval:
409 ctx.AddU32("{}=uint(gl_PatchVerticesIn)<<16;", inst);
410 break;
411 default:
412 LOG_WARNING(Shader, "(STUBBED) called");
413 ctx.AddU32("{}=uint(0x00ff0000);", inst);
414 }
415}
416
405void EmitSampleId(EmitContext& ctx, IR::Inst& inst) { 417void EmitSampleId(EmitContext& ctx, IR::Inst& inst) {
406 ctx.AddU32("{}=uint(gl_SampleID);", inst); 418 ctx.AddU32("{}=uint(gl_SampleID);", inst);
407} 419}
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 ce33fabf5..01f6ec9b5 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
@@ -514,6 +514,18 @@ Id EmitInvocationId(EmitContext& ctx) {
514 return ctx.OpLoad(ctx.U32[1], ctx.invocation_id); 514 return ctx.OpLoad(ctx.U32[1], ctx.invocation_id);
515} 515}
516 516
517Id EmitInvocationInfo(EmitContext& ctx) {
518 switch (ctx.stage) {
519 case Stage::TessellationControl:
520 case Stage::TessellationEval:
521 return ctx.OpShiftLeftLogical(ctx.U32[1], ctx.OpLoad(ctx.U32[1], ctx.patch_vertices_in),
522 ctx.Const(16u));
523 default:
524 LOG_WARNING(Shader, "(STUBBED) called");
525 return ctx.Const(0x00ff0000u);
526 }
527}
528
517Id EmitSampleId(EmitContext& ctx) { 529Id EmitSampleId(EmitContext& ctx) {
518 return ctx.OpLoad(ctx.U32[1], ctx.sample_id); 530 return ctx.OpLoad(ctx.U32[1], ctx.sample_id);
519} 531}
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 e80662d23..dd96b7803 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.cpp b/src/video_core/engines/maxwell_3d.cpp
index 4a2f2c1fd..d502d181c 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -249,6 +249,11 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
249 return; 249 return;
250 case MAXWELL3D_REG_INDEX(fragment_barrier): 250 case MAXWELL3D_REG_INDEX(fragment_barrier):
251 return rasterizer->FragmentBarrier(); 251 return rasterizer->FragmentBarrier();
252 case MAXWELL3D_REG_INDEX(invalidate_texture_data_cache):
253 rasterizer->InvalidateGPUCache();
254 return rasterizer->WaitForIdle();
255 case MAXWELL3D_REG_INDEX(tiled_cache_barrier):
256 return rasterizer->TiledCacheBarrier();
252 } 257 }
253} 258}
254 259
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index a948fcb14..34b085388 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -707,7 +707,7 @@ public:
707 case Size::Size_A2_B10_G10_R10: 707 case Size::Size_A2_B10_G10_R10:
708 return "2_10_10_10"; 708 return "2_10_10_10";
709 case Size::Size_B10_G11_R11: 709 case Size::Size_B10_G11_R11:
710 return "10_11_12"; 710 return "10_11_11";
711 default: 711 default:
712 ASSERT(false); 712 ASSERT(false);
713 return {}; 713 return {};
@@ -2639,7 +2639,7 @@ public:
2639 L2CacheControl l2_cache_control; ///< 0x0218 2639 L2CacheControl l2_cache_control; ///< 0x0218
2640 InvalidateShaderCache invalidate_shader_cache; ///< 0x021C 2640 InvalidateShaderCache invalidate_shader_cache; ///< 0x021C
2641 INSERT_PADDING_BYTES_NOINIT(0xA8); 2641 INSERT_PADDING_BYTES_NOINIT(0xA8);
2642 SyncInfo sync_info; ///< 0x02C8 2642 SyncInfo sync_info; ///< 0x02C8
2643 INSERT_PADDING_BYTES_NOINIT(0x4); 2643 INSERT_PADDING_BYTES_NOINIT(0x4);
2644 u32 prim_circular_buffer_throttle; ///< 0x02D0 2644 u32 prim_circular_buffer_throttle; ///< 0x02D0
2645 u32 flush_invalidate_rop_mini_cache; ///< 0x02D4 2645 u32 flush_invalidate_rop_mini_cache; ///< 0x02D4
@@ -2731,7 +2731,11 @@ public:
2731 s32 stencil_back_ref; ///< 0x0F54 2731 s32 stencil_back_ref; ///< 0x0F54
2732 u32 stencil_back_mask; ///< 0x0F58 2732 u32 stencil_back_mask; ///< 0x0F58
2733 u32 stencil_back_func_mask; ///< 0x0F5C 2733 u32 stencil_back_func_mask; ///< 0x0F5C
2734 INSERT_PADDING_BYTES_NOINIT(0x24); 2734 INSERT_PADDING_BYTES_NOINIT(0x14);
2735 u32 invalidate_texture_data_cache; ///< 0x0F74 Assumed - Not in official docs.
2736 INSERT_PADDING_BYTES_NOINIT(0x4);
2737 u32 tiled_cache_barrier; ///< 0x0F7C Assumed - Not in official docs.
2738 INSERT_PADDING_BYTES_NOINIT(0x4);
2735 VertexStreamSubstitute vertex_stream_substitute; ///< 0x0F84 2739 VertexStreamSubstitute vertex_stream_substitute; ///< 0x0F84
2736 u32 line_mode_clip_generated_edge_do_not_draw; ///< 0x0F8C 2740 u32 line_mode_clip_generated_edge_do_not_draw; ///< 0x0F8C
2737 u32 color_mask_common; ///< 0x0F90 2741 u32 color_mask_common; ///< 0x0F90
@@ -2791,7 +2795,8 @@ public:
2791 FillViaTriangleMode fill_via_triangle_mode; ///< 0x113C 2795 FillViaTriangleMode fill_via_triangle_mode; ///< 0x113C
2792 u32 blend_per_format_snorm8_unorm16_snorm16_enabled; ///< 0x1140 2796 u32 blend_per_format_snorm8_unorm16_snorm16_enabled; ///< 0x1140
2793 u32 flush_pending_writes_sm_gloal_store; ///< 0x1144 2797 u32 flush_pending_writes_sm_gloal_store; ///< 0x1144
2794 INSERT_PADDING_BYTES_NOINIT(0x18); 2798 u32 conservative_raster_enable; ///< 0x1148 Assumed - Not in official docs.
2799 INSERT_PADDING_BYTES_NOINIT(0x14);
2795 std::array<VertexAttribute, NumVertexAttributes> vertex_attrib_format; ///< 0x1160 2800 std::array<VertexAttribute, NumVertexAttributes> vertex_attrib_format; ///< 0x1160
2796 std::array<MsaaSampleLocation, 4> multisample_sample_locations; ///< 0x11E0 2801 std::array<MsaaSampleLocation, 4> multisample_sample_locations; ///< 0x11E0
2797 u32 offset_render_target_index_by_viewport_index; ///< 0x11F0 2802 u32 offset_render_target_index_by_viewport_index; ///< 0x11F0
@@ -2970,7 +2975,7 @@ public:
2970 CullFace gl_cull_face; ///< 0x1920 2975 CullFace gl_cull_face; ///< 0x1920
2971 Viewport::PixelCenter viewport_pixel_center; ///< 0x1924 2976 Viewport::PixelCenter viewport_pixel_center; ///< 0x1924
2972 INSERT_PADDING_BYTES_NOINIT(0x4); 2977 INSERT_PADDING_BYTES_NOINIT(0x4);
2973 u32 viewport_scale_offset_enbled; ///< 0x192C 2978 u32 viewport_scale_offset_enabled; ///< 0x192C
2974 INSERT_PADDING_BYTES_NOINIT(0xC); 2979 INSERT_PADDING_BYTES_NOINIT(0xC);
2975 ViewportClipControl viewport_clip_control; ///< 0x193C 2980 ViewportClipControl viewport_clip_control; ///< 0x193C
2976 UserClip::Op user_clip_op; ///< 0x1940 2981 UserClip::Op user_clip_op; ///< 0x1940
@@ -3287,6 +3292,8 @@ ASSERT_REG_POSITION(const_color_rendering, 0x0F40);
3287ASSERT_REG_POSITION(stencil_back_ref, 0x0F54); 3292ASSERT_REG_POSITION(stencil_back_ref, 0x0F54);
3288ASSERT_REG_POSITION(stencil_back_mask, 0x0F58); 3293ASSERT_REG_POSITION(stencil_back_mask, 0x0F58);
3289ASSERT_REG_POSITION(stencil_back_func_mask, 0x0F5C); 3294ASSERT_REG_POSITION(stencil_back_func_mask, 0x0F5C);
3295ASSERT_REG_POSITION(invalidate_texture_data_cache, 0x0F74);
3296ASSERT_REG_POSITION(tiled_cache_barrier, 0x0F7C);
3290ASSERT_REG_POSITION(vertex_stream_substitute, 0x0F84); 3297ASSERT_REG_POSITION(vertex_stream_substitute, 0x0F84);
3291ASSERT_REG_POSITION(line_mode_clip_generated_edge_do_not_draw, 0x0F8C); 3298ASSERT_REG_POSITION(line_mode_clip_generated_edge_do_not_draw, 0x0F8C);
3292ASSERT_REG_POSITION(color_mask_common, 0x0F90); 3299ASSERT_REG_POSITION(color_mask_common, 0x0F90);
@@ -3343,6 +3350,7 @@ ASSERT_REG_POSITION(post_ps_use_pre_ps_coverage, 0x1138);
3343ASSERT_REG_POSITION(fill_via_triangle_mode, 0x113C); 3350ASSERT_REG_POSITION(fill_via_triangle_mode, 0x113C);
3344ASSERT_REG_POSITION(blend_per_format_snorm8_unorm16_snorm16_enabled, 0x1140); 3351ASSERT_REG_POSITION(blend_per_format_snorm8_unorm16_snorm16_enabled, 0x1140);
3345ASSERT_REG_POSITION(flush_pending_writes_sm_gloal_store, 0x1144); 3352ASSERT_REG_POSITION(flush_pending_writes_sm_gloal_store, 0x1144);
3353ASSERT_REG_POSITION(conservative_raster_enable, 0x1148);
3346ASSERT_REG_POSITION(vertex_attrib_format, 0x1160); 3354ASSERT_REG_POSITION(vertex_attrib_format, 0x1160);
3347ASSERT_REG_POSITION(multisample_sample_locations, 0x11E0); 3355ASSERT_REG_POSITION(multisample_sample_locations, 0x11E0);
3348ASSERT_REG_POSITION(offset_render_target_index_by_viewport_index, 0x11F0); 3356ASSERT_REG_POSITION(offset_render_target_index_by_viewport_index, 0x11F0);
@@ -3482,7 +3490,7 @@ ASSERT_REG_POSITION(gl_cull_test_enabled, 0x1918);
3482ASSERT_REG_POSITION(gl_front_face, 0x191C); 3490ASSERT_REG_POSITION(gl_front_face, 0x191C);
3483ASSERT_REG_POSITION(gl_cull_face, 0x1920); 3491ASSERT_REG_POSITION(gl_cull_face, 0x1920);
3484ASSERT_REG_POSITION(viewport_pixel_center, 0x1924); 3492ASSERT_REG_POSITION(viewport_pixel_center, 0x1924);
3485ASSERT_REG_POSITION(viewport_scale_offset_enbled, 0x192C); 3493ASSERT_REG_POSITION(viewport_scale_offset_enabled, 0x192C);
3486ASSERT_REG_POSITION(viewport_clip_control, 0x193C); 3494ASSERT_REG_POSITION(viewport_clip_control, 0x193C);
3487ASSERT_REG_POSITION(user_clip_op, 0x1940); 3495ASSERT_REG_POSITION(user_clip_op, 0x1940);
3488ASSERT_REG_POSITION(render_enable_override, 0x1944); 3496ASSERT_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..1bf6ca2dd 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);
@@ -311,6 +314,7 @@ void MaxwellDMA::ReleaseSemaphore() {
311 } 314 }
312 default: 315 default:
313 ASSERT_MSG(false, "Unknown semaphore type: {}", static_cast<u32>(type.Value())); 316 ASSERT_MSG(false, "Unknown semaphore type: {}", static_cast<u32>(type.Value()));
317 break;
314 } 318 }
315} 319}
316 320
diff --git a/src/video_core/engines/puller.cpp b/src/video_core/engines/puller.cpp
index 3977bb0fb..4d2278811 100644
--- a/src/video_core/engines/puller.cpp
+++ b/src/video_core/engines/puller.cpp
@@ -50,6 +50,7 @@ void Puller::ProcessBindMethod(const MethodCall& method_call) {
50 break; 50 break;
51 default: 51 default:
52 UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id); 52 UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id);
53 break;
53 } 54 }
54} 55}
55 56
@@ -65,6 +66,7 @@ void Puller::ProcessFenceActionMethod() {
65 break; 66 break;
66 default: 67 default:
67 UNIMPLEMENTED_MSG("Unimplemented operation {}", regs.fence_action.op.Value()); 68 UNIMPLEMENTED_MSG("Unimplemented operation {}", regs.fence_action.op.Value());
69 break;
68 } 70 }
69} 71}
70 72
@@ -228,6 +230,7 @@ void Puller::CallEngineMethod(const MethodCall& method_call) {
228 break; 230 break;
229 default: 231 default:
230 UNIMPLEMENTED_MSG("Unimplemented engine"); 232 UNIMPLEMENTED_MSG("Unimplemented engine");
233 break;
231 } 234 }
232} 235}
233 236
@@ -254,6 +257,7 @@ void Puller::CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_s
254 break; 257 break;
255 default: 258 default:
256 UNIMPLEMENTED_MSG("Unimplemented engine"); 259 UNIMPLEMENTED_MSG("Unimplemented engine");
260 break;
257 } 261 }
258} 262}
259 263
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/macro/macro_interpreter.cpp b/src/video_core/macro/macro_interpreter.cpp
index c0d32c112..0d63495a9 100644
--- a/src/video_core/macro/macro_interpreter.cpp
+++ b/src/video_core/macro/macro_interpreter.cpp
@@ -201,6 +201,7 @@ bool MacroInterpreterImpl::Step(bool is_delay_slot) {
201 } 201 }
202 default: 202 default:
203 UNIMPLEMENTED_MSG("Unimplemented macro operation {}", opcode.operation.Value()); 203 UNIMPLEMENTED_MSG("Unimplemented macro operation {}", opcode.operation.Value());
204 break;
204 } 205 }
205 206
206 // An instruction with the Exit flag will not actually 207 // An instruction with the Exit flag will not actually
@@ -297,6 +298,7 @@ void MacroInterpreterImpl::ProcessResult(Macro::ResultOperation operation, u32 r
297 break; 298 break;
298 default: 299 default:
299 UNIMPLEMENTED_MSG("Unimplemented result operation {}", operation); 300 UNIMPLEMENTED_MSG("Unimplemented result operation {}", operation);
301 break;
300 } 302 }
301} 303}
302 304
diff --git a/src/video_core/macro/macro_jit_x64.cpp b/src/video_core/macro/macro_jit_x64.cpp
index 25c1ce798..7347cbd88 100644
--- a/src/video_core/macro/macro_jit_x64.cpp
+++ b/src/video_core/macro/macro_jit_x64.cpp
@@ -652,6 +652,7 @@ void MacroJITx64Impl::Compile_ProcessResult(Macro::ResultOperation operation, u3
652 break; 652 break;
653 default: 653 default:
654 UNIMPLEMENTED_MSG("Unimplemented macro operation {}", operation); 654 UNIMPLEMENTED_MSG("Unimplemented macro operation {}", operation);
655 break;
655 } 656 }
656} 657}
657 658
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 72e314d39..d05a5f60b 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);
@@ -770,7 +770,7 @@ void RasterizerOpenGL::SyncStencilTestState() {
770 770
771 if (regs.stencil_two_side_enable) { 771 if (regs.stencil_two_side_enable) {
772 glStencilFuncSeparate(GL_BACK, MaxwellToGL::ComparisonOp(regs.stencil_back_op.func), 772 glStencilFuncSeparate(GL_BACK, MaxwellToGL::ComparisonOp(regs.stencil_back_op.func),
773 regs.stencil_back_ref, regs.stencil_back_mask); 773 regs.stencil_back_ref, regs.stencil_back_func_mask);
774 glStencilOpSeparate(GL_BACK, MaxwellToGL::StencilOp(regs.stencil_back_op.fail), 774 glStencilOpSeparate(GL_BACK, MaxwellToGL::StencilOp(regs.stencil_back_op.fail),
775 MaxwellToGL::StencilOp(regs.stencil_back_op.zfail), 775 MaxwellToGL::StencilOp(regs.stencil_back_op.zfail),
776 MaxwellToGL::StencilOp(regs.stencil_back_op.zpass)); 776 MaxwellToGL::StencilOp(regs.stencil_back_op.zpass));
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_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 99cd11d1e..9f7ce7414 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -891,6 +891,7 @@ void Image::CopyBufferToImage(const VideoCommon::BufferImageCopy& copy, size_t b
891 break; 891 break;
892 default: 892 default:
893 ASSERT(false); 893 ASSERT(false);
894 break;
894 } 895 }
895} 896}
896 897
@@ -927,6 +928,7 @@ void Image::CopyImageToBuffer(const VideoCommon::BufferImageCopy& copy, size_t b
927 break; 928 break;
928 default: 929 default:
929 ASSERT(false); 930 ASSERT(false);
931 break;
930 } 932 }
931 // Compressed formats don't have a pixel format or type 933 // Compressed formats don't have a pixel format or type
932 const bool is_compressed = gl_format == GL_NONE; 934 const bool is_compressed = gl_format == GL_NONE;
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 8bd5eba7e..f29462f7c 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -340,6 +340,7 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
340 texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; 340 texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
341 // UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", 341 // UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}",
342 // static_cast<u32>(framebuffer.pixel_format)); 342 // static_cast<u32>(framebuffer.pixel_format));
343 break;
343 } 344 }
344 345
345 texture.resource.Release(); 346 texture.resource.Release();
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
index f85ed8e5b..98cc26679 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
@@ -90,6 +90,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
90 depth_format.Assign(static_cast<u32>(regs.zeta.format)); 90 depth_format.Assign(static_cast<u32>(regs.zeta.format));
91 y_negate.Assign(regs.window_origin.mode != Maxwell::WindowOrigin::Mode::UpperLeft ? 1 : 0); 91 y_negate.Assign(regs.window_origin.mode != Maxwell::WindowOrigin::Mode::UpperLeft ? 1 : 0);
92 provoking_vertex_last.Assign(regs.provoking_vertex == Maxwell::ProvokingVertex::Last ? 1 : 0); 92 provoking_vertex_last.Assign(regs.provoking_vertex == Maxwell::ProvokingVertex::Last ? 1 : 0);
93 conservative_raster_enable.Assign(regs.conservative_raster_enable != 0 ? 1 : 0);
93 smooth_lines.Assign(regs.line_anti_alias_enable != 0 ? 1 : 0); 94 smooth_lines.Assign(regs.line_anti_alias_enable != 0 ? 1 : 0);
94 95
95 for (size_t i = 0; i < regs.rt.size(); ++i) { 96 for (size_t i = 0; i < regs.rt.size(); ++i) {
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
index 43441209c..1afdef329 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
@@ -193,6 +193,7 @@ struct FixedPipelineState {
193 BitField<6, 5, u32> depth_format; 193 BitField<6, 5, u32> depth_format;
194 BitField<11, 1, u32> y_negate; 194 BitField<11, 1, u32> y_negate;
195 BitField<12, 1, u32> provoking_vertex_last; 195 BitField<12, 1, u32> provoking_vertex_last;
196 BitField<13, 1, u32> conservative_raster_enable;
196 BitField<14, 1, u32> smooth_lines; 197 BitField<14, 1, u32> smooth_lines;
197 }; 198 };
198 std::array<u8, Maxwell::NumRenderTargets> color_formats; 199 std::array<u8, Maxwell::NumRenderTargets> color_formats;
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 1aa116cea..ef75c126c 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -680,6 +680,15 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
680 .lineStippleFactor = 0, 680 .lineStippleFactor = 0,
681 .lineStipplePattern = 0, 681 .lineStipplePattern = 0,
682 }; 682 };
683 VkPipelineRasterizationConservativeStateCreateInfoEXT conservative_raster{
684 .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_CONSERVATIVE_STATE_CREATE_INFO_EXT,
685 .pNext = nullptr,
686 .flags = 0,
687 .conservativeRasterizationMode = key.state.conservative_raster_enable != 0
688 ? VK_CONSERVATIVE_RASTERIZATION_MODE_OVERESTIMATE_EXT
689 : VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT,
690 .extraPrimitiveOverestimationSize = 0.0f,
691 };
683 VkPipelineRasterizationProvokingVertexStateCreateInfoEXT provoking_vertex{ 692 VkPipelineRasterizationProvokingVertexStateCreateInfoEXT provoking_vertex{
684 .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT, 693 .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT,
685 .pNext = nullptr, 694 .pNext = nullptr,
@@ -690,6 +699,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
690 if (IsLine(input_assembly_topology) && device.IsExtLineRasterizationSupported()) { 699 if (IsLine(input_assembly_topology) && device.IsExtLineRasterizationSupported()) {
691 line_state.pNext = std::exchange(rasterization_ci.pNext, &line_state); 700 line_state.pNext = std::exchange(rasterization_ci.pNext, &line_state);
692 } 701 }
702 if (device.IsExtConservativeRasterizationSupported()) {
703 conservative_raster.pNext = std::exchange(rasterization_ci.pNext, &conservative_raster);
704 }
693 if (device.IsExtProvokingVertexSupported()) { 705 if (device.IsExtProvokingVertexSupported()) {
694 provoking_vertex.pNext = std::exchange(rasterization_ci.pNext, &provoking_vertex); 706 provoking_vertex.pNext = std::exchange(rasterization_ci.pNext, &provoking_vertex);
695 } 707 }
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_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index 7934f2a51..4a7b633b7 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -221,6 +221,7 @@ void Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_s
221 [[fallthrough]]; 221 [[fallthrough]];
222 default: 222 default:
223 vk::Check(result); 223 vk::Check(result);
224 break;
224 } 225 }
225 }); 226 });
226 chunk->MarkSubmit(); 227 chunk->MarkSubmit();
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/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 853b80d8a..a65bbeb1c 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -108,6 +108,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
108 break; 108 break;
109 default: 109 default:
110 ASSERT_MSG(false, "Invalid surface type"); 110 ASSERT_MSG(false, "Invalid surface type");
111 break;
111 } 112 }
112 } 113 }
113 if (info.storage) { 114 if (info.storage) {
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/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index fd1a4b987..59120cd09 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -170,6 +170,7 @@ void Swizzle(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixe
170#undef BPP_CASE 170#undef BPP_CASE
171 default: 171 default:
172 ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel); 172 ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel);
173 break;
173 } 174 }
174} 175}
175 176
@@ -217,6 +218,7 @@ void SwizzleSubrect(std::span<u8> output, std::span<const u8> input, u32 bytes_p
217#undef BPP_CASE 218#undef BPP_CASE
218 default: 219 default:
219 ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel); 220 ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel);
221 break;
220 } 222 }
221} 223}
222 224
@@ -240,6 +242,7 @@ void UnswizzleSubrect(std::span<u8> output, std::span<const u8> input, u32 bytes
240#undef BPP_CASE 242#undef BPP_CASE
241 default: 243 default:
242 ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel); 244 ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel);
245 break;
243 } 246 }
244} 247}
245 248
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..05f49c0d2 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"));
@@ -63,9 +126,70 @@ void CompatDB::Submit() {
63 break; 126 break;
64 default: 127 default:
65 LOG_ERROR(Frontend, "Unexpected page: {}", currentId()); 128 LOG_ERROR(Frontend, "Unexpected page: {}", currentId());
129 break;
130 }
131}
132
133int CompatDB::nextId() const {
134 switch ((static_cast<CompatDBPage>(currentId()))) {
135 case CompatDBPage::Intro:
136 return static_cast<int>(CompatDBPage::GameBoot);
137 case CompatDBPage::GameBoot:
138 if (ui->radioButton_GameBoot_No->isChecked()) {
139 return static_cast<int>(CompatDBPage::Final);
140 }
141 return static_cast<int>(CompatDBPage::GamePlay);
142 case CompatDBPage::GamePlay:
143 if (ui->radioButton_Gameplay_No->isChecked()) {
144 return static_cast<int>(CompatDBPage::Final);
145 }
146 return static_cast<int>(CompatDBPage::Freeze);
147 case CompatDBPage::Freeze:
148 if (ui->radioButton_NoFreeze_No->isChecked()) {
149 return static_cast<int>(CompatDBPage::Final);
150 }
151 return static_cast<int>(CompatDBPage::Completion);
152 case CompatDBPage::Completion:
153 if (ui->radioButton_Complete_No->isChecked()) {
154 return static_cast<int>(CompatDBPage::Final);
155 }
156 return static_cast<int>(CompatDBPage::Graphical);
157 case CompatDBPage::Graphical:
158 return static_cast<int>(CompatDBPage::Audio);
159 case CompatDBPage::Audio:
160 return static_cast<int>(CompatDBPage::Final);
161 case CompatDBPage::Final:
162 return -1;
163 default:
164 LOG_ERROR(Frontend, "Unexpected page: {}", currentId());
165 return static_cast<int>(CompatDBPage::Intro);
66 } 166 }
67} 167}
68 168
169CompatibilityStatus CompatDB::CalculateCompatibility() const {
170 if (ui->radioButton_GameBoot_No->isChecked()) {
171 return CompatibilityStatus::WontBoot;
172 }
173
174 if (ui->radioButton_Gameplay_No->isChecked()) {
175 return CompatibilityStatus::IntroMenu;
176 }
177
178 if (ui->radioButton_NoFreeze_No->isChecked() || ui->radioButton_Complete_No->isChecked()) {
179 return CompatibilityStatus::Ingame;
180 }
181
182 if (ui->radioButton_Graphical_Major->isChecked() || ui->radioButton_Audio_Major->isChecked()) {
183 return CompatibilityStatus::Ingame;
184 }
185
186 if (ui->radioButton_Graphical_Minor->isChecked() || ui->radioButton_Audio_Minor->isChecked()) {
187 return CompatibilityStatus::Playable;
188 }
189
190 return CompatibilityStatus::Perfect;
191}
192
69void CompatDB::OnTestcaseSubmitted() { 193void CompatDB::OnTestcaseSubmitted() {
70 if (!testcase_watcher.result()) { 194 if (!testcase_watcher.result()) {
71 QMessageBox::critical(this, tr("Communication error"), 195 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/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp
index 5c0217ba8..a47089988 100644
--- a/src/yuzu/configuration/configure_profile_manager.cpp
+++ b/src/yuzu/configuration/configure_profile_manager.cpp
@@ -2,6 +2,9 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <algorithm> 4#include <algorithm>
5#include <functional>
6#include <QDialog>
7#include <QDialogButtonBox>
5#include <QFileDialog> 8#include <QFileDialog>
6#include <QGraphicsItem> 9#include <QGraphicsItem>
7#include <QHeaderView> 10#include <QHeaderView>
@@ -108,9 +111,12 @@ ConfigureProfileManager::ConfigureProfileManager(const Core::System& system_, QW
108 111
109 connect(ui->pm_add, &QPushButton::clicked, this, &ConfigureProfileManager::AddUser); 112 connect(ui->pm_add, &QPushButton::clicked, this, &ConfigureProfileManager::AddUser);
110 connect(ui->pm_rename, &QPushButton::clicked, this, &ConfigureProfileManager::RenameUser); 113 connect(ui->pm_rename, &QPushButton::clicked, this, &ConfigureProfileManager::RenameUser);
111 connect(ui->pm_remove, &QPushButton::clicked, this, &ConfigureProfileManager::DeleteUser); 114 connect(ui->pm_remove, &QPushButton::clicked, this,
115 &ConfigureProfileManager::ConfirmDeleteUser);
112 connect(ui->pm_set_image, &QPushButton::clicked, this, &ConfigureProfileManager::SetUserImage); 116 connect(ui->pm_set_image, &QPushButton::clicked, this, &ConfigureProfileManager::SetUserImage);
113 117
118 confirm_dialog = new ConfigureProfileManagerDeleteDialog(this);
119
114 scene = new QGraphicsScene; 120 scene = new QGraphicsScene;
115 ui->current_user_icon->setScene(scene); 121 ui->current_user_icon->setScene(scene);
116 122
@@ -230,26 +236,23 @@ void ConfigureProfileManager::RenameUser() {
230 UpdateCurrentUser(); 236 UpdateCurrentUser();
231} 237}
232 238
233void ConfigureProfileManager::DeleteUser() { 239void ConfigureProfileManager::ConfirmDeleteUser() {
234 const auto index = tree_view->currentIndex().row(); 240 const auto index = tree_view->currentIndex().row();
235 const auto uuid = profile_manager->GetUser(index); 241 const auto uuid = profile_manager->GetUser(index);
236 ASSERT(uuid); 242 ASSERT(uuid);
237 const auto username = GetAccountUsername(*profile_manager, *uuid); 243 const auto username = GetAccountUsername(*profile_manager, *uuid);
238 244
239 const auto confirm = QMessageBox::question( 245 confirm_dialog->SetInfo(username, *uuid, [this, uuid]() { DeleteUser(*uuid); });
240 this, tr("Confirm Delete"), 246 confirm_dialog->show();
241 tr("You are about to delete user with name \"%1\". Are you sure?").arg(username)); 247}
242
243 if (confirm == QMessageBox::No) {
244 return;
245 }
246 248
249void ConfigureProfileManager::DeleteUser(const Common::UUID& uuid) {
247 if (Settings::values.current_user.GetValue() == tree_view->currentIndex().row()) { 250 if (Settings::values.current_user.GetValue() == tree_view->currentIndex().row()) {
248 Settings::values.current_user = 0; 251 Settings::values.current_user = 0;
249 } 252 }
250 UpdateCurrentUser(); 253 UpdateCurrentUser();
251 254
252 if (!profile_manager->RemoveUser(*uuid)) { 255 if (!profile_manager->RemoveUser(uuid)) {
253 return; 256 return;
254 } 257 }
255 258
@@ -319,3 +322,47 @@ void ConfigureProfileManager::SetUserImage() {
319 new QStandardItem{GetIcon(*uuid), FormatUserEntryText(username, *uuid)}); 322 new QStandardItem{GetIcon(*uuid), FormatUserEntryText(username, *uuid)});
320 UpdateCurrentUser(); 323 UpdateCurrentUser();
321} 324}
325
326ConfigureProfileManagerDeleteDialog::ConfigureProfileManagerDeleteDialog(QWidget* parent)
327 : QDialog{parent} {
328 auto dialog_vbox_layout = new QVBoxLayout(this);
329 dialog_button_box =
330 new QDialogButtonBox(QDialogButtonBox::Yes | QDialogButtonBox::No, Qt::Horizontal, parent);
331 auto label_message =
332 new QLabel(tr("Delete this user? All of the user's save data will be deleted."), this);
333 label_info = new QLabel(this);
334 auto dialog_hbox_layout_widget = new QWidget(this);
335 auto dialog_hbox_layout = new QHBoxLayout(dialog_hbox_layout_widget);
336 icon_scene = new QGraphicsScene(0, 0, 64, 64, this);
337 auto icon_view = new QGraphicsView(icon_scene, this);
338
339 dialog_hbox_layout_widget->setLayout(dialog_hbox_layout);
340 icon_view->setMaximumSize(64, 64);
341 icon_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
342 icon_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
343 this->setLayout(dialog_vbox_layout);
344 this->setWindowTitle(tr("Confirm Delete"));
345 this->setSizeGripEnabled(false);
346 dialog_vbox_layout->addWidget(label_message);
347 dialog_vbox_layout->addWidget(dialog_hbox_layout_widget);
348 dialog_vbox_layout->addWidget(dialog_button_box);
349 dialog_hbox_layout->addWidget(icon_view);
350 dialog_hbox_layout->addWidget(label_info);
351
352 connect(dialog_button_box, &QDialogButtonBox::rejected, this, [this]() { close(); });
353}
354
355ConfigureProfileManagerDeleteDialog::~ConfigureProfileManagerDeleteDialog() = default;
356
357void ConfigureProfileManagerDeleteDialog::SetInfo(const QString& username, const Common::UUID& uuid,
358 std::function<void()> accept_callback) {
359 label_info->setText(
360 tr("Name: %1\nUUID: %2").arg(username, QString::fromStdString(uuid.FormattedString())));
361 icon_scene->clear();
362 icon_scene->addPixmap(GetIcon(uuid));
363
364 connect(dialog_button_box, &QDialogButtonBox::accepted, this, [this, accept_callback]() {
365 close();
366 accept_callback();
367 });
368}
diff --git a/src/yuzu/configuration/configure_profile_manager.h b/src/yuzu/configuration/configure_profile_manager.h
index fe9033779..c4b1a334e 100644
--- a/src/yuzu/configuration/configure_profile_manager.h
+++ b/src/yuzu/configuration/configure_profile_manager.h
@@ -3,16 +3,24 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <functional>
6#include <memory> 7#include <memory>
7 8
9#include <QDialog>
8#include <QList> 10#include <QList>
9#include <QWidget> 11#include <QWidget>
10 12
13namespace Common {
14struct UUID;
15}
16
11namespace Core { 17namespace Core {
12class System; 18class System;
13} 19}
14 20
15class QGraphicsScene; 21class QGraphicsScene;
22class QDialogButtonBox;
23class QLabel;
16class QStandardItem; 24class QStandardItem;
17class QStandardItemModel; 25class QStandardItemModel;
18class QTreeView; 26class QTreeView;
@@ -26,6 +34,20 @@ namespace Ui {
26class ConfigureProfileManager; 34class ConfigureProfileManager;
27} 35}
28 36
37class ConfigureProfileManagerDeleteDialog : public QDialog {
38public:
39 explicit ConfigureProfileManagerDeleteDialog(QWidget* parent);
40 ~ConfigureProfileManagerDeleteDialog();
41
42 void SetInfo(const QString& username, const Common::UUID& uuid,
43 std::function<void()> accept_callback);
44
45private:
46 QDialogButtonBox* dialog_button_box;
47 QGraphicsScene* icon_scene;
48 QLabel* label_info;
49};
50
29class ConfigureProfileManager : public QWidget { 51class ConfigureProfileManager : public QWidget {
30 Q_OBJECT 52 Q_OBJECT
31 53
@@ -47,7 +69,8 @@ private:
47 void SelectUser(const QModelIndex& index); 69 void SelectUser(const QModelIndex& index);
48 void AddUser(); 70 void AddUser();
49 void RenameUser(); 71 void RenameUser();
50 void DeleteUser(); 72 void ConfirmDeleteUser();
73 void DeleteUser(const Common::UUID& uuid);
51 void SetUserImage(); 74 void SetUserImage();
52 75
53 QVBoxLayout* layout; 76 QVBoxLayout* layout;
@@ -55,6 +78,8 @@ private:
55 QStandardItemModel* item_model; 78 QStandardItemModel* item_model;
56 QGraphicsScene* scene; 79 QGraphicsScene* scene;
57 80
81 ConfigureProfileManagerDeleteDialog* confirm_dialog;
82
58 std::vector<QList<QStandardItem*>> list_items; 83 std::vector<QList<QStandardItem*>> list_items;
59 84
60 std::unique_ptr<Ui::ConfigureProfileManager> ui; 85 std::unique_ptr<Ui::ConfigureProfileManager> ui;
diff --git a/src/yuzu/configuration/configure_profile_manager.ui b/src/yuzu/configuration/configure_profile_manager.ui
index cfe7478c8..bd6dea4f4 100644
--- a/src/yuzu/configuration/configure_profile_manager.ui
+++ b/src/yuzu/configuration/configure_profile_manager.ui
@@ -57,6 +57,12 @@
57 <height>48</height> 57 <height>48</height>
58 </size> 58 </size>
59 </property> 59 </property>
60 <property name="frameShape">
61 <enum>QFrame::NoFrame</enum>
62 </property>
63 <property name="frameShadow">
64 <enum>QFrame::Plain</enum>
65 </property>
60 <property name="verticalScrollBarPolicy"> 66 <property name="verticalScrollBarPolicy">
61 <enum>Qt::ScrollBarAlwaysOff</enum> 67 <enum>Qt::ScrollBarAlwaysOff</enum>
62 </property> 68 </property>
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..7ee2302cc 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();
@@ -360,7 +361,11 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan
360 } 361 }
361 } 362 }
362 LOG_INFO(Frontend, "Host CPU: {}", cpu_string); 363 LOG_INFO(Frontend, "Host CPU: {}", cpu_string);
364 if (std::optional<int> processor_core = Common::GetProcessorCount()) {
365 LOG_INFO(Frontend, "Host CPU Cores: {}", *processor_core);
366 }
363#endif 367#endif
368 LOG_INFO(Frontend, "Host CPU Threads: {}", processor_count);
364 LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString()); 369 LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString());
365 LOG_INFO(Frontend, "Host RAM: {:.2f} GiB", 370 LOG_INFO(Frontend, "Host RAM: {:.2f} GiB",
366 Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB}); 371 Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB});
@@ -1951,6 +1956,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
1951 } 1956 }
1952 default: 1957 default:
1953 UNIMPLEMENTED(); 1958 UNIMPLEMENTED();
1959 break;
1954 } 1960 }
1955 1961
1956 const QString qpath = QString::fromStdString(Common::FS::PathToUTF8String(path)); 1962 const QString qpath = QString::fromStdString(Common::FS::PathToUTF8String(path));
@@ -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};
@@ -3168,6 +3200,7 @@ void GMainWindow::OnToggleGpuAccuracy() {
3168 case Settings::GPUAccuracy::Extreme: 3200 case Settings::GPUAccuracy::Extreme:
3169 default: { 3201 default: {
3170 Settings::values.gpu_accuracy.SetValue(Settings::GPUAccuracy::High); 3202 Settings::values.gpu_accuracy.SetValue(Settings::GPUAccuracy::High);
3203 break;
3171 } 3204 }
3172 } 3205 }
3173 3206
@@ -3500,6 +3533,7 @@ void GMainWindow::UpdateGPUAccuracyButton() {
3500 default: { 3533 default: {
3501 gpu_accuracy_button->setText(tr("GPU ERROR")); 3534 gpu_accuracy_button->setText(tr("GPU ERROR"));
3502 gpu_accuracy_button->setChecked(true); 3535 gpu_accuracy_button->setChecked(true);
3536 break;
3503 } 3537 }
3504 } 3538 }
3505} 3539}
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);
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
index 65455c86e..25948328c 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
@@ -84,6 +84,7 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsyste
84 default: 84 default:
85 LOG_CRITICAL(Frontend, "Window manager subsystem not implemented"); 85 LOG_CRITICAL(Frontend, "Window manager subsystem not implemented");
86 std::exit(EXIT_FAILURE); 86 std::exit(EXIT_FAILURE);
87 break;
87 } 88 }
88 89
89 OnResize(); 90 OnResize();
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index e16f79eb4..dfe5a30ea 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -351,6 +351,7 @@ int main(int argc, char** argv) {
351 "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}", 351 "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}",
352 loader_id, error_id, static_cast<Loader::ResultStatus>(error_id)); 352 loader_id, error_id, static_cast<Loader::ResultStatus>(error_id));
353 } 353 }
354 break;
354 } 355 }
355 356
356 system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", "SDL"); 357 system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", "SDL");