summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt8
-rw-r--r--externals/CMakeLists.txt11
m---------externals/dynarmic0
-rw-r--r--externals/microprofile/microprofileui.h4
m---------externals/xbyak0
-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
118 files changed, 2136 insertions, 681 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c6fc5dd9e..1d13dc74e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -133,13 +133,13 @@ if (NOT ENABLE_GENERIC)
if (MSVC)
detect_architecture("_M_AMD64" x86_64)
detect_architecture("_M_IX86" x86)
- detect_architecture("_M_ARM" ARM)
- detect_architecture("_M_ARM64" ARM64)
+ detect_architecture("_M_ARM" arm)
+ detect_architecture("_M_ARM64" arm64)
else()
detect_architecture("__x86_64__" x86_64)
detect_architecture("__i386__" x86)
- detect_architecture("__arm__" ARM)
- detect_architecture("__aarch64__" ARM64)
+ detect_architecture("__arm__" arm)
+ detect_architecture("__aarch64__" arm64)
endif()
endif()
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index e80fd124e..7f0a6d069 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -7,15 +7,14 @@ include(DownloadExternals)
# xbyak
if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
- add_library(xbyak INTERFACE)
- file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
- file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/xbyak/xbyak DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
- target_include_directories(xbyak SYSTEM INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
- target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES)
+ add_subdirectory(xbyak)
endif()
# Dynarmic
-if (ARCHITECTURE_x86_64)
+if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
+ if (ARCHITECTURE_arm64)
+ set(DYNARMIC_FRONTENDS "A32")
+ endif()
set(DYNARMIC_NO_BUNDLED_FMT ON)
set(DYNARMIC_IGNORE_ASSERTS ON CACHE BOOL "" FORCE)
add_subdirectory(dynarmic)
diff --git a/externals/dynarmic b/externals/dynarmic
-Subproject 2d4602a6516c67d547000d4c80bcc5f74976abd
+Subproject 424fdb5c5026ec5bdd7553271190397f63fb503
diff --git a/externals/microprofile/microprofileui.h b/externals/microprofile/microprofileui.h
index 1357a08fd..ca9fe7063 100644
--- a/externals/microprofile/microprofileui.h
+++ b/externals/microprofile/microprofileui.h
@@ -845,8 +845,6 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
MicroProfile& S = *MicroProfileGet();
MP_DEBUG_DUMP_RANGE();
int nY = nBaseY - UI.nOffsetY;
- int64_t nNumBoxes = 0;
- int64_t nNumLines = 0;
uint32_t nFrameNext = (S.nFrameCurrent+1) % MICROPROFILE_MAX_FRAME_HISTORY;
MicroProfileFrameState* pFrameCurrent = &S.Frames[S.nFrameCurrent];
@@ -1149,7 +1147,6 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
}
}
#endif
- ++nNumBoxes;
}
else
{
@@ -1165,7 +1162,6 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
}
nLinesDrawn[nStackPos] = nLineX;
MicroProfileDrawLineVertical(nLineX, fYStart + 0.5f, fYEnd + 0.5f, nColor|UI.nOpacityForeground);
- ++nNumLines;
}
}
nStackPos--;
diff --git a/externals/xbyak b/externals/xbyak
-Subproject c306b8e5786eeeb87b8925a8af5c3bf057ff5a9
+Subproject 348e3e548ebac06d243e5881caec8440e249f65
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()
endif()
target_link_libraries(audio_core PUBLIC common core)
-if (ARCHITECTURE_x86_64)
+if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
target_link_libraries(audio_core PRIVATE dynarmic)
endif()
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) {
impl = std::make_unique<
PerformanceManagerImpl<PerformanceVersion::Version1, PerformanceFrameHeaderVersion1,
PerformanceEntryVersion1, PerformanceDetailVersion1>>();
+ break;
}
}
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
bit_util.h
cityhash.cpp
cityhash.h
+ cache_management.cpp
+ cache_management.h
common_funcs.h
common_types.h
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 {
break;
default:
assert(false);
+ break;
}
}
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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <cstring>
+
+#include "alignment.h"
+#include "cache_management.h"
+#include "common_types.h"
+
+namespace Common {
+
+#if defined(ARCHITECTURE_x86_64)
+
+// Most cache operations are no-ops on x86
+
+void DataCacheLineCleanByVAToPoU(void* start, size_t size) {}
+void DataCacheLineCleanAndInvalidateByVAToPoC(void* start, size_t size) {}
+void DataCacheLineCleanByVAToPoC(void* start, size_t size) {}
+void DataCacheZeroByVA(void* start, size_t size) {
+ std::memset(start, 0, size);
+}
+
+#elif defined(ARCHITECTURE_arm64)
+
+// BS/DminLine is log2(cache size in words), we want size in bytes
+#define EXTRACT_DMINLINE(ctr_el0) (1 << ((((ctr_el0) >> 16) & 0xf) + 2))
+#define EXTRACT_BS(dczid_el0) (1 << (((dczid_el0)&0xf) + 2))
+
+#define DEFINE_DC_OP(op_name, function_name) \
+ void function_name(void* start, size_t size) { \
+ size_t ctr_el0; \
+ asm volatile("mrs %[ctr_el0], ctr_el0\n\t" : [ctr_el0] "=r"(ctr_el0)); \
+ size_t cacheline_size = EXTRACT_DMINLINE(ctr_el0); \
+ uintptr_t va_start = reinterpret_cast<uintptr_t>(start); \
+ uintptr_t va_end = va_start + size; \
+ for (uintptr_t va = va_start; va < va_end; va += cacheline_size) { \
+ asm volatile("dc " #op_name ", %[va]\n\t" : : [va] "r"(va) : "memory"); \
+ } \
+ }
+
+#define DEFINE_DC_OP_DCZID(op_name, function_name) \
+ void function_name(void* start, size_t size) { \
+ size_t dczid_el0; \
+ asm volatile("mrs %[dczid_el0], dczid_el0\n\t" : [dczid_el0] "=r"(dczid_el0)); \
+ size_t cacheline_size = EXTRACT_BS(dczid_el0); \
+ uintptr_t va_start = reinterpret_cast<uintptr_t>(start); \
+ uintptr_t va_end = va_start + size; \
+ for (uintptr_t va = va_start; va < va_end; va += cacheline_size) { \
+ asm volatile("dc " #op_name ", %[va]\n\t" : : [va] "r"(va) : "memory"); \
+ } \
+ }
+
+DEFINE_DC_OP(cvau, DataCacheLineCleanByVAToPoU);
+DEFINE_DC_OP(civac, DataCacheLineCleanAndInvalidateByVAToPoC);
+DEFINE_DC_OP(cvac, DataCacheLineCleanByVAToPoC);
+DEFINE_DC_OP_DCZID(zva, DataCacheZeroByVA);
+
+#endif
+
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "stdlib.h"
+
+namespace Common {
+
+// Data cache instructions enabled at EL0 by SCTLR_EL1.UCI.
+// VA = virtual address
+// PoC = point of coherency
+// PoU = point of unification
+
+// dc cvau
+void DataCacheLineCleanByVAToPoU(void* start, size_t size);
+
+// dc civac
+void DataCacheLineCleanAndInvalidateByVAToPoC(void* start, size_t size);
+
+// dc cvac
+void DataCacheLineCleanByVAToPoC(void* start, size_t size);
+
+// dc zva
+void DataCacheZeroByVA(void* start, size_t size);
+
+} // 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 @@
#ifndef _MSC_VER
-#ifdef ARCHITECTURE_x86_64
+#if defined(ARCHITECTURE_x86_64)
#define Crash() __asm__ __volatile__("int $3")
+#elif defined(ARCHITECTURE_arm64)
+#define Crash() __asm__ __volatile__("brk #0")
#else
#define Crash() exit(1)
#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:
}
});
+ long page_size = sysconf(_SC_PAGESIZE);
+ if (page_size != 0x1000) {
+ LOG_CRITICAL(HW_Memory, "page size {:#x} is incompatible with 4K paging", page_size);
+ throw std::bad_alloc{};
+ }
+
// Backing memory initialization
#if defined(__FreeBSD__) && __FreeBSD__ < 13
// 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() {
ASSERT(false);
info.up_scale = 1;
info.down_shift = 0;
+ break;
}
info.up_factor = static_cast<f32>(info.up_scale) / (1U << info.down_shift);
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 @@
#include <array>
#include <cstring>
+#include <fstream>
#include <iterator>
+#include <optional>
#include <string_view>
+#include <thread>
+#include <vector>
#include "common/bit_util.h"
#include "common/common_types.h"
+#include "common/logging/log.h"
#include "common/x64/cpu_detect.h"
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
#ifdef _MSC_VER
#include <intrin.h>
+
+static inline u64 xgetbv(u32 index) {
+ return _xgetbv(index);
+}
#else
#if defined(__DragonFly__) || defined(__FreeBSD__)
@@ -39,12 +52,11 @@ static inline void __cpuid(int info[4], u32 function_id) {
}
#define _XCR_XFEATURE_ENABLED_MASK 0
-static inline u64 _xgetbv(u32 index) {
+static inline u64 xgetbv(u32 index) {
u32 eax, edx;
__asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(index));
return ((u64)edx << 32) | eax;
}
-
#endif // _MSC_VER
namespace Common {
@@ -107,7 +119,7 @@ static CPUCaps Detect() {
// - Is the XSAVE bit set in CPUID?
// - XGETBV result has the XCR bit set.
if (Common::Bit<28>(cpu_id[2]) && Common::Bit<27>(cpu_id[2])) {
- if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) {
+ if ((xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) {
caps.avx = true;
if (Common::Bit<12>(cpu_id[2]))
caps.fma = true;
@@ -192,4 +204,45 @@ const CPUCaps& GetCPUCaps() {
return caps;
}
+std::optional<int> GetProcessorCount() {
+#if defined(_WIN32)
+ // Get the buffer length.
+ DWORD length = 0;
+ GetLogicalProcessorInformation(nullptr, &length);
+ if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ LOG_ERROR(Frontend, "Failed to query core count.");
+ return std::nullopt;
+ }
+ std::vector<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> buffer(
+ length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION));
+ // Now query the core count.
+ if (!GetLogicalProcessorInformation(buffer.data(), &length)) {
+ LOG_ERROR(Frontend, "Failed to query core count.");
+ return std::nullopt;
+ }
+ return static_cast<int>(
+ std::count_if(buffer.cbegin(), buffer.cend(), [](const auto& proc_info) {
+ return proc_info.Relationship == RelationProcessorCore;
+ }));
+#elif defined(__unix__)
+ const int thread_count = std::thread::hardware_concurrency();
+ std::ifstream smt("/sys/devices/system/cpu/smt/active");
+ char state = '0';
+ if (smt) {
+ smt.read(&state, sizeof(state));
+ }
+ switch (state) {
+ case '0':
+ return thread_count;
+ case '1':
+ return thread_count / 2;
+ default:
+ return std::nullopt;
+ }
+#else
+ // Shame on you
+ return std::nullopt;
+#endif
+}
+
} // 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 @@
#pragma once
+#include <optional>
#include <string_view>
#include "common/common_types.h"
@@ -74,4 +75,7 @@ struct CPUCaps {
*/
const CPUCaps& GetCPUCaps();
+/// Detects CPU core count
+std::optional<int> GetProcessorCount();
+
} // 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
hle/service/hid/irsensor/processor_base.h
hle/service/hid/irsensor/tera_plugin_processor.cpp
hle/service/hid/irsensor/tera_plugin_processor.h
- hle/service/jit/jit_context.cpp
- hle/service/jit/jit_context.h
- hle/service/jit/jit.cpp
- hle/service/jit/jit.h
hle/service/lbl/lbl.cpp
hle/service/lbl/lbl.h
hle/service/ldn/lan_discovery.cpp
@@ -805,14 +801,18 @@ if (ENABLE_WEB_SERVICE)
target_link_libraries(core PRIVATE web_service)
endif()
-if (ARCHITECTURE_x86_64)
+if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
target_sources(core PRIVATE
- arm/dynarmic/arm_dynarmic_32.cpp
- arm/dynarmic/arm_dynarmic_32.h
arm/dynarmic/arm_dynarmic_64.cpp
arm/dynarmic/arm_dynarmic_64.h
+ arm/dynarmic/arm_dynarmic_32.cpp
+ arm/dynarmic/arm_dynarmic_32.h
arm/dynarmic/arm_dynarmic_cp15.cpp
arm/dynarmic/arm_dynarmic_cp15.h
+ hle/service/jit/jit_context.cpp
+ hle/service/jit/jit_context.h
+ hle/service/jit/jit.cpp
+ hle/service/jit/jit.h
)
target_link_libraries(core PRIVATE dynarmic)
endif()
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*
}
}
+#ifdef ARCHITECTURE_arm64
+ // TODO: remove when fixed in dynarmic
+ config.optimizations &= ~Dynarmic::OptimizationFlag::BlockLinking;
+#endif
+
return std::make_unique<Dynarmic::A32::Jit>(config);
}
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
case 4:
// CP15_DATA_SYNC_BARRIER
return Callback{
- [](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
-#ifdef _MSC_VER
+ [](void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
+#if defined(_MSC_VER) && defined(ARCHITECTURE_x86_64)
_mm_mfence();
_mm_lfence();
-#else
+#elif defined(ARCHITECTURE_x86_64)
asm volatile("mfence\n\tlfence\n\t" : : : "memory");
+#elif defined(ARCHITECTURE_arm64)
+ asm volatile("dsb sy\n\t" : : : "memory");
+#else
+#error Unsupported architecture
#endif
return 0;
},
@@ -66,11 +70,15 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1
case 5:
// CP15_DATA_MEMORY_BARRIER
return Callback{
- [](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
-#ifdef _MSC_VER
+ [](void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
+#if defined(_MSC_VER) && defined(ARCHITECTURE_x86_64)
_mm_mfence();
-#else
+#elif defined(ARCHITECTURE_x86_64)
asm volatile("mfence\n\t" : : : "memory");
+#elif defined(ARCHITECTURE_arm64)
+ asm volatile("dmb sy\n\t" : : : "memory");
+#else
+#error Unsupported architecture
#endif
return 0;
},
@@ -115,7 +123,7 @@ CallbackOrAccessOneWord DynarmicCP15::CompileGetOneWord(bool two, unsigned opc1,
CallbackOrAccessTwoWords DynarmicCP15::CompileGetTwoWords(bool two, unsigned opc, CoprocReg CRm) {
if (!two && opc == 0 && CRm == CoprocReg::C14) {
// CNTPCT
- const auto callback = [](Dynarmic::A32::Jit*, void* arg, u32, u32) -> u64 {
+ const auto callback = [](void* arg, u32, u32) -> u64 {
const auto& parent_arg = *static_cast<ARM_Dynarmic_32*>(arg);
return parent_arg.system.CoreTiming().GetClockTicks();
};
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 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-#ifdef ARCHITECTURE_x86_64
+#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
#endif
#include "core/arm/exclusive_monitor.h"
@@ -13,7 +13,7 @@ ExclusiveMonitor::~ExclusiveMonitor() = default;
std::unique_ptr<Core::ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory,
std::size_t num_cores) {
-#ifdef ARCHITECTURE_x86_64
+#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
return std::make_unique<Core::DynarmicExclusiveMonitor>(memory, num_cores);
#else
// 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) {
const u8* buffer_start = reinterpret_cast<const u8*>(&buffer);
std::span<const u8> received_data{buffer_start, buffer_start + bytes_read};
c(received_data);
+ AsyncReceiveInto(r, buffer, c);
}
-
- AsyncReceiveInto(r, buffer, c);
});
}
+template <typename Callback>
+static void AsyncAccept(boost::asio::ip::tcp::acceptor& acceptor, Callback&& c) {
+ acceptor.async_accept([&, c](const boost::system::error_code& error, auto&& peer_socket) {
+ if (!error.failed()) {
+ c(peer_socket);
+ AsyncAccept(acceptor, c);
+ }
+ });
+}
+
template <typename Readable, typename Buffer>
static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) {
static_assert(std::is_trivial_v<Buffer>);
@@ -59,9 +68,7 @@ namespace Core {
class DebuggerImpl : public DebuggerBackend {
public:
- explicit DebuggerImpl(Core::System& system_, u16 port)
- : system{system_}, signal_pipe{io_context}, client_socket{io_context} {
- frontend = std::make_unique<GDBStub>(*this, system);
+ explicit DebuggerImpl(Core::System& system_, u16 port) : system{system_} {
InitializeServer(port);
}
@@ -70,39 +77,42 @@ public:
}
bool SignalDebugger(SignalInfo signal_info) {
- {
- std::scoped_lock lk{connection_lock};
+ std::scoped_lock lk{connection_lock};
- if (stopped) {
- // Do not notify the debugger about another event.
- // It should be ignored.
- return false;
- }
-
- // Set up the state.
- stopped = true;
- info = signal_info;
+ if (stopped || !state) {
+ // Do not notify the debugger about another event.
+ // It should be ignored.
+ return false;
}
+ // Set up the state.
+ stopped = true;
+ state->info = signal_info;
+
// Write a single byte into the pipe to wake up the debug interface.
- boost::asio::write(signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped)));
+ boost::asio::write(state->signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped)));
+
return true;
}
+ // These functions are callbacks from the frontend, and the lock will be held.
+ // There is no need to relock it.
+
std::span<const u8> ReadFromClient() override {
- return ReceiveInto(client_socket, client_data);
+ return ReceiveInto(state->client_socket, state->client_data);
}
void WriteToClient(std::span<const u8> data) override {
- boost::asio::write(client_socket, boost::asio::buffer(data.data(), data.size_bytes()));
+ boost::asio::write(state->client_socket,
+ boost::asio::buffer(data.data(), data.size_bytes()));
}
void SetActiveThread(Kernel::KThread* thread) override {
- active_thread = thread;
+ state->active_thread = thread;
}
Kernel::KThread* GetActiveThread() override {
- return active_thread;
+ return state->active_thread;
}
private:
@@ -113,65 +123,78 @@ private:
// Run the connection thread.
connection_thread = std::jthread([&, port](std::stop_token stop_token) {
+ Common::SetCurrentThreadName("Debugger");
+
try {
// Initialize the listening socket and accept a new client.
tcp::endpoint endpoint{boost::asio::ip::address_v4::any(), port};
tcp::acceptor acceptor{io_context, endpoint};
- acceptor.async_accept(client_socket, [](const auto&) {});
- io_context.run_one();
- io_context.restart();
+ AsyncAccept(acceptor, [&](auto&& peer) { AcceptConnection(std::move(peer)); });
- if (stop_token.stop_requested()) {
- return;
+ while (!stop_token.stop_requested() && io_context.run()) {
}
-
- ThreadLoop(stop_token);
} catch (const std::exception& ex) {
LOG_CRITICAL(Debug_GDBStub, "Stopping server: {}", ex.what());
}
});
}
- void ShutdownServer() {
- connection_thread.request_stop();
- io_context.stop();
- connection_thread.join();
- }
+ void AcceptConnection(boost::asio::ip::tcp::socket&& peer) {
+ LOG_INFO(Debug_GDBStub, "Accepting new peer connection");
- void ThreadLoop(std::stop_token stop_token) {
- Common::SetCurrentThreadName("Debugger");
+ std::scoped_lock lk{connection_lock};
+
+ // Ensure everything is stopped.
+ PauseEmulation();
+
+ // Set up the new frontend.
+ frontend = std::make_unique<GDBStub>(*this, system);
+
+ // Set the new state. This will tear down any existing state.
+ state = ConnectionState{
+ .client_socket{std::move(peer)},
+ .signal_pipe{io_context},
+ .info{},
+ .active_thread{},
+ .client_data{},
+ .pipe_data{},
+ };
// Set up the client signals for new data.
- AsyncReceiveInto(signal_pipe, pipe_data, [&](auto d) { PipeData(d); });
- AsyncReceiveInto(client_socket, client_data, [&](auto d) { ClientData(d); });
+ AsyncReceiveInto(state->signal_pipe, state->pipe_data, [&](auto d) { PipeData(d); });
+ AsyncReceiveInto(state->client_socket, state->client_data, [&](auto d) { ClientData(d); });
// Set the active thread.
UpdateActiveThread();
// Set up the frontend.
frontend->Connected();
+ }
- // Main event loop.
- while (!stop_token.stop_requested() && io_context.run()) {
- }
+ void ShutdownServer() {
+ connection_thread.request_stop();
+ io_context.stop();
+ connection_thread.join();
}
void PipeData(std::span<const u8> data) {
- switch (info.type) {
+ std::scoped_lock lk{connection_lock};
+
+ switch (state->info.type) {
case SignalType::Stopped:
case SignalType::Watchpoint:
// Stop emulation.
PauseEmulation();
// Notify the client.
- active_thread = info.thread;
+ state->active_thread = state->info.thread;
UpdateActiveThread();
- if (info.type == SignalType::Watchpoint) {
- frontend->Watchpoint(active_thread, *info.watchpoint);
+ if (state->info.type == SignalType::Watchpoint) {
+ frontend->Watchpoint(state->active_thread, *state->info.watchpoint);
} else {
- frontend->Stopped(active_thread);
+ frontend->Stopped(state->active_thread);
}
break;
@@ -179,8 +202,8 @@ private:
frontend->ShuttingDown();
// Wait for emulation to shut down gracefully now.
- signal_pipe.close();
- client_socket.shutdown(boost::asio::socket_base::shutdown_both);
+ state->signal_pipe.close();
+ state->client_socket.shutdown(boost::asio::socket_base::shutdown_both);
LOG_INFO(Debug_GDBStub, "Shut down server");
break;
@@ -188,17 +211,16 @@ private:
}
void ClientData(std::span<const u8> data) {
+ std::scoped_lock lk{connection_lock};
+
const auto actions{frontend->ClientData(data)};
for (const auto action : actions) {
switch (action) {
case DebuggerAction::Interrupt: {
- {
- std::scoped_lock lk{connection_lock};
- stopped = true;
- }
+ stopped = true;
PauseEmulation();
UpdateActiveThread();
- frontend->Stopped(active_thread);
+ frontend->Stopped(state->active_thread);
break;
}
case DebuggerAction::Continue:
@@ -206,15 +228,15 @@ private:
break;
case DebuggerAction::StepThreadUnlocked:
MarkResumed([&] {
- active_thread->SetStepState(Kernel::StepState::StepPending);
- active_thread->Resume(Kernel::SuspendType::Debug);
- ResumeEmulation(active_thread);
+ state->active_thread->SetStepState(Kernel::StepState::StepPending);
+ state->active_thread->Resume(Kernel::SuspendType::Debug);
+ ResumeEmulation(state->active_thread);
});
break;
case DebuggerAction::StepThreadLocked: {
MarkResumed([&] {
- active_thread->SetStepState(Kernel::StepState::StepPending);
- active_thread->Resume(Kernel::SuspendType::Debug);
+ state->active_thread->SetStepState(Kernel::StepState::StepPending);
+ state->active_thread->Resume(Kernel::SuspendType::Debug);
});
break;
}
@@ -254,15 +276,14 @@ private:
template <typename Callback>
void MarkResumed(Callback&& cb) {
Kernel::KScopedSchedulerLock sl{system.Kernel()};
- std::scoped_lock cl{connection_lock};
stopped = false;
cb();
}
void UpdateActiveThread() {
const auto& threads{ThreadList()};
- if (std::find(threads.begin(), threads.end(), active_thread) == threads.end()) {
- active_thread = threads[0];
+ if (std::find(threads.begin(), threads.end(), state->active_thread) == threads.end()) {
+ state->active_thread = threads[0];
}
}
@@ -274,18 +295,22 @@ private:
System& system;
std::unique_ptr<DebuggerFrontend> frontend;
+ boost::asio::io_context io_context;
std::jthread connection_thread;
std::mutex connection_lock;
- boost::asio::io_context io_context;
- boost::process::async_pipe signal_pipe;
- boost::asio::ip::tcp::socket client_socket;
- SignalInfo info;
- Kernel::KThread* active_thread;
- bool pipe_data;
- bool stopped;
+ struct ConnectionState {
+ boost::asio::ip::tcp::socket client_socket;
+ boost::process::async_pipe signal_pipe;
+
+ SignalInfo info;
+ Kernel::KThread* active_thread;
+ std::array<u8, 4096> client_data;
+ bool pipe_data;
+ };
- std::array<u8, 4096> client_data;
+ std::optional<ConnectionState> state{};
+ bool stopped{};
};
Debugger::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) {
} else if (command.starts_with("StartNoAckMode")) {
no_ack = true;
SendReply(GDB_STUB_REPLY_OK);
+ } else if (command.starts_with("Rcmd,")) {
+ HandleRcmd(Common::HexStringToVector(command.substr(5), false));
} else {
SendReply(GDB_STUB_REPLY_EMPTY);
}
@@ -645,6 +647,155 @@ void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>&
}
}
+constexpr std::array<std::pair<const char*, Kernel::Svc::MemoryState>, 22> MemoryStateNames{{
+ {"----- Free -----", Kernel::Svc::MemoryState::Free},
+ {"Io ", Kernel::Svc::MemoryState::Io},
+ {"Static ", Kernel::Svc::MemoryState::Static},
+ {"Code ", Kernel::Svc::MemoryState::Code},
+ {"CodeData ", Kernel::Svc::MemoryState::CodeData},
+ {"Normal ", Kernel::Svc::MemoryState::Normal},
+ {"Shared ", Kernel::Svc::MemoryState::Shared},
+ {"AliasCode ", Kernel::Svc::MemoryState::AliasCode},
+ {"AliasCodeData ", Kernel::Svc::MemoryState::AliasCodeData},
+ {"Ipc ", Kernel::Svc::MemoryState::Ipc},
+ {"Stack ", Kernel::Svc::MemoryState::Stack},
+ {"ThreadLocal ", Kernel::Svc::MemoryState::ThreadLocal},
+ {"Transfered ", Kernel::Svc::MemoryState::Transfered},
+ {"SharedTransfered", Kernel::Svc::MemoryState::SharedTransfered},
+ {"SharedCode ", Kernel::Svc::MemoryState::SharedCode},
+ {"Inaccessible ", Kernel::Svc::MemoryState::Inaccessible},
+ {"NonSecureIpc ", Kernel::Svc::MemoryState::NonSecureIpc},
+ {"NonDeviceIpc ", Kernel::Svc::MemoryState::NonDeviceIpc},
+ {"Kernel ", Kernel::Svc::MemoryState::Kernel},
+ {"GeneratedCode ", Kernel::Svc::MemoryState::GeneratedCode},
+ {"CodeOut ", Kernel::Svc::MemoryState::CodeOut},
+ {"Coverage ", Kernel::Svc::MemoryState::Coverage},
+}};
+
+static constexpr const char* GetMemoryStateName(Kernel::Svc::MemoryState state) {
+ for (size_t i = 0; i < MemoryStateNames.size(); i++) {
+ if (std::get<1>(MemoryStateNames[i]) == state) {
+ return std::get<0>(MemoryStateNames[i]);
+ }
+ }
+ return "Unknown ";
+}
+
+static constexpr const char* GetMemoryPermissionString(const Kernel::Svc::MemoryInfo& info) {
+ if (info.state == Kernel::Svc::MemoryState::Free) {
+ return " ";
+ }
+
+ switch (info.permission) {
+ case Kernel::Svc::MemoryPermission::ReadExecute:
+ return "r-x";
+ case Kernel::Svc::MemoryPermission::Read:
+ return "r--";
+ case Kernel::Svc::MemoryPermission::ReadWrite:
+ return "rw-";
+ default:
+ return "---";
+ }
+}
+
+static VAddr GetModuleEnd(Kernel::KPageTable& page_table, VAddr base) {
+ Kernel::Svc::MemoryInfo mem_info;
+ VAddr cur_addr{base};
+
+ // Expect: r-x Code (.text)
+ mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
+ cur_addr = mem_info.base_address + mem_info.size;
+ if (mem_info.state != Kernel::Svc::MemoryState::Code ||
+ mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) {
+ return cur_addr - 1;
+ }
+
+ // Expect: r-- Code (.rodata)
+ mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
+ cur_addr = mem_info.base_address + mem_info.size;
+ if (mem_info.state != Kernel::Svc::MemoryState::Code ||
+ mem_info.permission != Kernel::Svc::MemoryPermission::Read) {
+ return cur_addr - 1;
+ }
+
+ // Expect: rw- CodeData (.data)
+ mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
+ cur_addr = mem_info.base_address + mem_info.size;
+ return cur_addr - 1;
+}
+
+void GDBStub::HandleRcmd(const std::vector<u8>& command) {
+ std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()};
+ std::string reply;
+
+ auto* process = system.CurrentProcess();
+ auto& page_table = process->PageTable();
+
+ if (command_str == "get info") {
+ Loader::AppLoader::Modules modules;
+ system.GetAppLoader().ReadNSOModules(modules);
+
+ reply = fmt::format("Process: {:#x} ({})\n"
+ "Program Id: {:#018x}\n",
+ process->GetProcessID(), process->GetName(), process->GetProgramID());
+ reply +=
+ fmt::format("Layout:\n"
+ " Alias: {:#012x} - {:#012x}\n"
+ " Heap: {:#012x} - {:#012x}\n"
+ " Aslr: {:#012x} - {:#012x}\n"
+ " Stack: {:#012x} - {:#012x}\n"
+ "Modules:\n",
+ page_table.GetAliasRegionStart(), page_table.GetAliasRegionEnd(),
+ page_table.GetHeapRegionStart(), page_table.GetHeapRegionEnd(),
+ page_table.GetAliasCodeRegionStart(), page_table.GetAliasCodeRegionEnd(),
+ page_table.GetStackRegionStart(), page_table.GetStackRegionEnd());
+
+ for (const auto& [vaddr, name] : modules) {
+ reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr,
+ GetModuleEnd(page_table, vaddr), name);
+ }
+ } else if (command_str == "get mappings") {
+ reply = "Mappings:\n";
+ VAddr cur_addr = 0;
+
+ while (true) {
+ using MemoryAttribute = Kernel::Svc::MemoryAttribute;
+
+ auto mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
+
+ if (mem_info.state != Kernel::Svc::MemoryState::Inaccessible ||
+ mem_info.base_address + mem_info.size - 1 != std::numeric_limits<u64>::max()) {
+ const char* state = GetMemoryStateName(mem_info.state);
+ const char* perm = GetMemoryPermissionString(mem_info);
+
+ const char l = True(mem_info.attribute & MemoryAttribute::Locked) ? 'L' : '-';
+ const char i = True(mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-';
+ const char d = True(mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-';
+ const char u = True(mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-';
+
+ reply +=
+ fmt::format(" {:#012x} - {:#012x} {} {} {}{}{}{} [{}, {}]\n",
+ mem_info.base_address, mem_info.base_address + mem_info.size - 1,
+ perm, state, l, i, d, u, mem_info.ipc_count, mem_info.device_count);
+ }
+
+ const uintptr_t next_address = mem_info.base_address + mem_info.size;
+ if (next_address <= cur_addr) {
+ break;
+ }
+
+ cur_addr = next_address;
+ }
+ } else if (command_str == "help") {
+ reply = "Commands:\n get info\n get mappings\n";
+ } else {
+ reply = "Unknown command.\nCommands:\n get info\n get mappings\n";
+ }
+
+ std::span<const u8> reply_span{reinterpret_cast<u8*>(&reply.front()), reply.size()};
+ SendReply(Common::HexToString(reply_span, false));
+}
+
Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
const auto& threads{system.GlobalSchedulerContext().GetThreadList()};
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:
void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions);
void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions);
void HandleQuery(std::string_view command);
+ void HandleRcmd(const std::vector<u8>& command);
void HandleBreakpointInsert(std::string_view command);
void HandleBreakpointRemove(std::string_view command);
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:
context->AddDomainObject(std::move(iface));
} else {
kernel.CurrentProcess()->GetResourceLimit()->Reserve(
- Kernel::LimitableResource::Sessions, 1);
+ Kernel::LimitableResource::SessionCountMax, 1);
auto* session = Kernel::KSession::Create(kernel);
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) {
// If we somehow get an invalid type, abort.
default:
ASSERT_MSG(false, "Unknown slab type: {}", slab_types[i]);
+ break;
}
// If we've hit the end of a gap, free it.
@@ -265,7 +266,8 @@ void KPageBufferSlabHeap::Initialize(Core::System& system) {
const size_t slab_size = num_pages * PageSize;
// Reserve memory from the system resource limit.
- ASSERT(kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemory, slab_size));
+ ASSERT(
+ kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemoryMax, slab_size));
// Allocate memory for the slab.
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 {
Result KClientPort::CreateSession(KClientSession** out) {
// Reserve a new session from the resource limit.
KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(),
- LimitableResource::Sessions);
+ LimitableResource::SessionCountMax);
R_UNLESS(session_reservation.Succeeded(), ResultLimitReached);
// 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) {
m_readable_event.Initialize(this);
// Set our owner process.
- m_owner = owner;
- m_owner->Open();
+ // HACK: this should never be nullptr, but service threads don't have a
+ // proper parent process yet.
+ if (owner != nullptr) {
+ m_owner = owner;
+ m_owner->Open();
+ }
// Mark initialized.
m_initialized = true;
@@ -50,8 +54,11 @@ Result KEvent::Clear() {
void KEvent::PostDestroy(uintptr_t arg) {
// Release the event count resource the owner process holds.
KProcess* owner = reinterpret_cast<KProcess*>(arg);
- owner->GetResourceLimit()->Release(LimitableResource::Events, 1);
- owner->Close();
+
+ if (owner != nullptr) {
+ owner->GetResourceLimit()->Release(LimitableResource::EventCountMax, 1);
+ owner->Close();
+ }
}
} // 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 {
constexpr Svc::MemoryInfo GetSvcMemoryInfo() const {
return {
- .addr = m_address,
+ .base_address = m_address,
.size = m_size,
.state = static_cast<Svc::MemoryState>(m_state & KMemoryState::Mask),
- .attr = static_cast<Svc::MemoryAttribute>(m_attribute & KMemoryAttribute::UserMask),
- .perm = static_cast<Svc::MemoryPermission>(m_permission & KMemoryPermission::UserMask),
- .ipc_refcount = m_ipc_lock_count,
- .device_refcount = m_device_use_count,
+ .attribute =
+ static_cast<Svc::MemoryAttribute>(m_attribute & KMemoryAttribute::UserMask),
+ .permission =
+ static_cast<Svc::MemoryPermission>(m_permission & KMemoryPermission::UserMask),
+ .ipc_count = m_ipc_lock_count,
+ .device_count = m_device_use_count,
.padding = {},
};
}
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
// Reserve space for any partial pages we allocate.
const size_t unmapped_size = aligned_src_size - mapping_src_size;
- KScopedResourceReservation memory_reservation(m_resource_limit,
- LimitableResource::PhysicalMemory, unmapped_size);
+ KScopedResourceReservation memory_reservation(
+ m_resource_limit, LimitableResource::PhysicalMemoryMax, unmapped_size);
R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
// Ensure that we manage page references correctly.
@@ -1227,7 +1227,7 @@ Result KPageTable::CleanupForIpcServer(VAddr address, size_t size, KMemoryState
const VAddr mapping_start = Common::AlignUp((address), PageSize);
const VAddr mapping_end = Common::AlignDown((address) + size, PageSize);
const size_t mapping_size = (mapping_start < mapping_end) ? mapping_end - mapping_start : 0;
- m_resource_limit->Release(LimitableResource::PhysicalMemory, aligned_size - mapping_size);
+ m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, aligned_size - mapping_size);
R_SUCCEED();
}
@@ -1568,7 +1568,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
{
// Reserve the memory from the process resource limit.
KScopedResourceReservation memory_reservation(
- m_resource_limit, LimitableResource::PhysicalMemory, size - mapped_size);
+ m_resource_limit, LimitableResource::PhysicalMemoryMax, size - mapped_size);
R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
// Allocate pages for the new memory.
@@ -1908,7 +1908,7 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) {
// Release the memory resource.
m_mapped_physical_memory_size -= mapped_size;
- m_resource_limit->Release(LimitableResource::PhysicalMemory, mapped_size);
+ m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, mapped_size);
// Update memory blocks.
m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize,
@@ -2301,6 +2301,7 @@ Result KPageTable::SetProcessMemoryPermission(VAddr addr, size_t size,
break;
default:
ASSERT(false);
+ break;
}
}
@@ -2492,7 +2493,7 @@ Result KPageTable::SetHeapSize(VAddr* out, size_t size) {
OperationType::Unmap));
// Release the memory from the resource limit.
- m_resource_limit->Release(LimitableResource::PhysicalMemory, num_pages * PageSize);
+ m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, num_pages * PageSize);
// Apply the memory block update.
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) {
// Reserve memory for the heap extension.
KScopedResourceReservation memory_reservation(
- m_resource_limit, LimitableResource::PhysicalMemory, allocation_size);
+ m_resource_limit, LimitableResource::PhysicalMemoryMax, allocation_size);
R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
// Allocate pages for the heap extension.
@@ -2803,6 +2804,7 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, const KPageGroup& page_
break;
default:
ASSERT(false);
+ break;
}
addr += size;
@@ -2838,6 +2840,7 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, KMemoryPermission perm,
break;
default:
ASSERT(false);
+ break;
}
R_SUCCEED();
}
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:
constexpr VAddr GetAliasCodeRegionStart() const {
return m_alias_code_region_start;
}
+ constexpr VAddr GetAliasCodeRegionEnd() const {
+ return m_alias_code_region_end;
+ }
constexpr VAddr GetAliasCodeRegionSize() const {
return m_alias_code_region_end - m_alias_code_region_start;
}
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 {
*/
void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority, VAddr stack_top) {
const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart();
- ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::Threads, 1));
+ ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::ThreadCountMax, 1));
KThread* thread = KThread::Create(system.Kernel());
SCOPE_EXIT({ thread->Close(); });
@@ -124,7 +124,7 @@ void KProcess::DecrementRunningThreadCount() {
}
u64 KProcess::GetTotalPhysicalMemoryAvailable() {
- const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) +
+ const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemoryMax) +
page_table.GetNormalMemorySize() + GetSystemResourceSize() + image_size +
main_thread_stack_size};
if (const auto pool_size = kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application);
@@ -349,8 +349,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
// We currently do not support process-specific system resource
UNIMPLEMENTED_IF(system_resource_size != 0);
- KScopedResourceReservation memory_reservation(resource_limit, LimitableResource::PhysicalMemory,
- code_size + system_resource_size);
+ KScopedResourceReservation memory_reservation(
+ resource_limit, LimitableResource::PhysicalMemoryMax, code_size + system_resource_size);
if (!memory_reservation.Succeeded()) {
LOG_ERROR(Kernel, "Could not reserve process memory requirements of size {:X} bytes",
code_size + system_resource_size);
@@ -395,6 +395,7 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
default:
ASSERT(false);
+ break;
}
// Create TLS region
@@ -406,8 +407,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
void KProcess::Run(s32 main_thread_priority, u64 stack_size) {
AllocateMainThreadStack(stack_size);
- resource_limit->Reserve(LimitableResource::Threads, 1);
- resource_limit->Reserve(LimitableResource::PhysicalMemory, main_thread_stack_size);
+ resource_limit->Reserve(LimitableResource::ThreadCountMax, 1);
+ resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, main_thread_stack_size);
const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)};
ASSERT(!page_table.SetMaxHeapSize(heap_capacity).IsError());
@@ -442,7 +443,7 @@ void KProcess::PrepareForTermination() {
plr_address = 0;
if (resource_limit) {
- resource_limit->Release(LimitableResource::PhysicalMemory,
+ resource_limit->Release(LimitableResource::PhysicalMemoryMax,
main_thread_stack_size + image_size);
}
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
// TODO(bunnei): These values are the system defaults, the limits for service processes are
// lower. These should use the correct limit values.
- ASSERT(resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, physical_memory_size)
+ ASSERT(resource_limit->SetLimitValue(LimitableResource::PhysicalMemoryMax, physical_memory_size)
.IsSuccess());
- ASSERT(resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess());
- ASSERT(resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess());
- ASSERT(resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200).IsSuccess());
- ASSERT(resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess());
+ ASSERT(resource_limit->SetLimitValue(LimitableResource::ThreadCountMax, 800).IsSuccess());
+ ASSERT(resource_limit->SetLimitValue(LimitableResource::EventCountMax, 900).IsSuccess());
+ ASSERT(
+ resource_limit->SetLimitValue(LimitableResource::TransferMemoryCountMax, 200).IsSuccess());
+ ASSERT(resource_limit->SetLimitValue(LimitableResource::SessionCountMax, 1133).IsSuccess());
return resource_limit;
}
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;
namespace Kernel {
class KernelCore;
-enum class LimitableResource : u32 {
- PhysicalMemory = 0,
- Threads = 1,
- Events = 2,
- TransferMemory = 3,
- Sessions = 4,
-
- Count,
-};
+
+using LimitableResource = Svc::LimitableResource;
constexpr bool IsValidResourceType(LimitableResource type) {
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) {
void KScheduler::ScheduleImpl() {
// First, clear the needs scheduling bool.
- m_state.needs_scheduling.store(false, std::memory_order_seq_cst);
+ m_state.needs_scheduling.store(false, std::memory_order_relaxed);
+ std::atomic_thread_fence(std::memory_order_seq_cst);
// Load the appropriate thread pointers for scheduling.
KThread* const cur_thread{GetCurrentThreadPointer(kernel)};
@@ -400,7 +401,8 @@ void KScheduler::ScheduleImpl() {
// If there aren't, we want to check if the highest priority thread is the same as the current
// thread.
if (highest_priority_thread == cur_thread) {
- // If they're the same, then we can just return.
+ // If they're the same, then we can just issue a memory barrier and return.
+ std::atomic_thread_fence(std::memory_order_seq_cst);
return;
}
@@ -476,7 +478,8 @@ void KScheduler::ScheduleImplFiber() {
// We failed to successfully do the context switch, and need to retry.
// Clear needs_scheduling.
- m_state.needs_scheduling.store(false, std::memory_order_seq_cst);
+ m_state.needs_scheduling.store(false, std::memory_order_relaxed);
+ std::atomic_thread_fence(std::memory_order_seq_cst);
// Refresh the highest priority thread.
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:
// Release an instance of the lock.
if ((--lock_count) == 0) {
+ // Perform a memory barrier here.
+ std::atomic_thread_fence(std::memory_order_seq_cst);
+
// We're no longer going to hold the lock. Take note of what cores need scheduling.
const u64 cores_needing_scheduling =
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() {
void KSession::PostDestroy(uintptr_t arg) {
// Release the session count resource the owner process holds.
KProcess* owner = reinterpret_cast<KProcess*>(arg);
- owner->GetResourceLimit()->Release(LimitableResource::Sessions, 1);
+ owner->GetResourceLimit()->Release(LimitableResource::SessionCountMax, 1);
owner->Close();
}
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 {
KSharedMemory::KSharedMemory(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_} {}
KSharedMemory::~KSharedMemory() {
- kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemory, size);
+ kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemoryMax, size);
}
Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_,
@@ -35,7 +35,7 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o
KResourceLimit* reslimit = kernel.GetSystemResourceLimit();
// Reserve memory for ourselves.
- KScopedResourceReservation memory_reservation(reslimit, LimitableResource::PhysicalMemory,
+ KScopedResourceReservation memory_reservation(reslimit, LimitableResource::PhysicalMemoryMax,
size_);
R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
@@ -57,7 +57,7 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o
void KSharedMemory::Finalize() {
// Release the memory reservation.
- resource_limit->Release(LimitableResource::PhysicalMemory, size);
+ resource_limit->Release(LimitableResource::PhysicalMemoryMax, size);
resource_limit->Close();
// 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_
R_SUCCEED();
}
-Result KThread::InitializeDummyThread(KThread* thread) {
+Result KThread::InitializeDummyThread(KThread* thread, KProcess* owner) {
// Initialize the thread.
- R_TRY(thread->Initialize({}, {}, {}, DummyThreadPriority, 3, {}, ThreadType::Dummy));
+ R_TRY(thread->Initialize({}, {}, {}, DummyThreadPriority, 3, owner, ThreadType::Dummy));
// Initialize emulation parameters.
thread->stack_parameters.disable_count = 0;
@@ -303,7 +303,7 @@ void KThread::PostDestroy(uintptr_t arg) {
const bool resource_limit_release_hint = (arg & 1);
const s64 hint_value = (resource_limit_release_hint ? 0 : 1);
if (owner != nullptr) {
- owner->GetResourceLimit()->Release(LimitableResource::Threads, 1, hint_value);
+ owner->GetResourceLimit()->Release(LimitableResource::ThreadCountMax, 1, hint_value);
owner->Close();
}
}
@@ -1054,7 +1054,7 @@ void KThread::Exit() {
// Release the thread resource hint, running thread count from parent.
if (parent != nullptr) {
- parent->GetResourceLimit()->Release(Kernel::LimitableResource::Threads, 0, 1);
+ parent->GetResourceLimit()->Release(Kernel::LimitableResource::ThreadCountMax, 0, 1);
resource_limit_release_hint = true;
parent->DecrementRunningThreadCount();
}
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:
static void PostDestroy(uintptr_t arg);
- [[nodiscard]] static Result InitializeDummyThread(KThread* thread);
+ [[nodiscard]] static Result InitializeDummyThread(KThread* thread, KProcess* owner);
[[nodiscard]] static Result InitializeMainThread(Core::System& system, KThread* thread,
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() {
void KTransferMemory::PostDestroy(uintptr_t arg) {
KProcess* owner = reinterpret_cast<KProcess*>(arg);
- owner->GetResourceLimit()->Release(LimitableResource::TransferMemory, 1);
+ owner->GetResourceLimit()->Release(LimitableResource::TransferMemoryCountMax, 1);
owner->Close();
}
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 {
pt_heap_region.GetSize());
}
- RegisterHostThread();
+ RegisterHostThread(nullptr);
default_service_thread = &CreateServiceThread(kernel, "DefaultServiceThread");
}
@@ -229,18 +229,22 @@ struct KernelCore::Impl {
const auto kernel_size{sizes.second};
// If setting the default system values fails, then something seriously wrong has occurred.
- ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, total_size)
+ ASSERT(
+ system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemoryMax, total_size)
+ .IsSuccess());
+ ASSERT(system_resource_limit->SetLimitValue(LimitableResource::ThreadCountMax, 800)
.IsSuccess());
- ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess());
- ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess());
- ASSERT(system_resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200)
+ ASSERT(system_resource_limit->SetLimitValue(LimitableResource::EventCountMax, 900)
.IsSuccess());
- ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess());
- system_resource_limit->Reserve(LimitableResource::PhysicalMemory, kernel_size);
+ ASSERT(system_resource_limit->SetLimitValue(LimitableResource::TransferMemoryCountMax, 200)
+ .IsSuccess());
+ ASSERT(system_resource_limit->SetLimitValue(LimitableResource::SessionCountMax, 1133)
+ .IsSuccess());
+ system_resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, kernel_size);
// Reserve secure applet memory, introduced in firmware 5.0.0
constexpr u64 secure_applet_memory_size{4_MiB};
- ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory,
+ ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemoryMax,
secure_applet_memory_size));
}
@@ -373,15 +377,18 @@ struct KernelCore::Impl {
}
// Gets the dummy KThread for the caller, allocating a new one if this is the first time
- KThread* GetHostDummyThread() {
+ KThread* GetHostDummyThread(KThread* existing_thread) {
auto initialize = [this](KThread* thread) {
- ASSERT(KThread::InitializeDummyThread(thread).IsSuccess());
+ ASSERT(KThread::InitializeDummyThread(thread, nullptr).IsSuccess());
thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId()));
return thread;
};
- thread_local auto raw_thread = KThread(system.Kernel());
- thread_local auto thread = initialize(&raw_thread);
+ thread_local KThread raw_thread{system.Kernel()};
+ thread_local KThread* thread = nullptr;
+ if (thread == nullptr) {
+ thread = (existing_thread == nullptr) ? initialize(&raw_thread) : existing_thread;
+ }
return thread;
}
@@ -396,9 +403,9 @@ struct KernelCore::Impl {
}
/// Registers a new host thread by allocating a host thread ID for it
- void RegisterHostThread() {
+ void RegisterHostThread(KThread* existing_thread) {
[[maybe_unused]] const auto this_id = GetHostThreadId();
- [[maybe_unused]] const auto dummy_thread = GetHostDummyThread();
+ [[maybe_unused]] const auto dummy_thread = GetHostDummyThread(existing_thread);
}
[[nodiscard]] u32 GetCurrentHostThreadID() {
@@ -429,7 +436,7 @@ struct KernelCore::Impl {
KThread* GetCurrentEmuThread() {
const auto thread_id = GetCurrentHostThreadID();
if (thread_id >= Core::Hardware::NUM_CPU_CORES) {
- return GetHostDummyThread();
+ return GetHostDummyThread(nullptr);
}
return current_thread;
@@ -1120,8 +1127,12 @@ void KernelCore::RegisterCoreThread(std::size_t core_id) {
impl->RegisterCoreThread(core_id);
}
-void KernelCore::RegisterHostThread() {
- impl->RegisterHostThread();
+void KernelCore::RegisterHostThread(KThread* existing_thread) {
+ impl->RegisterHostThread(existing_thread);
+
+ if (existing_thread != nullptr) {
+ ASSERT(GetCurrentEmuThread() == existing_thread);
+ }
}
u32 KernelCore::GetCurrentHostThreadID() const {
@@ -1196,16 +1207,28 @@ void KernelCore::Suspend(bool suspended) {
const bool should_suspend{exception_exited || suspended};
const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable;
- for (auto* process : GetProcessList()) {
- process->SetActivity(activity);
+ std::vector<KScopedAutoObject<KThread>> process_threads;
+ {
+ KScopedSchedulerLock sl{*this};
+
+ if (auto* process = CurrentProcess(); process != nullptr) {
+ process->SetActivity(activity);
+
+ if (!should_suspend) {
+ // Runnable now; no need to wait.
+ return;
+ }
- if (should_suspend) {
- // Wait for execution to stop
for (auto* thread : process->GetThreadList()) {
- thread->WaitUntilSuspended();
+ process_threads.emplace_back(thread);
}
}
}
+
+ // Wait for execution to stop.
+ for (auto& thread : process_threads) {
+ thread->WaitUntilSuspended();
+ }
}
void 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:
void RegisterCoreThread(std::size_t core_id);
/// Register the current thread as a non CPU core thread.
- void RegisterHostThread();
+ void RegisterHostThread(KThread* existing_thread = nullptr);
/// Gets the virtual memory manager for the kernel.
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 {
PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_)
: core_index{core_index_}, system{system_}, scheduler{scheduler_} {
-#ifdef ARCHITECTURE_x86_64
+#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
// TODO(bunnei): Initialization relies on a core being available. We may later replace this with
// a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager.
auto& kernel = system.Kernel();
@@ -26,7 +26,7 @@ PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KSche
PhysicalCore::~PhysicalCore() = default;
void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) {
-#ifdef ARCHITECTURE_x86_64
+#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
auto& kernel = system.Kernel();
if (!is_64_bit) {
// 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:
private:
KernelCore& kernel;
- std::jthread m_thread;
+ std::jthread m_host_thread;
std::mutex m_session_mutex;
std::map<KServerSession*, std::shared_ptr<SessionRequestManager>> m_sessions;
KEvent* m_wakeup_event;
- KProcess* m_process;
+ KThread* m_thread;
std::atomic<bool> m_shutdown_requested;
const std::string m_service_name;
};
@@ -132,7 +132,7 @@ void ServiceThread::Impl::SessionClosed(KServerSession* server_session,
void ServiceThread::Impl::LoopProcess() {
Common::SetCurrentThreadName(m_service_name.c_str());
- kernel.RegisterHostThread();
+ kernel.RegisterHostThread(m_thread);
while (!m_shutdown_requested.load()) {
WaitAndProcessImpl();
@@ -160,7 +160,7 @@ ServiceThread::Impl::~Impl() {
// Shut down the processing thread.
m_shutdown_requested.store(true);
m_wakeup_event->Signal();
- m_thread.join();
+ m_host_thread.join();
// Lock mutex.
m_session_mutex.lock();
@@ -177,33 +177,22 @@ ServiceThread::Impl::~Impl() {
m_wakeup_event->GetReadableEvent().Close();
m_wakeup_event->Close();
- // Close process.
- m_process->Close();
+ // Close thread.
+ m_thread->Close();
}
ServiceThread::Impl::Impl(KernelCore& kernel_, const std::string& service_name)
: kernel{kernel_}, m_service_name{service_name} {
- // Initialize process.
- m_process = KProcess::Create(kernel);
- KProcess::Initialize(m_process, kernel.System(), service_name,
- KProcess::ProcessType::KernelInternal, kernel.GetSystemResourceLimit());
-
- // Reserve a new event from the process resource limit
- KScopedResourceReservation event_reservation(m_process, LimitableResource::Events);
- ASSERT(event_reservation.Succeeded());
-
// Initialize event.
m_wakeup_event = KEvent::Create(kernel);
- m_wakeup_event->Initialize(m_process);
-
- // Commit the event reservation.
- event_reservation.Commit();
+ m_wakeup_event->Initialize(nullptr);
- // Register the event.
- KEvent::Register(kernel, m_wakeup_event);
+ // Initialize thread.
+ m_thread = KThread::Create(kernel);
+ ASSERT(KThread::InitializeDummyThread(m_thread, nullptr).IsSuccess());
// Start thread.
- m_thread = std::jthread([this] { LoopProcess(); });
+ m_host_thread = std::jthread([this] { LoopProcess(); });
}
ServiceThread::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
// Reserve a new session from the process resource limit.
// FIXME: LimitableResource_SessionCountMax
- KScopedResourceReservation session_reservation(&process, LimitableResource::Sessions);
+ KScopedResourceReservation session_reservation(&process, LimitableResource::SessionCountMax);
if (session_reservation.Succeeded()) {
session = T::Create(system.Kernel());
} else {
@@ -298,7 +298,7 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien
// We successfully allocated a session, so add the object we allocated to the resource
// limit.
- // system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::Sessions, 1);
+ // system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::SessionCountMax, 1);
}
// Check that we successfully created a session.
@@ -656,27 +656,12 @@ static Result ArbitrateUnlock32(Core::System& system, u32 address) {
return ArbitrateUnlock(system, address);
}
-enum class BreakType : u32 {
- Panic = 0,
- AssertionFailed = 1,
- PreNROLoad = 3,
- PostNROLoad = 4,
- PreNROUnload = 5,
- PostNROUnload = 6,
- CppException = 7,
-};
-
-struct BreakReason {
- union {
- u32 raw;
- BitField<0, 30, BreakType> break_type;
- BitField<31, 1, u32> signal_debugger;
- };
-};
-
/// Break program execution
static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
- BreakReason break_reason{reason};
+ BreakReason break_reason =
+ static_cast<BreakReason>(reason & ~static_cast<u32>(BreakReason::NotificationOnlyFlag));
+ bool notification_only = (reason & static_cast<u32>(BreakReason::NotificationOnlyFlag)) != 0;
+
bool has_dumped_buffer{};
std::vector<u8> debug_buffer;
@@ -705,57 +690,56 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
}
has_dumped_buffer = true;
};
- switch (break_reason.break_type) {
- case BreakType::Panic:
- LOG_CRITICAL(Debug_Emulated, "Signalling debugger, PANIC! info1=0x{:016X}, info2=0x{:016X}",
- info1, info2);
+ switch (break_reason) {
+ case BreakReason::Panic:
+ LOG_CRITICAL(Debug_Emulated, "Userspace PANIC! info1=0x{:016X}, info2=0x{:016X}", info1,
+ info2);
handle_debug_buffer(info1, info2);
break;
- case BreakType::AssertionFailed:
- LOG_CRITICAL(Debug_Emulated,
- "Signalling debugger, Assertion failed! info1=0x{:016X}, info2=0x{:016X}",
+ case BreakReason::Assert:
+ LOG_CRITICAL(Debug_Emulated, "Userspace Assertion failed! info1=0x{:016X}, info2=0x{:016X}",
info1, info2);
handle_debug_buffer(info1, info2);
break;
- case BreakType::PreNROLoad:
- LOG_WARNING(
- Debug_Emulated,
- "Signalling debugger, Attempting to load an NRO at 0x{:016X} with size 0x{:016X}",
- info1, info2);
+ case BreakReason::User:
+ LOG_WARNING(Debug_Emulated, "Userspace Break! 0x{:016X} with size 0x{:016X}", info1, info2);
+ handle_debug_buffer(info1, info2);
break;
- case BreakType::PostNROLoad:
- LOG_WARNING(Debug_Emulated,
- "Signalling debugger, Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
- info2);
+ case BreakReason::PreLoadDll:
+ LOG_INFO(Debug_Emulated,
+ "Userspace Attempting to load an NRO at 0x{:016X} with size 0x{:016X}", info1,
+ info2);
break;
- case BreakType::PreNROUnload:
- LOG_WARNING(
- Debug_Emulated,
- "Signalling debugger, Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}",
- info1, info2);
+ case BreakReason::PostLoadDll:
+ LOG_INFO(Debug_Emulated, "Userspace Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
+ info2);
break;
- case BreakType::PostNROUnload:
- LOG_WARNING(Debug_Emulated,
- "Signalling debugger, Unloaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
- info2);
+ case BreakReason::PreUnloadDll:
+ LOG_INFO(Debug_Emulated,
+ "Userspace Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}", info1,
+ info2);
break;
- case BreakType::CppException:
+ case BreakReason::PostUnloadDll:
+ LOG_INFO(Debug_Emulated, "Userspace Unloaded an NRO at 0x{:016X} with size 0x{:016X}",
+ info1, info2);
+ break;
+ case BreakReason::CppException:
LOG_CRITICAL(Debug_Emulated, "Signalling debugger. Uncaught C++ exception encountered.");
break;
default:
LOG_WARNING(
Debug_Emulated,
- "Signalling debugger, Unknown break reason {}, info1=0x{:016X}, info2=0x{:016X}",
- static_cast<u32>(break_reason.break_type.Value()), info1, info2);
+ "Signalling debugger, Unknown break reason {:#X}, info1=0x{:016X}, info2=0x{:016X}",
+ reason, info1, info2);
handle_debug_buffer(info1, info2);
break;
}
- system.GetReporter().SaveSvcBreakReport(
- static_cast<u32>(break_reason.break_type.Value()), break_reason.signal_debugger.As<bool>(),
- info1, info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt);
+ system.GetReporter().SaveSvcBreakReport(reason, notification_only, info1, info2,
+ has_dumped_buffer ? std::make_optional(debug_buffer)
+ : std::nullopt);
- if (!break_reason.signal_debugger) {
+ if (!notification_only) {
LOG_CRITICAL(
Debug_Emulated,
"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
auto& memory{system.Memory()};
const auto memory_info{process->PageTable().QueryInfo(address).GetSvcMemoryInfo()};
- memory.Write64(memory_info_address + 0x00, memory_info.addr);
+ memory.Write64(memory_info_address + 0x00, memory_info.base_address);
memory.Write64(memory_info_address + 0x08, memory_info.size);
memory.Write32(memory_info_address + 0x10, static_cast<u32>(memory_info.state) & 0xff);
- memory.Write32(memory_info_address + 0x14, static_cast<u32>(memory_info.attr));
- memory.Write32(memory_info_address + 0x18, static_cast<u32>(memory_info.perm));
- memory.Write32(memory_info_address + 0x1c, memory_info.ipc_refcount);
- memory.Write32(memory_info_address + 0x20, memory_info.device_refcount);
+ memory.Write32(memory_info_address + 0x14, static_cast<u32>(memory_info.attribute));
+ memory.Write32(memory_info_address + 0x18, static_cast<u32>(memory_info.permission));
+ memory.Write32(memory_info_address + 0x1c, memory_info.ipc_count);
+ memory.Write32(memory_info_address + 0x20, memory_info.device_count);
memory.Write32(memory_info_address + 0x24, 0);
// 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
// Reserve a new thread from the process resource limit (waiting up to 100ms).
KScopedResourceReservation thread_reservation(
- kernel.CurrentProcess(), LimitableResource::Threads, 1,
+ kernel.CurrentProcess(), LimitableResource::ThreadCountMax, 1,
system.CoreTiming().GetGlobalTimeNs().count() + 100000000);
if (!thread_reservation.Succeeded()) {
LOG_ERROR(Kernel_SVC, "Could not reserve a new thread");
@@ -2344,7 +2328,7 @@ static Result CreateTransferMemory(Core::System& system, Handle* out, VAddr addr
// Reserve a new transfer memory from the process resource limit.
KScopedResourceReservation trmem_reservation(kernel.CurrentProcess(),
- LimitableResource::TransferMemory);
+ LimitableResource::TransferMemoryCountMax);
R_UNLESS(trmem_reservation.Succeeded(), ResultLimitReached);
// Create the transfer memory.
@@ -2496,7 +2480,7 @@ static Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_r
// Reserve a new event from the process resource limit
KScopedResourceReservation event_reservation(kernel.CurrentProcess(),
- LimitableResource::Events);
+ LimitableResource::EventCountMax);
R_UNLESS(event_reservation.Succeeded(), ResultLimitReached);
// Create a new event.
@@ -2539,11 +2523,6 @@ static Result CreateEvent32(Core::System& system, Handle* out_write, Handle* out
static Result GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) {
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type);
- // This function currently only allows retrieving a process' status.
- enum class InfoType {
- Status,
- };
-
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
if (process.IsNull()) {
@@ -2552,9 +2531,9 @@ static Result GetProcessInfo(Core::System& system, u64* out, Handle process_hand
return ResultInvalidHandle;
}
- const auto info_type = static_cast<InfoType>(type);
- if (info_type != InfoType::Status) {
- LOG_ERROR(Kernel_SVC, "Expected info_type to be Status but got {} instead", type);
+ const auto info_type = static_cast<ProcessInfoType>(type);
+ if (info_type != ProcessInfoType::ProcessState) {
+ LOG_ERROR(Kernel_SVC, "Expected info_type to be ProcessState but got {} instead", type);
return ResultInvalidEnumValue;
}
@@ -2722,14 +2701,24 @@ static Result GetThreadList(Core::System& system, u32* out_num_threads, VAddr ou
return ResultSuccess;
}
-static Result FlushProcessDataCache32([[maybe_unused]] Core::System& system,
- [[maybe_unused]] Handle handle, [[maybe_unused]] u32 address,
- [[maybe_unused]] u32 size) {
- // Note(Blinkhawk): For emulation purposes of the data cache this is mostly a no-op,
- // as all emulation is done in the same cache level in host architecture, thus data cache
- // does not need flushing.
- LOG_DEBUG(Kernel_SVC, "called");
- return ResultSuccess;
+static Result FlushProcessDataCache32(Core::System& system, Handle process_handle, u64 address,
+ u64 size) {
+ // Validate address/size.
+ R_UNLESS(size > 0, ResultInvalidSize);
+ R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory);
+ R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory);
+
+ // Get the process from its handle.
+ KScopedAutoObject process =
+ system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle);
+ R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
+
+ // Verify the region is within range.
+ auto& page_table = process->PageTable();
+ R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
+
+ // Perform the operation.
+ R_RETURN(system.Memory().FlushDataCache(*process, address, size));
}
namespace {
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 @@
namespace Kernel::Svc {
+using Handle = u32;
+
enum class MemoryState : u32 {
Free = 0x00,
Io = 0x01,
@@ -55,17 +57,6 @@ enum class MemoryPermission : u32 {
};
DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission);
-struct MemoryInfo {
- u64 addr{};
- u64 size{};
- MemoryState state{};
- MemoryAttribute attr{};
- MemoryPermission perm{};
- u32 ipc_refcount{};
- u32 device_refcount{};
- u32 padding{};
-};
-
enum class SignalType : u32 {
Signal = 0,
SignalAndIncrementIfEqual = 1,
@@ -124,7 +115,57 @@ enum class ProcessExitReason : u32 {
constexpr inline size_t ThreadLocalRegionSize = 0x200;
-// Debug types.
+struct PageInfo {
+ u32 flags;
+};
+
+// Info Types.
+enum class InfoType : u32 {
+ CoreMask = 0,
+ PriorityMask = 1,
+ AliasRegionAddress = 2,
+ AliasRegionSize = 3,
+ HeapRegionAddress = 4,
+ HeapRegionSize = 5,
+ TotalMemorySize = 6,
+ UsedMemorySize = 7,
+ DebuggerAttached = 8,
+ ResourceLimit = 9,
+ IdleTickCount = 10,
+ RandomEntropy = 11,
+ AslrRegionAddress = 12,
+ AslrRegionSize = 13,
+ StackRegionAddress = 14,
+ StackRegionSize = 15,
+ SystemResourceSizeTotal = 16,
+ SystemResourceSizeUsed = 17,
+ ProgramId = 18,
+ InitialProcessIdRange = 19,
+ UserExceptionContextAddress = 20,
+ TotalNonSystemMemorySize = 21,
+ UsedNonSystemMemorySize = 22,
+ IsApplication = 23,
+ FreeThreadCount = 24,
+ ThreadTickCount = 25,
+ IsSvcPermitted = 26,
+
+ MesosphereMeta = 65000,
+ MesosphereCurrentProcess = 65001,
+};
+
+enum class BreakReason : u32 {
+ Panic = 0,
+ Assert = 1,
+ User = 2,
+ PreLoadDll = 3,
+ PostLoadDll = 4,
+ PreUnloadDll = 5,
+ PostUnloadDll = 6,
+ CppException = 7,
+
+ NotificationOnlyFlag = 0x80000000,
+};
+
enum class DebugEvent : u32 {
CreateProcess = 0,
CreateThread = 1,
@@ -133,6 +174,14 @@ enum class DebugEvent : u32 {
Exception = 4,
};
+enum class DebugThreadParam : u32 {
+ Priority = 0,
+ State = 1,
+ IdealCore = 2,
+ CurrentCore = 3,
+ AffinityMask = 4,
+};
+
enum class DebugException : u32 {
UndefinedInstruction = 0,
InstructionAbort = 1,
@@ -146,4 +195,401 @@ enum class DebugException : u32 {
MemorySystemError = 9,
};
+enum class DebugEventFlag : u32 {
+ Stopped = (1u << 0),
+};
+
+enum class BreakPointType : u32 {
+ HardwareInstruction = 0,
+ HardwareData = 1,
+};
+
+enum class HardwareBreakPointRegisterName : u32 {
+ I0 = 0,
+ I1 = 1,
+ I2 = 2,
+ I3 = 3,
+ I4 = 4,
+ I5 = 5,
+ I6 = 6,
+ I7 = 7,
+ I8 = 8,
+ I9 = 9,
+ I10 = 10,
+ I11 = 11,
+ I12 = 12,
+ I13 = 13,
+ I14 = 14,
+ I15 = 15,
+ D0 = 16,
+ D1 = 17,
+ D2 = 18,
+ D3 = 19,
+ D4 = 20,
+ D5 = 21,
+ D6 = 22,
+ D7 = 23,
+ D8 = 24,
+ D9 = 25,
+ D10 = 26,
+ D11 = 27,
+ D12 = 28,
+ D13 = 29,
+ D14 = 30,
+ D15 = 31,
+};
+
+namespace lp64 {
+struct LastThreadContext {
+ u64 fp;
+ u64 sp;
+ u64 lr;
+ u64 pc;
+};
+
+struct PhysicalMemoryInfo {
+ PAddr physical_address;
+ u64 virtual_address;
+ u64 size;
+};
+
+struct DebugInfoCreateProcess {
+ u64 program_id;
+ u64 process_id;
+ std::array<char, 0xC> name;
+ u32 flags;
+ u64 user_exception_context_address; // 5.0.0+
+};
+
+struct DebugInfoCreateThread {
+ u64 thread_id;
+ u64 tls_address;
+ // Removed in 11.0.0 u64 entrypoint;
+};
+
+struct DebugInfoExitProcess {
+ ProcessExitReason reason;
+};
+
+struct DebugInfoExitThread {
+ ThreadExitReason reason;
+};
+
+struct DebugInfoUndefinedInstructionException {
+ u32 insn;
+};
+
+struct DebugInfoDataAbortException {
+ u64 address;
+};
+
+struct DebugInfoAlignmentFaultException {
+ u64 address;
+};
+
+struct DebugInfoBreakPointException {
+ BreakPointType type;
+ u64 address;
+};
+
+struct DebugInfoUserBreakException {
+ BreakReason break_reason;
+ u64 address;
+ u64 size;
+};
+
+struct DebugInfoDebuggerBreakException {
+ std::array<u64, 4> active_thread_ids;
+};
+
+struct DebugInfoUndefinedSystemCallException {
+ u32 id;
+};
+
+union DebugInfoSpecificException {
+ DebugInfoUndefinedInstructionException undefined_instruction;
+ DebugInfoDataAbortException data_abort;
+ DebugInfoAlignmentFaultException alignment_fault;
+ DebugInfoBreakPointException break_point;
+ DebugInfoUserBreakException user_break;
+ DebugInfoDebuggerBreakException debugger_break;
+ DebugInfoUndefinedSystemCallException undefined_system_call;
+ u64 raw;
+};
+
+struct DebugInfoException {
+ DebugException type;
+ u64 address;
+ DebugInfoSpecificException specific;
+};
+
+union DebugInfo {
+ DebugInfoCreateProcess create_process;
+ DebugInfoCreateThread create_thread;
+ DebugInfoExitProcess exit_process;
+ DebugInfoExitThread exit_thread;
+ DebugInfoException exception;
+};
+
+struct DebugEventInfo {
+ DebugEvent type;
+ u32 flags;
+ u64 thread_id;
+ DebugInfo info;
+};
+static_assert(sizeof(DebugEventInfo) >= 0x40);
+
+struct SecureMonitorArguments {
+ std::array<u64, 8> r;
+};
+static_assert(sizeof(SecureMonitorArguments) == 0x40);
+} // namespace lp64
+
+namespace ilp32 {
+struct LastThreadContext {
+ u32 fp;
+ u32 sp;
+ u32 lr;
+ u32 pc;
+};
+
+struct PhysicalMemoryInfo {
+ PAddr physical_address;
+ u32 virtual_address;
+ u32 size;
+};
+
+struct DebugInfoCreateProcess {
+ u64 program_id;
+ u64 process_id;
+ std::array<char, 0xC> name;
+ u32 flags;
+ u32 user_exception_context_address; // 5.0.0+
+};
+
+struct DebugInfoCreateThread {
+ u64 thread_id;
+ u32 tls_address;
+ // Removed in 11.0.0 u32 entrypoint;
+};
+
+struct DebugInfoExitProcess {
+ ProcessExitReason reason;
+};
+
+struct DebugInfoExitThread {
+ ThreadExitReason reason;
+};
+
+struct DebugInfoUndefinedInstructionException {
+ u32 insn;
+};
+
+struct DebugInfoDataAbortException {
+ u32 address;
+};
+
+struct DebugInfoAlignmentFaultException {
+ u32 address;
+};
+
+struct DebugInfoBreakPointException {
+ BreakPointType type;
+ u32 address;
+};
+
+struct DebugInfoUserBreakException {
+ BreakReason break_reason;
+ u32 address;
+ u32 size;
+};
+
+struct DebugInfoDebuggerBreakException {
+ std::array<u64, 4> active_thread_ids;
+};
+
+struct DebugInfoUndefinedSystemCallException {
+ u32 id;
+};
+
+union DebugInfoSpecificException {
+ DebugInfoUndefinedInstructionException undefined_instruction;
+ DebugInfoDataAbortException data_abort;
+ DebugInfoAlignmentFaultException alignment_fault;
+ DebugInfoBreakPointException break_point;
+ DebugInfoUserBreakException user_break;
+ DebugInfoDebuggerBreakException debugger_break;
+ DebugInfoUndefinedSystemCallException undefined_system_call;
+ u64 raw;
+};
+
+struct DebugInfoException {
+ DebugException type;
+ u32 address;
+ DebugInfoSpecificException specific;
+};
+
+union DebugInfo {
+ DebugInfoCreateProcess create_process;
+ DebugInfoCreateThread create_thread;
+ DebugInfoExitProcess exit_process;
+ DebugInfoExitThread exit_thread;
+ DebugInfoException exception;
+};
+
+struct DebugEventInfo {
+ DebugEvent type;
+ u32 flags;
+ u64 thread_id;
+ DebugInfo info;
+};
+
+struct SecureMonitorArguments {
+ std::array<u32, 8> r;
+};
+static_assert(sizeof(SecureMonitorArguments) == 0x20);
+} // namespace ilp32
+
+struct ThreadContext {
+ std::array<u64, 29> r;
+ u64 fp;
+ u64 lr;
+ u64 sp;
+ u64 pc;
+ u32 pstate;
+ u32 padding;
+ std::array<u128, 32> v;
+ u32 fpcr;
+ u32 fpsr;
+ u64 tpidr;
+};
+static_assert(sizeof(ThreadContext) == 0x320);
+
+struct MemoryInfo {
+ u64 base_address;
+ u64 size;
+ MemoryState state;
+ MemoryAttribute attribute;
+ MemoryPermission permission;
+ u32 ipc_count;
+ u32 device_count;
+ u32 padding;
+};
+
+enum class LimitableResource : u32 {
+ PhysicalMemoryMax = 0,
+ ThreadCountMax = 1,
+ EventCountMax = 2,
+ TransferMemoryCountMax = 3,
+ SessionCountMax = 4,
+ Count,
+};
+
+enum class IoPoolType : u32 {
+ // Not supported.
+ Count = 0,
+};
+
+enum class MemoryMapping : u32 {
+ IoRegister = 0,
+ Uncached = 1,
+ Memory = 2,
+};
+
+enum class KernelDebugType : u32 {
+ Thread = 0,
+ ThreadCallStack = 1,
+ KernelObject = 2,
+ Handle_ = 3,
+ Memory = 4,
+ PageTable = 5,
+ CpuUtilization = 6,
+ Process = 7,
+ SuspendProcess = 8,
+ ResumeProcess = 9,
+ Port = 10,
+};
+
+enum class KernelTraceState : u32 {
+ Disabled = 0,
+ Enabled = 1,
+};
+
+enum class CodeMemoryOperation : u32 {
+ Map = 0,
+ MapToOwner = 1,
+ Unmap = 2,
+ UnmapFromOwner = 3,
+};
+
+enum class InterruptType : u32 {
+ Edge = 0,
+ Level = 1,
+};
+
+enum class DeviceName {
+ Afi = 0,
+ Avpc = 1,
+ Dc = 2,
+ Dcb = 3,
+ Hc = 4,
+ Hda = 5,
+ Isp2 = 6,
+ MsencNvenc = 7,
+ Nv = 8,
+ Nv2 = 9,
+ Ppcs = 10,
+ Sata = 11,
+ Vi = 12,
+ Vic = 13,
+ XusbHost = 14,
+ XusbDev = 15,
+ Tsec = 16,
+ Ppcs1 = 17,
+ Dc1 = 18,
+ Sdmmc1a = 19,
+ Sdmmc2a = 20,
+ Sdmmc3a = 21,
+ Sdmmc4a = 22,
+ Isp2b = 23,
+ Gpu = 24,
+ Gpub = 25,
+ Ppcs2 = 26,
+ Nvdec = 27,
+ Ape = 28,
+ Se = 29,
+ Nvjpg = 30,
+ Hc1 = 31,
+ Se1 = 32,
+ Axiap = 33,
+ Etr = 34,
+ Tsecb = 35,
+ Tsec1 = 36,
+ Tsecb1 = 37,
+ Nvdec1 = 38,
+ Count,
+};
+
+enum class SystemInfoType : u32 {
+ TotalPhysicalMemorySize = 0,
+ UsedPhysicalMemorySize = 1,
+ InitialProcessIdRange = 2,
+};
+
+enum class ProcessInfoType : u32 {
+ ProcessState = 0,
+};
+
+struct CreateProcessParameter {
+ std::array<char, 12> name;
+ u32 version;
+ u64 program_id;
+ u64 code_address;
+ s32 code_num_pages;
+ u32 flags;
+ Handle reslimit;
+ s32 system_resource_num_pages;
+};
+static_assert(sizeof(CreateProcessParameter) == 0x30);
+
} // 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) {
FuncReturn(system, retval);
}
+// Used by Invalidate/Store/FlushProcessDataCache32
+template <Result func(Core::System&, Handle, u64, u64)>
+void SvcWrap32(Core::System& system) {
+ const u64 address = (Param(system, 3) << 32) | Param(system, 2);
+ const u64 size = (Param(system, 4) << 32) | Param(system, 1);
+ FuncReturn32(system, func(system, Param32(system, 0), address, size).raw);
+}
+
} // 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 {
Loader = 9,
CMIF = 10,
HIPC = 11,
+ TMA = 12,
+ DMNT = 13,
+ GDS = 14,
PM = 15,
NS = 16,
+ BSDSockets = 17,
HTC = 18,
+ TSC = 19,
NCMContent = 20,
SM = 21,
RO = 22,
+ GC = 23,
SDMMC = 24,
OVLN = 25,
SPL = 26,
+ Socket = 27,
+ HTCLOW = 29,
+ DDSF = 30,
+ HTCFS = 31,
+ Async = 32,
+ Util = 33,
+ TIPC = 35,
+ ANIF = 37,
ETHC = 100,
I2C = 101,
GPIO = 102,
UART = 103,
+ CPAD = 104,
Settings = 105,
+ FTM = 106,
WLAN = 107,
XCD = 108,
+ TMP451 = 109,
NIFM = 110,
Hwopus = 111,
+ LSM6DS3 = 112,
Bluetooth = 113,
VI = 114,
NFP = 115,
Time = 116,
FGM = 117,
OE = 118,
+ BH1730FVC = 119,
PCIe = 120,
Friends = 121,
BCAT = 122,
@@ -65,7 +84,7 @@ enum class ErrorModule : u32 {
AHID = 130,
Qlaunch = 132,
PCV = 133,
- OMM = 134,
+ USBPD = 134,
BPC = 135,
PSM = 136,
NIM = 137,
@@ -75,18 +94,22 @@ enum class ErrorModule : u32 {
NSD = 141,
PCTL = 142,
BTM = 143,
+ LA = 144,
ETicket = 145,
NGC = 146,
ERPT = 147,
APM = 148,
+ CEC = 149,
Profiler = 150,
ErrorUpload = 151,
+ LIDBE = 152,
Audio = 153,
NPNS = 154,
NPNSHTTPSTREAM = 155,
ARP = 157,
SWKBD = 158,
BOOT = 159,
+ NetDiag = 160,
NFCMifare = 161,
UserlandAssert = 162,
Fatal = 163,
@@ -94,17 +117,68 @@ enum class ErrorModule : u32 {
SPSM = 165,
BGTC = 167,
UserlandCrash = 168,
+ SASBUS = 169,
+ PI = 170,
+ AudioCtrl = 172,
+ LBL = 173,
+ JIT = 175,
+ HDCP = 176,
+ OMM = 177,
+ PDM = 178,
+ OLSC = 179,
SREPO = 180,
Dauth = 181,
+ STDFU = 182,
+ DBG = 183,
+ DHCPS = 186,
+ SPI = 187,
+ AVM = 188,
+ PWM = 189,
+ RTC = 191,
+ Regulator = 192,
+ LED = 193,
+ SIO = 195,
+ PCM = 196,
+ CLKRST = 197,
+ POWCTL = 198,
+ AudioOld = 201,
HID = 202,
LDN = 203,
+ CS = 204,
Irsensor = 205,
Capture = 206,
Manu = 208,
ATK = 209,
+ WEB = 210,
+ LCS = 211,
GRC = 212,
+ Repair = 213,
+ Album = 214,
+ RID = 215,
Migration = 216,
MigrationLdcServ = 217,
+ HIDBUS = 218,
+ ENS = 219,
+ WebSocket = 223,
+ DCDMTP = 227,
+ PGL = 228,
+ Notification = 229,
+ INS = 230,
+ LP2P = 231,
+ RCD = 232,
+ LCM40607 = 233,
+ PRC = 235,
+ TMAHTC = 237,
+ ECTX = 238,
+ MNPP = 239,
+ HSHL = 240,
+ CAPMTP = 242,
+ DP2HDMI = 244,
+ Cradle = 245,
+ SProfile = 246,
+ NDRM = 250,
+ TSPM = 499,
+ DevMenu = 500,
GeneralWebApplet = 800,
WifiWebAuthApplet = 809,
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() {
break;
default:
UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", mode);
+ break;
}
}
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() {
}
default:
unimplemented_log();
+ break;
}
}
@@ -192,6 +193,7 @@ void PhotoViewer::Execute() {
break;
default:
UNIMPLEMENTED_MSG("Unimplemented PhotoViewer applet mode={:02X}!", mode);
+ break;
}
}
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() {
Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) {
// Reserve a new event from the process resource limit
Kernel::KScopedResourceReservation event_reservation(process,
- Kernel::LimitableResource::Events);
+ Kernel::LimitableResource::EventCountMax);
if (!event_reservation.Succeeded()) {
LOG_CRITICAL(Service, "Resource limit reached!");
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) {
return error_notifier_event;
case 2:
return unknown_event;
- default: {
+ default:
LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id);
+ return nullptr;
}
- }
- return nullptr;
}
} // 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) {
return sm_exception_breakpoint_pause_report_event;
case 3:
return error_notifier_event;
- default: {
+ default:
LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id);
+ return nullptr;
}
- }
- return nullptr;
}
} // 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() {
}
void BufferQueueCore::SignalDequeueCondition() {
+ dequeue_possible.store(true);
dequeue_condition.notify_all();
}
-bool BufferQueueCore::WaitForDequeueCondition() {
+bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock<std::mutex>& lk) {
if (is_shutting_down) {
return false;
}
- dequeue_condition.wait(mutex);
+ dequeue_condition.wait(lk, [&] { return dequeue_possible.load(); });
+ dequeue_possible.store(false);
return true;
}
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:
private:
void SignalDequeueCondition();
- bool WaitForDequeueCondition();
+ bool WaitForDequeueCondition(std::unique_lock<std::mutex>& lk);
s32 GetMinUndequeuedBufferCountLocked(bool async) const;
s32 GetMinMaxBufferCountLocked(bool async) const;
@@ -60,7 +60,8 @@ private:
BufferQueueDefs::SlotsType slots{};
std::vector<BufferItem> queue;
s32 override_max_buffer_count{};
- mutable std::condition_variable_any dequeue_condition;
+ std::condition_variable dequeue_condition;
+ std::atomic<bool> dequeue_possible{};
const bool use_async_buffer{}; // This is always disabled on HOS
bool dequeue_buffer_cannot_block{};
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) {
return Status::NoError;
}
-Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found,
- Status* return_flags) const {
+Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags,
+ std::unique_lock<std::mutex>& lk) const {
bool try_again = true;
while (try_again) {
@@ -214,7 +214,7 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found,
return Status::WouldBlock;
}
- if (!core->WaitForDequeueCondition()) {
+ if (!core->WaitForDequeueCondition(lk)) {
// We are no longer running
return Status::NoError;
}
@@ -237,7 +237,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool
Status return_flags = Status::NoError;
bool attached_by_consumer = false;
{
- std::scoped_lock lock{core->mutex};
+ std::unique_lock lock{core->mutex};
core->WaitWhileAllocatingLocked();
if (format == PixelFormat::NoFormat) {
@@ -248,7 +248,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool
usage |= core->consumer_usage_bit;
s32 found{};
- Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags);
+ Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags, lock);
if (status != Status::NoError) {
return status;
}
@@ -400,13 +400,13 @@ Status BufferQueueProducer::AttachBuffer(s32* out_slot,
return Status::BadValue;
}
- std::scoped_lock lock{core->mutex};
+ std::unique_lock lock{core->mutex};
core->WaitWhileAllocatingLocked();
Status return_flags = Status::NoError;
s32 found{};
- const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags);
+ const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags, lock);
if (status != Status::NoError) {
return status;
}
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:
private:
BufferQueueProducer(const BufferQueueProducer&) = delete;
- Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags) const;
+ Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags,
+ std::unique_lock<std::mutex>& lk) const;
Kernel::KEvent* buffer_wait_event{};
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,
}
UNIMPLEMENTED_MSG("command_type={}", ctx.GetCommandType());
+ break;
}
// 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) {
// once this is a proper process
// Reserve a new session from the process resource limit.
- Kernel::KScopedResourceReservation session_reservation(&process,
- Kernel::LimitableResource::Sessions);
+ Kernel::KScopedResourceReservation session_reservation(
+ &process, Kernel::LimitableResource::SessionCountMax);
ASSERT(session_reservation.Succeeded());
// 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) {
}
default:
ASSERT(false);
+ break;
}
return value + rule.transition_time + offset;
}
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 @@
#include "common/assert.h"
#include "common/atomic_ops.h"
+#include "common/cache_management.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/page_table.h"
@@ -329,6 +330,55 @@ struct Memory::Impl {
});
}
+ template <typename Callback>
+ Result PerformCacheOperation(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size,
+ Callback&& cb) {
+ class InvalidMemoryException : public std::exception {};
+
+ try {
+ WalkBlock(
+ process, dest_addr, size,
+ [&](const std::size_t block_size, const VAddr current_vaddr) {
+ LOG_ERROR(HW_Memory, "Unmapped cache maintenance @ {:#018X}", current_vaddr);
+ throw InvalidMemoryException();
+ },
+ [&](const std::size_t block_size, u8* const host_ptr) { cb(block_size, host_ptr); },
+ [&](const VAddr current_vaddr, const std::size_t block_size, u8* const host_ptr) {
+ system.GPU().FlushRegion(current_vaddr, block_size);
+ cb(block_size, host_ptr);
+ },
+ [](const std::size_t block_size) {});
+ } catch (InvalidMemoryException&) {
+ return Kernel::ResultInvalidCurrentMemory;
+ }
+
+ return ResultSuccess;
+ }
+
+ Result InvalidateDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size) {
+ auto perform = [&](const std::size_t block_size, u8* const host_ptr) {
+ // Do nothing; this operation (dc ivac) cannot be supported
+ // from EL0
+ };
+ return PerformCacheOperation(process, dest_addr, size, perform);
+ }
+
+ Result StoreDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size) {
+ auto perform = [&](const std::size_t block_size, u8* const host_ptr) {
+ // dc cvac: Store to point of coherency
+ Common::DataCacheLineCleanByVAToPoC(host_ptr, block_size);
+ };
+ return PerformCacheOperation(process, dest_addr, size, perform);
+ }
+
+ Result FlushDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size) {
+ auto perform = [&](const std::size_t block_size, u8* const host_ptr) {
+ // dc civac: Store to point of coherency, and invalidate from cache
+ Common::DataCacheLineCleanAndInvalidateByVAToPoC(host_ptr, block_size);
+ };
+ return PerformCacheOperation(process, dest_addr, size, perform);
+ }
+
void MarkRegionDebug(VAddr vaddr, u64 size, bool debug) {
if (vaddr == 0) {
return;
@@ -786,6 +836,21 @@ void Memory::ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, const s
impl->ZeroBlock(process, dest_addr, size);
}
+Result Memory::InvalidateDataCache(const Kernel::KProcess& process, VAddr dest_addr,
+ const std::size_t size) {
+ return impl->InvalidateDataCache(process, dest_addr, size);
+}
+
+Result Memory::StoreDataCache(const Kernel::KProcess& process, VAddr dest_addr,
+ const std::size_t size) {
+ return impl->StoreDataCache(process, dest_addr, size);
+}
+
+Result Memory::FlushDataCache(const Kernel::KProcess& process, VAddr dest_addr,
+ const std::size_t size) {
+ return impl->FlushDataCache(process, dest_addr, size);
+}
+
void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
impl->RasterizerMarkRegionCached(vaddr, size, cached);
}
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 @@
#include <memory>
#include <string>
#include "common/common_types.h"
+#include "core/hle/result.h"
namespace Common {
struct PageTable;
@@ -450,6 +451,39 @@ public:
void ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size);
/**
+ * Invalidates a range of bytes within the current process' address space at the specified
+ * virtual address.
+ *
+ * @param process The process that will have data invalidated within its address space.
+ * @param dest_addr The destination virtual address to invalidate the data from.
+ * @param size The size of the range to invalidate, in bytes.
+ *
+ */
+ Result InvalidateDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size);
+
+ /**
+ * Stores a range of bytes within the current process' address space at the specified
+ * virtual address.
+ *
+ * @param process The process that will have data stored within its address space.
+ * @param dest_addr The destination virtual address to store the data from.
+ * @param size The size of the range to store, in bytes.
+ *
+ */
+ Result StoreDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size);
+
+ /**
+ * Flushes a range of bytes within the current process' address space at the specified
+ * virtual address.
+ *
+ * @param process The process that will have data flushed within its address space.
+ * @param dest_addr The destination virtual address to flush the data from.
+ * @param size The size of the range to flush, in bytes.
+ *
+ */
+ Result FlushDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size);
+
+ /**
* Marks each page within the specified address range as cached or uncached.
*
* @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) {
ctx.Add("MOV.S {}.x,primitive_invocation.x;", inst);
}
+void EmitInvocationInfo(EmitContext& ctx, IR::Inst& inst) {
+ switch (ctx.stage) {
+ case Stage::TessellationControl:
+ case Stage::TessellationEval:
+ ctx.Add("SHL.U {}.x,primitive.vertexcount,16;", inst);
+ break;
+ default:
+ LOG_WARNING(Shader, "(STUBBED) called");
+ ctx.Add("MOV.S {}.x,0x00ff0000;", inst);
+ }
+}
+
void EmitSampleId(EmitContext& ctx, IR::Inst& inst) {
ctx.Add("MOV.S {}.x,fragment.sampleid.x;", inst);
}
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);
void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst);
void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst);
void EmitInvocationId(EmitContext& ctx, IR::Inst& inst);
+void EmitInvocationInfo(EmitContext& ctx, IR::Inst& inst);
void EmitSampleId(EmitContext& ctx, IR::Inst& inst);
void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst);
void 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
if (info.uses_invocation_id) {
Add("ATTRIB primitive_invocation=primitive.invocation;");
}
+ if (info.uses_invocation_info &&
+ (stage == Stage::TessellationControl || stage == Stage::TessellationEval)) {
+ Add("ATTRIB primitive_vertexcount = primitive.vertexcount;");
+ }
if (info.stores_tess_level_outer) {
Add("OUTPUT result_patch_tessouter[]={{result.patch.tessouter[0..3]}};");
}
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) {
ctx.AddU32("{}=uint(gl_InvocationID);", inst);
}
+void EmitInvocationInfo(EmitContext& ctx, IR::Inst& inst) {
+ switch (ctx.stage) {
+ case Stage::TessellationControl:
+ case Stage::TessellationEval:
+ ctx.AddU32("{}=uint(gl_PatchVerticesIn)<<16;", inst);
+ break;
+ default:
+ LOG_WARNING(Shader, "(STUBBED) called");
+ ctx.AddU32("{}=uint(0x00ff0000);", inst);
+ }
+}
+
void EmitSampleId(EmitContext& ctx, IR::Inst& inst) {
ctx.AddU32("{}=uint(gl_SampleID);", inst);
}
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);
void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst);
void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst);
void EmitInvocationId(EmitContext& ctx, IR::Inst& inst);
+void EmitInvocationInfo(EmitContext& ctx, IR::Inst& inst);
void EmitSampleId(EmitContext& ctx, IR::Inst& inst);
void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst);
void 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) {
return ctx.OpLoad(ctx.U32[1], ctx.invocation_id);
}
+Id EmitInvocationInfo(EmitContext& ctx) {
+ switch (ctx.stage) {
+ case Stage::TessellationControl:
+ case Stage::TessellationEval:
+ return ctx.OpShiftLeftLogical(ctx.U32[1], ctx.OpLoad(ctx.U32[1], ctx.patch_vertices_in),
+ ctx.Const(16u));
+ default:
+ LOG_WARNING(Shader, "(STUBBED) called");
+ return ctx.Const(0x00ff0000u);
+ }
+}
+
Id EmitSampleId(EmitContext& ctx) {
return ctx.OpLoad(ctx.U32[1], ctx.sample_id);
}
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);
Id EmitWorkgroupId(EmitContext& ctx);
Id EmitLocalInvocationId(EmitContext& ctx);
Id EmitInvocationId(EmitContext& ctx);
+Id EmitInvocationInfo(EmitContext& ctx);
Id EmitSampleId(EmitContext& ctx);
Id EmitIsHelperInvocation(EmitContext& ctx);
Id 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) {
if (info.uses_invocation_id) {
invocation_id = DefineInput(*this, U32[1], false, spv::BuiltIn::InvocationId);
}
+ if (info.uses_invocation_info &&
+ (stage == Shader::Stage::TessellationControl || stage == Shader::Stage::TessellationEval)) {
+ patch_vertices_in = DefineInput(*this, U32[1], false, spv::BuiltIn::PatchVertices);
+ }
if (info.uses_sample_id) {
sample_id = DefineInput(*this, U32[1], false, spv::BuiltIn::SampleId);
}
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:
Id workgroup_id{};
Id local_invocation_id{};
Id invocation_id{};
+ Id patch_vertices_in{};
Id sample_id{};
Id is_helper_invocation{};
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() {
return Inst<U32>(Opcode::InvocationId);
}
+U32 IREmitter::InvocationInfo() {
+ return Inst<U32>(Opcode::InvocationInfo);
+}
+
U32 IREmitter::SampleId() {
return Inst<U32>(Opcode::SampleId);
}
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:
[[nodiscard]] U32 LocalInvocationIdZ();
[[nodiscard]] U32 InvocationId();
+ [[nodiscard]] U32 InvocationInfo();
[[nodiscard]] U32 SampleId();
[[nodiscard]] U1 IsHelperInvocation();
[[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,
OPCODE(WorkgroupId, U32x3, )
OPCODE(LocalInvocationId, U32x3, )
OPCODE(InvocationId, U32, )
+OPCODE(InvocationInfo, U32, )
OPCODE(SampleId, U32, )
OPCODE(IsHelperInvocation, U1, )
OPCODE(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 {
TessellationLodBottom,
TessellationLodInteriorU,
TessellationLodInteriorV,
- ComponentPadding0,
- ComponentPadding1,
Component0,
Component1,
Component2,
@@ -137,7 +135,7 @@ enum class Patch : u64 {
Component118,
Component119,
};
-static_assert(static_cast<u64>(Patch::Component119) == 127);
+static_assert(static_cast<u64>(Patch::Component119) == 125);
[[nodiscard]] bool IsGeneric(Patch patch) noexcept;
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 {
case SpecialRegister::SR_THREAD_KILL:
return IR::U32{ir.Select(ir.IsHelperInvocation(), ir.Imm32(-1), ir.Imm32(0))};
case SpecialRegister::SR_INVOCATION_INFO:
- LOG_WARNING(Shader, "(STUBBED) SR_INVOCATION_INFO");
- return ir.Imm32(0x00ff'0000);
+ return ir.InvocationInfo();
case SpecialRegister::SR_TID: {
const IR::Value tid{ir.LocalInvocationId()};
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
Optimization::PositionPass(env, program);
Optimization::GlobalMemoryToStorageBufferPass(program);
- Optimization::TexturePass(env, program);
+ Optimization::TexturePass(env, program, host_info);
if (Settings::values.resolution_info.active) {
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 {
bool support_float16{}; ///< True when the device supports 16-bit floats
bool support_int64{}; ///< True when the device supports 64-bit integers
bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered
+ bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers
};
} // 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) {
case IR::Opcode::InvocationId:
info.uses_invocation_id = true;
break;
+ case IR::Opcode::InvocationInfo:
+ info.uses_invocation_info = true;
+ break;
case IR::Opcode::SampleId:
info.uses_sample_id = true;
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 @@
#include "shader_recompiler/environment.h"
#include "shader_recompiler/frontend/ir/program.h"
+namespace Shader {
+struct HostTranslateInfo;
+}
+
namespace Shader::Optimization {
void CollectShaderInfoPass(Environment& env, IR::Program& program);
@@ -18,7 +22,7 @@ void LowerInt64ToInt32(IR::Program& program);
void RescalingPass(IR::Program& program);
void SsaRewritePass(IR::Program& program);
void PositionPass(Environment& env, IR::Program& program);
-void TexturePass(Environment& env, IR::Program& program);
+void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo& host_info);
void VerificationPass(const IR::Program& program);
// 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 @@
#include <boost/container/small_vector.hpp>
-#include "common/settings.h"
#include "shader_recompiler/environment.h"
#include "shader_recompiler/frontend/ir/basic_block.h"
#include "shader_recompiler/frontend/ir/breadth_first_search.h"
#include "shader_recompiler/frontend/ir/ir_emitter.h"
+#include "shader_recompiler/host_translate_info.h"
#include "shader_recompiler/ir_opt/passes.h"
#include "shader_recompiler/shader_info.h"
@@ -461,7 +461,7 @@ void PatchImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
ir.FPRecip(ir.ConvertUToF(32, 32, ir.CompositeExtract(texture_size, 1))))));
}
-void PathTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_format) {
+void PatchTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_format) {
const auto it{IR::Block::InstructionList::s_iterator_to(inst)};
IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
auto get_max_value = [pixel_format]() -> float {
@@ -494,7 +494,7 @@ void PathTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_f
}
} // Anonymous namespace
-void TexturePass(Environment& env, IR::Program& program) {
+void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo& host_info) {
TextureInstVector to_replace;
for (IR::Block* const block : program.post_order_blocks) {
for (IR::Inst& inst : block->Instructions()) {
@@ -639,11 +639,11 @@ void TexturePass(Environment& env, IR::Program& program) {
inst->SetArg(0, IR::Value{});
}
- if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL &&
- inst->GetOpcode() == IR::Opcode::ImageFetch && flags.type == TextureType::Buffer) {
+ if (!host_info.support_snorm_render_buffer && inst->GetOpcode() == IR::Opcode::ImageFetch &&
+ flags.type == TextureType::Buffer) {
const auto pixel_format = ReadTexturePixelFormat(env, cbuf);
if (pixel_format != TexturePixelFormat::OTHER) {
- PathTexelFetch(*texture_inst.block, *texture_inst.inst, pixel_format);
+ PatchTexelFetch(*texture_inst.block, *texture_inst.inst, pixel_format);
}
}
}
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 {
bool uses_workgroup_id{};
bool uses_local_invocation_id{};
bool uses_invocation_id{};
+ bool uses_invocation_info{};
bool uses_sample_id{};
bool uses_is_helper_invocation{};
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
macro/macro_hle.h
macro/macro_interpreter.cpp
macro/macro_interpreter.h
- macro/macro_jit_x64.cpp
- macro/macro_jit_x64.h
fence_manager.h
gpu.cpp
gpu.h
@@ -245,7 +243,7 @@ add_library(video_core STATIC
create_target_directory_groups(video_core)
target_link_libraries(video_core PUBLIC common core)
-target_link_libraries(video_core PUBLIC glad shader_recompiler xbyak)
+target_link_libraries(video_core PUBLIC glad shader_recompiler)
if (YUZU_USE_BUNDLED_FFMPEG AND NOT WIN32)
add_dependencies(video_core ffmpeg-build)
@@ -282,8 +280,19 @@ else()
-Wno-sign-conversion
)
+
+ # xbyak
+ set_source_files_properties(macro/macro_jit_x64.cpp PROPERTIES COMPILE_OPTIONS "-Wno-conversion;-Wno-shadow")
endif()
if (ARCHITECTURE_x86_64)
+ target_sources(video_core PRIVATE
+ macro/macro_jit_x64.cpp
+ macro/macro_jit_x64.h
+ )
+ target_link_libraries(video_core PUBLIC xbyak)
+endif()
+
+if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
target_link_libraries(video_core PRIVATE dynarmic)
endif()
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
return;
case MAXWELL3D_REG_INDEX(fragment_barrier):
return rasterizer->FragmentBarrier();
+ case MAXWELL3D_REG_INDEX(invalidate_texture_data_cache):
+ rasterizer->InvalidateGPUCache();
+ return rasterizer->WaitForIdle();
+ case MAXWELL3D_REG_INDEX(tiled_cache_barrier):
+ return rasterizer->TiledCacheBarrier();
}
}
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:
case Size::Size_A2_B10_G10_R10:
return "2_10_10_10";
case Size::Size_B10_G11_R11:
- return "10_11_12";
+ return "10_11_11";
default:
ASSERT(false);
return {};
@@ -2639,7 +2639,7 @@ public:
L2CacheControl l2_cache_control; ///< 0x0218
InvalidateShaderCache invalidate_shader_cache; ///< 0x021C
INSERT_PADDING_BYTES_NOINIT(0xA8);
- SyncInfo sync_info; ///< 0x02C8
+ SyncInfo sync_info; ///< 0x02C8
INSERT_PADDING_BYTES_NOINIT(0x4);
u32 prim_circular_buffer_throttle; ///< 0x02D0
u32 flush_invalidate_rop_mini_cache; ///< 0x02D4
@@ -2731,7 +2731,11 @@ public:
s32 stencil_back_ref; ///< 0x0F54
u32 stencil_back_mask; ///< 0x0F58
u32 stencil_back_func_mask; ///< 0x0F5C
- INSERT_PADDING_BYTES_NOINIT(0x24);
+ INSERT_PADDING_BYTES_NOINIT(0x14);
+ u32 invalidate_texture_data_cache; ///< 0x0F74 Assumed - Not in official docs.
+ INSERT_PADDING_BYTES_NOINIT(0x4);
+ u32 tiled_cache_barrier; ///< 0x0F7C Assumed - Not in official docs.
+ INSERT_PADDING_BYTES_NOINIT(0x4);
VertexStreamSubstitute vertex_stream_substitute; ///< 0x0F84
u32 line_mode_clip_generated_edge_do_not_draw; ///< 0x0F8C
u32 color_mask_common; ///< 0x0F90
@@ -2791,7 +2795,8 @@ public:
FillViaTriangleMode fill_via_triangle_mode; ///< 0x113C
u32 blend_per_format_snorm8_unorm16_snorm16_enabled; ///< 0x1140
u32 flush_pending_writes_sm_gloal_store; ///< 0x1144
- INSERT_PADDING_BYTES_NOINIT(0x18);
+ u32 conservative_raster_enable; ///< 0x1148 Assumed - Not in official docs.
+ INSERT_PADDING_BYTES_NOINIT(0x14);
std::array<VertexAttribute, NumVertexAttributes> vertex_attrib_format; ///< 0x1160
std::array<MsaaSampleLocation, 4> multisample_sample_locations; ///< 0x11E0
u32 offset_render_target_index_by_viewport_index; ///< 0x11F0
@@ -2970,7 +2975,7 @@ public:
CullFace gl_cull_face; ///< 0x1920
Viewport::PixelCenter viewport_pixel_center; ///< 0x1924
INSERT_PADDING_BYTES_NOINIT(0x4);
- u32 viewport_scale_offset_enbled; ///< 0x192C
+ u32 viewport_scale_offset_enabled; ///< 0x192C
INSERT_PADDING_BYTES_NOINIT(0xC);
ViewportClipControl viewport_clip_control; ///< 0x193C
UserClip::Op user_clip_op; ///< 0x1940
@@ -3287,6 +3292,8 @@ ASSERT_REG_POSITION(const_color_rendering, 0x0F40);
ASSERT_REG_POSITION(stencil_back_ref, 0x0F54);
ASSERT_REG_POSITION(stencil_back_mask, 0x0F58);
ASSERT_REG_POSITION(stencil_back_func_mask, 0x0F5C);
+ASSERT_REG_POSITION(invalidate_texture_data_cache, 0x0F74);
+ASSERT_REG_POSITION(tiled_cache_barrier, 0x0F7C);
ASSERT_REG_POSITION(vertex_stream_substitute, 0x0F84);
ASSERT_REG_POSITION(line_mode_clip_generated_edge_do_not_draw, 0x0F8C);
ASSERT_REG_POSITION(color_mask_common, 0x0F90);
@@ -3343,6 +3350,7 @@ ASSERT_REG_POSITION(post_ps_use_pre_ps_coverage, 0x1138);
ASSERT_REG_POSITION(fill_via_triangle_mode, 0x113C);
ASSERT_REG_POSITION(blend_per_format_snorm8_unorm16_snorm16_enabled, 0x1140);
ASSERT_REG_POSITION(flush_pending_writes_sm_gloal_store, 0x1144);
+ASSERT_REG_POSITION(conservative_raster_enable, 0x1148);
ASSERT_REG_POSITION(vertex_attrib_format, 0x1160);
ASSERT_REG_POSITION(multisample_sample_locations, 0x11E0);
ASSERT_REG_POSITION(offset_render_target_index_by_viewport_index, 0x11F0);
@@ -3482,7 +3490,7 @@ ASSERT_REG_POSITION(gl_cull_test_enabled, 0x1918);
ASSERT_REG_POSITION(gl_front_face, 0x191C);
ASSERT_REG_POSITION(gl_cull_face, 0x1920);
ASSERT_REG_POSITION(viewport_pixel_center, 0x1924);
-ASSERT_REG_POSITION(viewport_scale_offset_enbled, 0x192C);
+ASSERT_REG_POSITION(viewport_scale_offset_enabled, 0x192C);
ASSERT_REG_POSITION(viewport_clip_control, 0x193C);
ASSERT_REG_POSITION(user_clip_op, 0x1940);
ASSERT_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() {
const bool is_src_pitch = IsPitchKind(static_cast<PTEKind>(src_kind));
const bool is_dst_pitch = IsPitchKind(static_cast<PTEKind>(dst_kind));
if (!is_src_pitch && is_dst_pitch) {
- std::vector<u8> tmp_buffer(regs.line_length_in);
- std::vector<u8> dst_buffer(regs.line_length_in);
- memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(),
- regs.line_length_in);
- for (u32 offset = 0; offset < regs.line_length_in; ++offset) {
- dst_buffer[offset] =
- tmp_buffer[convert_linear_2_blocklinear_addr(regs.offset_in + offset) -
- regs.offset_in];
+ UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0);
+ UNIMPLEMENTED_IF(regs.offset_in % 16 != 0);
+ UNIMPLEMENTED_IF(regs.offset_out % 16 != 0);
+ std::vector<u8> tmp_buffer(16);
+ for (u32 offset = 0; offset < regs.line_length_in; offset += 16) {
+ memory_manager.ReadBlockUnsafe(
+ convert_linear_2_blocklinear_addr(regs.offset_in + offset),
+ tmp_buffer.data(), tmp_buffer.size());
+ memory_manager.WriteBlock(regs.offset_out + offset, tmp_buffer.data(),
+ tmp_buffer.size());
}
- memory_manager.WriteBlock(regs.offset_out, dst_buffer.data(), regs.line_length_in);
} else if (is_src_pitch && !is_dst_pitch) {
- std::vector<u8> tmp_buffer(regs.line_length_in);
- std::vector<u8> dst_buffer(regs.line_length_in);
- memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(),
- regs.line_length_in);
- for (u32 offset = 0; offset < regs.line_length_in; ++offset) {
- dst_buffer[convert_linear_2_blocklinear_addr(regs.offset_out + offset) -
- regs.offset_out] = tmp_buffer[offset];
+ UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0);
+ UNIMPLEMENTED_IF(regs.offset_in % 16 != 0);
+ UNIMPLEMENTED_IF(regs.offset_out % 16 != 0);
+ std::vector<u8> tmp_buffer(16);
+ for (u32 offset = 0; offset < regs.line_length_in; offset += 16) {
+ memory_manager.ReadBlockUnsafe(regs.offset_in + offset, tmp_buffer.data(),
+ tmp_buffer.size());
+ memory_manager.WriteBlock(
+ convert_linear_2_blocklinear_addr(regs.offset_out + offset),
+ tmp_buffer.data(), tmp_buffer.size());
}
- memory_manager.WriteBlock(regs.offset_out, dst_buffer.data(), regs.line_length_in);
} else {
if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) {
std::vector<u8> tmp_buffer(regs.line_length_in);
@@ -311,6 +314,7 @@ void MaxwellDMA::ReleaseSemaphore() {
}
default:
ASSERT_MSG(false, "Unknown semaphore type: {}", static_cast<u32>(type.Value()));
+ break;
}
}
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) {
break;
default:
UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id);
+ break;
}
}
@@ -65,6 +66,7 @@ void Puller::ProcessFenceActionMethod() {
break;
default:
UNIMPLEMENTED_MSG("Unimplemented operation {}", regs.fence_action.op.Value());
+ break;
}
}
@@ -228,6 +230,7 @@ void Puller::CallEngineMethod(const MethodCall& method_call) {
break;
default:
UNIMPLEMENTED_MSG("Unimplemented engine");
+ break;
}
}
@@ -254,6 +257,7 @@ void Puller::CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_s
break;
default:
UNIMPLEMENTED_MSG("Unimplemented engine");
+ break;
}
}
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 @@
#include "video_core/macro/macro.h"
#include "video_core/macro/macro_hle.h"
#include "video_core/macro/macro_interpreter.h"
+
+#ifdef ARCHITECTURE_x86_64
#include "video_core/macro/macro_jit_x64.h"
+#endif
namespace Tegra {
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) {
}
default:
UNIMPLEMENTED_MSG("Unimplemented macro operation {}", opcode.operation.Value());
+ break;
}
// An instruction with the Exit flag will not actually
@@ -297,6 +298,7 @@ void MacroInterpreterImpl::ProcessResult(Macro::ResultOperation operation, u32 r
break;
default:
UNIMPLEMENTED_MSG("Unimplemented result operation {}", operation);
+ break;
}
}
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
break;
default:
UNIMPLEMENTED_MSG("Unimplemented macro operation {}", operation);
+ break;
}
}
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() {
}
flags[Dirty::Viewport0 + index] = false;
- if (!regs.viewport_scale_offset_enbled) {
+ if (!regs.viewport_scale_offset_enabled) {
const auto x = static_cast<GLfloat>(regs.surface_clip.x);
const auto y = static_cast<GLfloat>(regs.surface_clip.y);
const auto width = static_cast<GLfloat>(regs.surface_clip.width);
@@ -770,7 +770,7 @@ void RasterizerOpenGL::SyncStencilTestState() {
if (regs.stencil_two_side_enable) {
glStencilFuncSeparate(GL_BACK, MaxwellToGL::ComparisonOp(regs.stencil_back_op.func),
- regs.stencil_back_ref, regs.stencil_back_mask);
+ regs.stencil_back_ref, regs.stencil_back_func_mask);
glStencilOpSeparate(GL_BACK, MaxwellToGL::StencilOp(regs.stencil_back_op.fail),
MaxwellToGL::StencilOp(regs.stencil_back_op.zfail),
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,
}
break;
case Shader::Stage::TessellationEval:
- info.tess_clockwise = key.tessellation_clockwise != 0;
+ // Flip the face, as OpenGL's drawing is flipped.
+ info.tess_clockwise = key.tessellation_clockwise == 0;
info.tess_primitive = [&key] {
switch (key.tessellation_primitive) {
case Maxwell::Tessellation::DomainType::Isolines:
@@ -218,6 +219,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
.support_float16 = false,
.support_int64 = device.HasShaderInt64(),
.needs_demote_reorder = device.IsAmd(),
+ .support_snorm_render_buffer = false,
} {
if (use_asynchronous_shaders) {
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) {
FillBlock(tables[1], OFF(viewport_transform), NUM(viewport_transform), Viewports);
FillBlock(tables[1], OFF(viewports), NUM(viewports), Viewports);
- tables[0][OFF(viewport_scale_offset_enbled)] = ViewportTransform;
- tables[1][OFF(viewport_scale_offset_enbled)] = Viewports;
+ tables[0][OFF(viewport_scale_offset_enabled)] = ViewportTransform;
+ tables[1][OFF(viewport_scale_offset_enabled)] = Viewports;
}
void 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
break;
default:
ASSERT(false);
+ break;
}
}
@@ -927,6 +928,7 @@ void Image::CopyImageToBuffer(const VideoCommon::BufferImageCopy& copy, size_t b
break;
default:
ASSERT(false);
+ break;
}
// Compressed formats don't have a pixel format or type
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,
texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
// UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}",
// static_cast<u32>(framebuffer.pixel_format));
+ break;
}
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,
depth_format.Assign(static_cast<u32>(regs.zeta.format));
y_negate.Assign(regs.window_origin.mode != Maxwell::WindowOrigin::Mode::UpperLeft ? 1 : 0);
provoking_vertex_last.Assign(regs.provoking_vertex == Maxwell::ProvokingVertex::Last ? 1 : 0);
+ conservative_raster_enable.Assign(regs.conservative_raster_enable != 0 ? 1 : 0);
smooth_lines.Assign(regs.line_anti_alias_enable != 0 ? 1 : 0);
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 {
BitField<6, 5, u32> depth_format;
BitField<11, 1, u32> y_negate;
BitField<12, 1, u32> provoking_vertex_last;
+ BitField<13, 1, u32> conservative_raster_enable;
BitField<14, 1, u32> smooth_lines;
};
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) {
.lineStippleFactor = 0,
.lineStipplePattern = 0,
};
+ VkPipelineRasterizationConservativeStateCreateInfoEXT conservative_raster{
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_CONSERVATIVE_STATE_CREATE_INFO_EXT,
+ .pNext = nullptr,
+ .flags = 0,
+ .conservativeRasterizationMode = key.state.conservative_raster_enable != 0
+ ? VK_CONSERVATIVE_RASTERIZATION_MODE_OVERESTIMATE_EXT
+ : VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT,
+ .extraPrimitiveOverestimationSize = 0.0f,
+ };
VkPipelineRasterizationProvokingVertexStateCreateInfoEXT provoking_vertex{
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT,
.pNext = nullptr,
@@ -690,6 +699,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
if (IsLine(input_assembly_topology) && device.IsExtLineRasterizationSupported()) {
line_state.pNext = std::exchange(rasterization_ci.pNext, &line_state);
}
+ if (device.IsExtConservativeRasterizationSupported()) {
+ conservative_raster.pNext = std::exchange(rasterization_ci.pNext, &conservative_raster);
+ }
if (device.IsExtProvokingVertexSupported()) {
provoking_vertex.pNext = std::exchange(rasterization_ci.pNext, &provoking_vertex);
}
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
}
break;
case Shader::Stage::TessellationEval:
+ info.tess_clockwise = key.state.tessellation_clockwise != 0;
info.tess_primitive = [&key] {
const u32 raw{key.state.tessellation_primitive.Value()};
switch (static_cast<Maxwell::Tessellation::DomainType>(raw)) {
@@ -325,6 +326,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
.support_int64 = device.IsShaderInt64Supported(),
.needs_demote_reorder = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY_KHR ||
driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR,
+ .support_snorm_render_buffer = true,
};
}
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
if (!state_tracker.TouchViewports()) {
return;
}
- if (!regs.viewport_scale_offset_enbled) {
+ if (!regs.viewport_scale_offset_enabled) {
const auto x = static_cast<float>(regs.surface_clip.x);
const auto y = static_cast<float>(regs.surface_clip.y);
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
[[fallthrough]];
default:
vk::Check(result);
+ break;
}
});
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() {
void SetupDirtyViewports(Tables& tables) {
FillBlock(tables[0], OFF(viewport_transform), NUM(viewport_transform), Viewports);
FillBlock(tables[0], OFF(viewports), NUM(viewports), Viewports);
- tables[0][OFF(viewport_scale_offset_enbled)] = Viewports;
+ tables[0][OFF(viewport_scale_offset_enabled)] = Viewports;
tables[1][OFF(window_origin)] = Viewports;
}
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) {
break;
default:
ASSERT_MSG(false, "Invalid surface type");
+ break;
}
}
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
u32 GraphicsEnvironment::ReadViewportTransformState() {
const auto& regs{maxwell3d->regs};
- viewport_transform_state = regs.viewport_scale_offset_enbled;
+ viewport_transform_state = regs.viewport_scale_offset_enabled;
return viewport_transform_state;
}
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
#undef BPP_CASE
default:
ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel);
+ break;
}
}
@@ -217,6 +218,7 @@ void SwizzleSubrect(std::span<u8> output, std::span<const u8> input, u32 bytes_p
#undef BPP_CASE
default:
ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel);
+ break;
}
}
@@ -240,6 +242,7 @@ void UnswizzleSubrect(std::span<u8> output, std::span<const u8> input, u32 bytes
#undef BPP_CASE
default:
ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel);
+ break;
}
}
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)
target_compile_definitions(yuzu PRIVATE HAS_OPENGL)
endif()
-if (ARCHITECTURE_x86_64)
+if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
target_link_libraries(yuzu PRIVATE dynarmic)
endif()
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) {
}
int GRenderWindow::QtKeyToSwitchKey(Qt::Key qt_key) {
- switch (qt_key) {
- case Qt::Key_A:
- return Settings::NativeKeyboard::A;
- case Qt::Key_B:
- return Settings::NativeKeyboard::B;
- case Qt::Key_C:
- return Settings::NativeKeyboard::C;
- case Qt::Key_D:
- return Settings::NativeKeyboard::D;
- case Qt::Key_E:
- return Settings::NativeKeyboard::E;
- case Qt::Key_F:
- return Settings::NativeKeyboard::F;
- case Qt::Key_G:
- return Settings::NativeKeyboard::G;
- case Qt::Key_H:
- return Settings::NativeKeyboard::H;
- case Qt::Key_I:
- return Settings::NativeKeyboard::I;
- case Qt::Key_J:
- return Settings::NativeKeyboard::J;
- case Qt::Key_K:
- return Settings::NativeKeyboard::K;
- case Qt::Key_L:
- return Settings::NativeKeyboard::L;
- case Qt::Key_M:
- return Settings::NativeKeyboard::M;
- case Qt::Key_N:
- return Settings::NativeKeyboard::N;
- case Qt::Key_O:
- return Settings::NativeKeyboard::O;
- case Qt::Key_P:
- return Settings::NativeKeyboard::P;
- case Qt::Key_Q:
- return Settings::NativeKeyboard::Q;
- case Qt::Key_R:
- return Settings::NativeKeyboard::R;
- case Qt::Key_S:
- return Settings::NativeKeyboard::S;
- case Qt::Key_T:
- return Settings::NativeKeyboard::T;
- case Qt::Key_U:
- return Settings::NativeKeyboard::U;
- case Qt::Key_V:
- return Settings::NativeKeyboard::V;
- case Qt::Key_W:
- return Settings::NativeKeyboard::W;
- case Qt::Key_X:
- return Settings::NativeKeyboard::X;
- case Qt::Key_Y:
- return Settings::NativeKeyboard::Y;
- case Qt::Key_Z:
- return Settings::NativeKeyboard::Z;
- case Qt::Key_1:
- return Settings::NativeKeyboard::N1;
- case Qt::Key_2:
- return Settings::NativeKeyboard::N2;
- case Qt::Key_3:
- return Settings::NativeKeyboard::N3;
- case Qt::Key_4:
- return Settings::NativeKeyboard::N4;
- case Qt::Key_5:
- return Settings::NativeKeyboard::N5;
- case Qt::Key_6:
- return Settings::NativeKeyboard::N6;
- case Qt::Key_7:
- return Settings::NativeKeyboard::N7;
- case Qt::Key_8:
- return Settings::NativeKeyboard::N8;
- case Qt::Key_9:
- return Settings::NativeKeyboard::N9;
- case Qt::Key_0:
- return Settings::NativeKeyboard::N0;
- case Qt::Key_Return:
- return Settings::NativeKeyboard::Return;
- case Qt::Key_Escape:
- return Settings::NativeKeyboard::Escape;
- case Qt::Key_Backspace:
- return Settings::NativeKeyboard::Backspace;
- case Qt::Key_Tab:
- return Settings::NativeKeyboard::Tab;
- case Qt::Key_Space:
- return Settings::NativeKeyboard::Space;
- case Qt::Key_Minus:
- return Settings::NativeKeyboard::Minus;
- case Qt::Key_Plus:
- case Qt::Key_questiondown:
- return Settings::NativeKeyboard::Plus;
- case Qt::Key_BracketLeft:
- case Qt::Key_BraceLeft:
- return Settings::NativeKeyboard::OpenBracket;
- case Qt::Key_BracketRight:
- case Qt::Key_BraceRight:
- return Settings::NativeKeyboard::CloseBracket;
- case Qt::Key_Bar:
- return Settings::NativeKeyboard::Pipe;
- case Qt::Key_Dead_Tilde:
- return Settings::NativeKeyboard::Tilde;
- case Qt::Key_Ntilde:
- case Qt::Key_Semicolon:
- return Settings::NativeKeyboard::Semicolon;
- case Qt::Key_Apostrophe:
- return Settings::NativeKeyboard::Quote;
- case Qt::Key_Dead_Grave:
- return Settings::NativeKeyboard::Backquote;
- case Qt::Key_Comma:
- return Settings::NativeKeyboard::Comma;
- case Qt::Key_Period:
- return Settings::NativeKeyboard::Period;
- case Qt::Key_Slash:
- return Settings::NativeKeyboard::Slash;
- case Qt::Key_CapsLock:
- return Settings::NativeKeyboard::CapsLock;
- case Qt::Key_F1:
- return Settings::NativeKeyboard::F1;
- case Qt::Key_F2:
- return Settings::NativeKeyboard::F2;
- case Qt::Key_F3:
- return Settings::NativeKeyboard::F3;
- case Qt::Key_F4:
- return Settings::NativeKeyboard::F4;
- case Qt::Key_F5:
- return Settings::NativeKeyboard::F5;
- case Qt::Key_F6:
- return Settings::NativeKeyboard::F6;
- case Qt::Key_F7:
- return Settings::NativeKeyboard::F7;
- case Qt::Key_F8:
- return Settings::NativeKeyboard::F8;
- case Qt::Key_F9:
- return Settings::NativeKeyboard::F9;
- case Qt::Key_F10:
- return Settings::NativeKeyboard::F10;
- case Qt::Key_F11:
- return Settings::NativeKeyboard::F11;
- case Qt::Key_F12:
- return Settings::NativeKeyboard::F12;
- case Qt::Key_Print:
- return Settings::NativeKeyboard::PrintScreen;
- case Qt::Key_ScrollLock:
- return Settings::NativeKeyboard::ScrollLock;
- case Qt::Key_Pause:
- return Settings::NativeKeyboard::Pause;
- case Qt::Key_Insert:
- return Settings::NativeKeyboard::Insert;
- case Qt::Key_Home:
- return Settings::NativeKeyboard::Home;
- case Qt::Key_PageUp:
- return Settings::NativeKeyboard::PageUp;
- case Qt::Key_Delete:
- return Settings::NativeKeyboard::Delete;
- case Qt::Key_End:
- return Settings::NativeKeyboard::End;
- case Qt::Key_PageDown:
- return Settings::NativeKeyboard::PageDown;
- case Qt::Key_Right:
- return Settings::NativeKeyboard::Right;
- case Qt::Key_Left:
- return Settings::NativeKeyboard::Left;
- case Qt::Key_Down:
- return Settings::NativeKeyboard::Down;
- case Qt::Key_Up:
- return Settings::NativeKeyboard::Up;
- case Qt::Key_NumLock:
- return Settings::NativeKeyboard::NumLock;
- // Numpad keys are missing here
- case Qt::Key_F13:
- return Settings::NativeKeyboard::F13;
- case Qt::Key_F14:
- return Settings::NativeKeyboard::F14;
- case Qt::Key_F15:
- return Settings::NativeKeyboard::F15;
- case Qt::Key_F16:
- return Settings::NativeKeyboard::F16;
- case Qt::Key_F17:
- return Settings::NativeKeyboard::F17;
- case Qt::Key_F18:
- return Settings::NativeKeyboard::F18;
- case Qt::Key_F19:
- return Settings::NativeKeyboard::F19;
- case Qt::Key_F20:
- return Settings::NativeKeyboard::F20;
- case Qt::Key_F21:
- return Settings::NativeKeyboard::F21;
- case Qt::Key_F22:
- return Settings::NativeKeyboard::F22;
- case Qt::Key_F23:
- return Settings::NativeKeyboard::F23;
- case Qt::Key_F24:
- return Settings::NativeKeyboard::F24;
- // case Qt:::
- // return Settings::NativeKeyboard::KPComma;
- // case Qt:::
- // return Settings::NativeKeyboard::Ro;
- case Qt::Key_Hiragana_Katakana:
- return Settings::NativeKeyboard::KatakanaHiragana;
- case Qt::Key_yen:
- return Settings::NativeKeyboard::Yen;
- case Qt::Key_Henkan:
- return Settings::NativeKeyboard::Henkan;
- case Qt::Key_Muhenkan:
- return Settings::NativeKeyboard::Muhenkan;
- // case Qt:::
- // return Settings::NativeKeyboard::NumPadCommaPc98;
- case Qt::Key_Hangul:
- return Settings::NativeKeyboard::HangulEnglish;
- case Qt::Key_Hangul_Hanja:
- return Settings::NativeKeyboard::Hanja;
- case Qt::Key_Katakana:
- return Settings::NativeKeyboard::KatakanaKey;
- case Qt::Key_Hiragana:
- return Settings::NativeKeyboard::HiraganaKey;
- case Qt::Key_Zenkaku_Hankaku:
- return Settings::NativeKeyboard::ZenkakuHankaku;
- // Modifier keys are handled by the modifier property
- default:
- return Settings::NativeKeyboard::None;
+ static constexpr std::array<std::pair<Qt::Key, Settings::NativeKeyboard::Keys>, 106> key_map = {
+ std::pair<Qt::Key, Settings::NativeKeyboard::Keys>{Qt::Key_A, Settings::NativeKeyboard::A},
+ {Qt::Key_A, Settings::NativeKeyboard::A},
+ {Qt::Key_B, Settings::NativeKeyboard::B},
+ {Qt::Key_C, Settings::NativeKeyboard::C},
+ {Qt::Key_D, Settings::NativeKeyboard::D},
+ {Qt::Key_E, Settings::NativeKeyboard::E},
+ {Qt::Key_F, Settings::NativeKeyboard::F},
+ {Qt::Key_G, Settings::NativeKeyboard::G},
+ {Qt::Key_H, Settings::NativeKeyboard::H},
+ {Qt::Key_I, Settings::NativeKeyboard::I},
+ {Qt::Key_J, Settings::NativeKeyboard::J},
+ {Qt::Key_K, Settings::NativeKeyboard::K},
+ {Qt::Key_L, Settings::NativeKeyboard::L},
+ {Qt::Key_M, Settings::NativeKeyboard::M},
+ {Qt::Key_N, Settings::NativeKeyboard::N},
+ {Qt::Key_O, Settings::NativeKeyboard::O},
+ {Qt::Key_P, Settings::NativeKeyboard::P},
+ {Qt::Key_Q, Settings::NativeKeyboard::Q},
+ {Qt::Key_R, Settings::NativeKeyboard::R},
+ {Qt::Key_S, Settings::NativeKeyboard::S},
+ {Qt::Key_T, Settings::NativeKeyboard::T},
+ {Qt::Key_U, Settings::NativeKeyboard::U},
+ {Qt::Key_V, Settings::NativeKeyboard::V},
+ {Qt::Key_W, Settings::NativeKeyboard::W},
+ {Qt::Key_X, Settings::NativeKeyboard::X},
+ {Qt::Key_Y, Settings::NativeKeyboard::Y},
+ {Qt::Key_Z, Settings::NativeKeyboard::Z},
+ {Qt::Key_1, Settings::NativeKeyboard::N1},
+ {Qt::Key_2, Settings::NativeKeyboard::N2},
+ {Qt::Key_3, Settings::NativeKeyboard::N3},
+ {Qt::Key_4, Settings::NativeKeyboard::N4},
+ {Qt::Key_5, Settings::NativeKeyboard::N5},
+ {Qt::Key_6, Settings::NativeKeyboard::N6},
+ {Qt::Key_7, Settings::NativeKeyboard::N7},
+ {Qt::Key_8, Settings::NativeKeyboard::N8},
+ {Qt::Key_9, Settings::NativeKeyboard::N9},
+ {Qt::Key_0, Settings::NativeKeyboard::N0},
+ {Qt::Key_Return, Settings::NativeKeyboard::Return},
+ {Qt::Key_Escape, Settings::NativeKeyboard::Escape},
+ {Qt::Key_Backspace, Settings::NativeKeyboard::Backspace},
+ {Qt::Key_Tab, Settings::NativeKeyboard::Tab},
+ {Qt::Key_Space, Settings::NativeKeyboard::Space},
+ {Qt::Key_Minus, Settings::NativeKeyboard::Minus},
+ {Qt::Key_Plus, Settings::NativeKeyboard::Plus},
+ {Qt::Key_questiondown, Settings::NativeKeyboard::Plus},
+ {Qt::Key_BracketLeft, Settings::NativeKeyboard::OpenBracket},
+ {Qt::Key_BraceLeft, Settings::NativeKeyboard::OpenBracket},
+ {Qt::Key_BracketRight, Settings::NativeKeyboard::CloseBracket},
+ {Qt::Key_BraceRight, Settings::NativeKeyboard::CloseBracket},
+ {Qt::Key_Bar, Settings::NativeKeyboard::Pipe},
+ {Qt::Key_Dead_Tilde, Settings::NativeKeyboard::Tilde},
+ {Qt::Key_Ntilde, Settings::NativeKeyboard::Semicolon},
+ {Qt::Key_Semicolon, Settings::NativeKeyboard::Semicolon},
+ {Qt::Key_Apostrophe, Settings::NativeKeyboard::Quote},
+ {Qt::Key_Dead_Grave, Settings::NativeKeyboard::Backquote},
+ {Qt::Key_Comma, Settings::NativeKeyboard::Comma},
+ {Qt::Key_Period, Settings::NativeKeyboard::Period},
+ {Qt::Key_Slash, Settings::NativeKeyboard::Slash},
+ {Qt::Key_CapsLock, Settings::NativeKeyboard::CapsLockKey},
+ {Qt::Key_F1, Settings::NativeKeyboard::F1},
+ {Qt::Key_F2, Settings::NativeKeyboard::F2},
+ {Qt::Key_F3, Settings::NativeKeyboard::F3},
+ {Qt::Key_F4, Settings::NativeKeyboard::F4},
+ {Qt::Key_F5, Settings::NativeKeyboard::F5},
+ {Qt::Key_F6, Settings::NativeKeyboard::F6},
+ {Qt::Key_F7, Settings::NativeKeyboard::F7},
+ {Qt::Key_F8, Settings::NativeKeyboard::F8},
+ {Qt::Key_F9, Settings::NativeKeyboard::F9},
+ {Qt::Key_F10, Settings::NativeKeyboard::F10},
+ {Qt::Key_F11, Settings::NativeKeyboard::F11},
+ {Qt::Key_F12, Settings::NativeKeyboard::F12},
+ {Qt::Key_Print, Settings::NativeKeyboard::PrintScreen},
+ {Qt::Key_ScrollLock, Settings::NativeKeyboard::ScrollLockKey},
+ {Qt::Key_Pause, Settings::NativeKeyboard::Pause},
+ {Qt::Key_Insert, Settings::NativeKeyboard::Insert},
+ {Qt::Key_Home, Settings::NativeKeyboard::Home},
+ {Qt::Key_PageUp, Settings::NativeKeyboard::PageUp},
+ {Qt::Key_Delete, Settings::NativeKeyboard::Delete},
+ {Qt::Key_End, Settings::NativeKeyboard::End},
+ {Qt::Key_PageDown, Settings::NativeKeyboard::PageDown},
+ {Qt::Key_Right, Settings::NativeKeyboard::Right},
+ {Qt::Key_Left, Settings::NativeKeyboard::Left},
+ {Qt::Key_Down, Settings::NativeKeyboard::Down},
+ {Qt::Key_Up, Settings::NativeKeyboard::Up},
+ {Qt::Key_NumLock, Settings::NativeKeyboard::NumLockKey},
+ // Numpad keys are missing here
+ {Qt::Key_F13, Settings::NativeKeyboard::F13},
+ {Qt::Key_F14, Settings::NativeKeyboard::F14},
+ {Qt::Key_F15, Settings::NativeKeyboard::F15},
+ {Qt::Key_F16, Settings::NativeKeyboard::F16},
+ {Qt::Key_F17, Settings::NativeKeyboard::F17},
+ {Qt::Key_F18, Settings::NativeKeyboard::F18},
+ {Qt::Key_F19, Settings::NativeKeyboard::F19},
+ {Qt::Key_F20, Settings::NativeKeyboard::F20},
+ {Qt::Key_F21, Settings::NativeKeyboard::F21},
+ {Qt::Key_F22, Settings::NativeKeyboard::F22},
+ {Qt::Key_F23, Settings::NativeKeyboard::F23},
+ {Qt::Key_F24, Settings::NativeKeyboard::F24},
+ // {Qt::..., Settings::NativeKeyboard::KPComma},
+ // {Qt::..., Settings::NativeKeyboard::Ro},
+ {Qt::Key_Hiragana_Katakana, Settings::NativeKeyboard::KatakanaHiragana},
+ {Qt::Key_yen, Settings::NativeKeyboard::Yen},
+ {Qt::Key_Henkan, Settings::NativeKeyboard::Henkan},
+ {Qt::Key_Muhenkan, Settings::NativeKeyboard::Muhenkan},
+ // {Qt::..., Settings::NativeKeyboard::NumPadCommaPc98},
+ {Qt::Key_Hangul, Settings::NativeKeyboard::HangulEnglish},
+ {Qt::Key_Hangul_Hanja, Settings::NativeKeyboard::Hanja},
+ {Qt::Key_Katakana, Settings::NativeKeyboard::KatakanaKey},
+ {Qt::Key_Hiragana, Settings::NativeKeyboard::HiraganaKey},
+ {Qt::Key_Zenkaku_Hankaku, Settings::NativeKeyboard::ZenkakuHankaku},
+ // Modifier keys are handled by the modifier property
+ };
+
+ for (const auto& [qkey, nkey] : key_map) {
+ if (qt_key == qkey) {
+ return nkey;
+ }
}
+
+ return Settings::NativeKeyboard::None;
}
int 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)
: QWizard(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint),
ui{std::make_unique<Ui::CompatDB>()}, telemetry_session{telemetry_session_} {
ui->setupUi(this);
- connect(ui->radioButton_Perfect, &QRadioButton::clicked, this, &CompatDB::EnableNext);
- connect(ui->radioButton_Great, &QRadioButton::clicked, this, &CompatDB::EnableNext);
- connect(ui->radioButton_Okay, &QRadioButton::clicked, this, &CompatDB::EnableNext);
- connect(ui->radioButton_Bad, &QRadioButton::clicked, this, &CompatDB::EnableNext);
- connect(ui->radioButton_IntroMenu, &QRadioButton::clicked, this, &CompatDB::EnableNext);
- connect(ui->radioButton_WontBoot, &QRadioButton::clicked, this, &CompatDB::EnableNext);
+
+ connect(ui->radioButton_GameBoot_Yes, &QRadioButton::clicked, this, &CompatDB::EnableNext);
+ connect(ui->radioButton_GameBoot_No, &QRadioButton::clicked, this, &CompatDB::EnableNext);
+ connect(ui->radioButton_Gameplay_Yes, &QRadioButton::clicked, this, &CompatDB::EnableNext);
+ connect(ui->radioButton_Gameplay_No, &QRadioButton::clicked, this, &CompatDB::EnableNext);
+ connect(ui->radioButton_NoFreeze_Yes, &QRadioButton::clicked, this, &CompatDB::EnableNext);
+ connect(ui->radioButton_NoFreeze_No, &QRadioButton::clicked, this, &CompatDB::EnableNext);
+ connect(ui->radioButton_Complete_Yes, &QRadioButton::clicked, this, &CompatDB::EnableNext);
+ connect(ui->radioButton_Complete_No, &QRadioButton::clicked, this, &CompatDB::EnableNext);
+ connect(ui->radioButton_Graphical_Major, &QRadioButton::clicked, this, &CompatDB::EnableNext);
+ connect(ui->radioButton_Graphical_Minor, &QRadioButton::clicked, this, &CompatDB::EnableNext);
+ connect(ui->radioButton_Graphical_No, &QRadioButton::clicked, this, &CompatDB::EnableNext);
+ connect(ui->radioButton_Audio_Major, &QRadioButton::clicked, this, &CompatDB::EnableNext);
+ connect(ui->radioButton_Audio_Minor, &QRadioButton::clicked, this, &CompatDB::EnableNext);
+ connect(ui->radioButton_Audio_No, &QRadioButton::clicked, this, &CompatDB::EnableNext);
+
connect(button(NextButton), &QPushButton::clicked, this, &CompatDB::Submit);
connect(&testcase_watcher, &QFutureWatcher<bool>::finished, this,
&CompatDB::OnTestcaseSubmitted);
@@ -30,29 +40,82 @@ CompatDB::~CompatDB() = default;
enum class CompatDBPage {
Intro = 0,
- Selection = 1,
- Final = 2,
+ GameBoot = 1,
+ GamePlay = 2,
+ Freeze = 3,
+ Completion = 4,
+ Graphical = 5,
+ Audio = 6,
+ Final = 7,
};
void CompatDB::Submit() {
- QButtonGroup* compatibility = new QButtonGroup(this);
- compatibility->addButton(ui->radioButton_Perfect, 0);
- compatibility->addButton(ui->radioButton_Great, 1);
- compatibility->addButton(ui->radioButton_Okay, 2);
- compatibility->addButton(ui->radioButton_Bad, 3);
- compatibility->addButton(ui->radioButton_IntroMenu, 4);
- compatibility->addButton(ui->radioButton_WontBoot, 5);
+ QButtonGroup* compatibility_GameBoot = new QButtonGroup(this);
+ compatibility_GameBoot->addButton(ui->radioButton_GameBoot_Yes, 0);
+ compatibility_GameBoot->addButton(ui->radioButton_GameBoot_No, 1);
+
+ QButtonGroup* compatibility_Gameplay = new QButtonGroup(this);
+ compatibility_Gameplay->addButton(ui->radioButton_Gameplay_Yes, 0);
+ compatibility_Gameplay->addButton(ui->radioButton_Gameplay_No, 1);
+
+ QButtonGroup* compatibility_NoFreeze = new QButtonGroup(this);
+ compatibility_NoFreeze->addButton(ui->radioButton_NoFreeze_Yes, 0);
+ compatibility_NoFreeze->addButton(ui->radioButton_NoFreeze_No, 1);
+
+ QButtonGroup* compatibility_Complete = new QButtonGroup(this);
+ compatibility_Complete->addButton(ui->radioButton_Complete_Yes, 0);
+ compatibility_Complete->addButton(ui->radioButton_Complete_No, 1);
+
+ QButtonGroup* compatibility_Graphical = new QButtonGroup(this);
+ compatibility_Graphical->addButton(ui->radioButton_Graphical_Major, 0);
+ compatibility_Graphical->addButton(ui->radioButton_Graphical_Minor, 1);
+ compatibility_Graphical->addButton(ui->radioButton_Graphical_No, 2);
+
+ QButtonGroup* compatibility_Audio = new QButtonGroup(this);
+ compatibility_Audio->addButton(ui->radioButton_Audio_Major, 0);
+ compatibility_Graphical->addButton(ui->radioButton_Audio_Minor, 1);
+ compatibility_Audio->addButton(ui->radioButton_Audio_No, 2);
+
+ const int compatiblity = static_cast<int>(CalculateCompatibility());
+
switch ((static_cast<CompatDBPage>(currentId()))) {
- case CompatDBPage::Selection:
- if (compatibility->checkedId() == -1) {
+ case CompatDBPage::Intro:
+ break;
+ case CompatDBPage::GameBoot:
+ if (compatibility_GameBoot->checkedId() == -1) {
+ button(NextButton)->setEnabled(false);
+ }
+ break;
+ case CompatDBPage::GamePlay:
+ if (compatibility_Gameplay->checkedId() == -1) {
+ button(NextButton)->setEnabled(false);
+ }
+ break;
+ case CompatDBPage::Freeze:
+ if (compatibility_NoFreeze->checkedId() == -1) {
+ button(NextButton)->setEnabled(false);
+ }
+ break;
+ case CompatDBPage::Completion:
+ if (compatibility_Complete->checkedId() == -1) {
+ button(NextButton)->setEnabled(false);
+ }
+ break;
+ case CompatDBPage::Graphical:
+ if (compatibility_Graphical->checkedId() == -1) {
+ button(NextButton)->setEnabled(false);
+ }
+ break;
+ case CompatDBPage::Audio:
+ if (compatibility_Audio->checkedId() == -1) {
button(NextButton)->setEnabled(false);
}
break;
case CompatDBPage::Final:
back();
- LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId());
+ LOG_INFO(Frontend, "Compatibility Rating: {}", compatiblity);
telemetry_session.AddField(Common::Telemetry::FieldType::UserFeedback, "Compatibility",
- compatibility->checkedId());
+ compatiblity);
button(NextButton)->setEnabled(false);
button(NextButton)->setText(tr("Submitting"));
@@ -63,9 +126,70 @@ void CompatDB::Submit() {
break;
default:
LOG_ERROR(Frontend, "Unexpected page: {}", currentId());
+ break;
+ }
+}
+
+int CompatDB::nextId() const {
+ switch ((static_cast<CompatDBPage>(currentId()))) {
+ case CompatDBPage::Intro:
+ return static_cast<int>(CompatDBPage::GameBoot);
+ case CompatDBPage::GameBoot:
+ if (ui->radioButton_GameBoot_No->isChecked()) {
+ return static_cast<int>(CompatDBPage::Final);
+ }
+ return static_cast<int>(CompatDBPage::GamePlay);
+ case CompatDBPage::GamePlay:
+ if (ui->radioButton_Gameplay_No->isChecked()) {
+ return static_cast<int>(CompatDBPage::Final);
+ }
+ return static_cast<int>(CompatDBPage::Freeze);
+ case CompatDBPage::Freeze:
+ if (ui->radioButton_NoFreeze_No->isChecked()) {
+ return static_cast<int>(CompatDBPage::Final);
+ }
+ return static_cast<int>(CompatDBPage::Completion);
+ case CompatDBPage::Completion:
+ if (ui->radioButton_Complete_No->isChecked()) {
+ return static_cast<int>(CompatDBPage::Final);
+ }
+ return static_cast<int>(CompatDBPage::Graphical);
+ case CompatDBPage::Graphical:
+ return static_cast<int>(CompatDBPage::Audio);
+ case CompatDBPage::Audio:
+ return static_cast<int>(CompatDBPage::Final);
+ case CompatDBPage::Final:
+ return -1;
+ default:
+ LOG_ERROR(Frontend, "Unexpected page: {}", currentId());
+ return static_cast<int>(CompatDBPage::Intro);
}
}
+CompatibilityStatus CompatDB::CalculateCompatibility() const {
+ if (ui->radioButton_GameBoot_No->isChecked()) {
+ return CompatibilityStatus::WontBoot;
+ }
+
+ if (ui->radioButton_Gameplay_No->isChecked()) {
+ return CompatibilityStatus::IntroMenu;
+ }
+
+ if (ui->radioButton_NoFreeze_No->isChecked() || ui->radioButton_Complete_No->isChecked()) {
+ return CompatibilityStatus::Ingame;
+ }
+
+ if (ui->radioButton_Graphical_Major->isChecked() || ui->radioButton_Audio_Major->isChecked()) {
+ return CompatibilityStatus::Ingame;
+ }
+
+ if (ui->radioButton_Graphical_Minor->isChecked() || ui->radioButton_Audio_Minor->isChecked()) {
+ return CompatibilityStatus::Playable;
+ }
+
+ return CompatibilityStatus::Perfect;
+}
+
void CompatDB::OnTestcaseSubmitted() {
if (!testcase_watcher.result()) {
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 {
class CompatDB;
}
+enum class CompatibilityStatus {
+ Perfect = 0,
+ Playable = 1,
+ // Unused: Okay = 2,
+ Ingame = 3,
+ IntroMenu = 4,
+ WontBoot = 5,
+};
+
class CompatDB : public QWizard {
Q_OBJECT
public:
explicit CompatDB(Core::TelemetrySession& telemetry_session_, QWidget* parent = nullptr);
~CompatDB();
+ int nextId() const override;
private:
QFutureWatcher<bool> testcase_watcher;
@@ -25,6 +35,7 @@ private:
std::unique_ptr<Ui::CompatDB> ui;
void Submit();
+ CompatibilityStatus CalculateCompatibility() const;
void OnTestcaseSubmitted();
void EnableNext();
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 @@
</item>
</layout>
</widget>
- <widget class="QWizardPage" name="wizard_Report">
+ <widget class="QWizardPage" name="wizard_GameBoot">
<property name="title">
<string>Report Game Compatibility</string>
</property>
<attribute name="pageId">
<string notr="true">1</string>
</attribute>
- <layout class="QFormLayout" name="formLayout">
+ <layout class="QFormLayout" name="formLayout1">
+ <item row="0" column="0" colspan="2">
+ <widget class="QLabel" name="lbl_Independent1">
+ <property name="font">
+ <font>
+ <pointsize>10</pointsize>
+ </font>
+ </property>
+ <property name="text">
+ <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>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" colspan="2">
+ <spacer name="verticalSpacer1">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
<item row="2" column="0">
- <widget class="QRadioButton" name="radioButton_Perfect">
+ <widget class="QRadioButton" name="radioButton_GameBoot_Yes">
<property name="text">
- <string>Perfect</string>
+ <string>Yes The game starts to output video or audio</string>
</property>
</widget>
</item>
- <item row="2" column="1">
- <widget class="QLabel" name="lbl_Perfect">
+ <item row="4" column="0">
+ <widget class="QRadioButton" name="radioButton_GameBoot_No">
<property name="text">
- <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>
+ <string>No The game doesn't get past the &quot;Launching...&quot; screen</string>
</property>
- <property name="wordWrap">
- <bool>true</bool>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWizardPage" name="wizard_GamePlay">
+ <property name="title">
+ <string>Report Game Compatibility</string>
+ </property>
+ <attribute name="pageId">
+ <string notr="true">2</string>
+ </attribute>
+ <layout class="QFormLayout" name="formLayout2">
+ <item row="2" column="0">
+ <widget class="QRadioButton" name="radioButton_Gameplay_Yes">
+ <property name="text">
+ <string>Yes The game gets past the intro/menu and into gameplay</string>
</property>
</widget>
</item>
<item row="4" column="0">
- <widget class="QRadioButton" name="radioButton_Great">
+ <widget class="QRadioButton" name="radioButton_Gameplay_No">
<property name="text">
- <string>Great</string>
+ <string>No The game crashes or freezes while loading or using the menu</string>
</property>
</widget>
</item>
- <item row="4" column="1">
- <widget class="QLabel" name="lbl_Great">
+ <item row="0" column="0" colspan="2">
+ <widget class="QLabel" name="lbl_Independent2">
+ <property name="font">
+ <font>
+ <pointsize>10</pointsize>
+ </font>
+ </property>
<property name="text">
- <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>
+ <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>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
- <item row="5" column="0">
- <widget class="QRadioButton" name="radioButton_Okay">
+ <item row="1" column="0" colspan="2">
+ <spacer name="verticalSpacer2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWizardPage" name="wizard_NoFreeze">
+ <property name="title">
+ <string>Report Game Compatibility</string>
+ </property>
+ <attribute name="pageId">
+ <string notr="true">3</string>
+ </attribute>
+ <layout class="QFormLayout" name="formLayout3">
+ <item row="2" column="0">
+ <widget class="QRadioButton" name="radioButton_NoFreeze_Yes">
<property name="text">
- <string>Okay</string>
+ <string>Yes The game works without crashes</string>
</property>
</widget>
</item>
- <item row="5" column="1">
- <widget class="QLabel" name="lbl_Okay">
+ <item row="4" column="0">
+ <widget class="QRadioButton" name="radioButton_NoFreeze_No">
+ <property name="text">
+ <string>No The game crashes or freezes during gameplay</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" colspan="2">
+ <widget class="QLabel" name="lbl_Independent3">
+ <property name="font">
+ <font>
+ <pointsize>10</pointsize>
+ </font>
+ </property>
<property name="text">
- <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>
+ <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>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
- <item row="6" column="0">
- <widget class="QRadioButton" name="radioButton_Bad">
+ <item row="1" column="0" colspan="2">
+ <spacer name="verticalSpacer3">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWizardPage" name="wizard_Complete">
+ <property name="title">
+ <string>Report Game Compatibility</string>
+ </property>
+ <attribute name="pageId">
+ <string notr="true">4</string>
+ </attribute>
+ <layout class="QFormLayout" name="formLayout4">
+ <item row="2" column="0">
+ <widget class="QRadioButton" name="radioButton_Complete_Yes">
<property name="text">
- <string>Bad</string>
+ <string>Yes The game can be finished without any workarounds</string>
</property>
</widget>
</item>
- <item row="6" column="1">
- <widget class="QLabel" name="lbl_Bad">
+ <item row="4" column="0">
+ <widget class="QRadioButton" name="radioButton_Complete_No">
+ <property name="text">
+ <string>No The game can't progress past a certain area</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" colspan="2">
+ <widget class="QLabel" name="lbl_Independent4">
+ <property name="font">
+ <font>
+ <pointsize>10</pointsize>
+ </font>
+ </property>
<property name="text">
- <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>
+ <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>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
- <item row="7" column="0">
- <widget class="QRadioButton" name="radioButton_IntroMenu">
+ <item row="1" column="0" colspan="2">
+ <spacer name="verticalSpacer4">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWizardPage" name="wizard_Graphical">
+ <property name="title">
+ <string>Report Game Compatibility</string>
+ </property>
+ <attribute name="pageId">
+ <string notr="true">5</string>
+ </attribute>
+ <layout class="QFormLayout" name="formLayout5">
+ <item row="2" column="0">
+ <widget class="QRadioButton" name="radioButton_Graphical_Major">
+ <property name="text">
+ <string>Major The game has major graphical errors</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QRadioButton" name="radioButton_Graphical_Minor">
<property name="text">
- <string>Intro/Menu</string>
+ <string>Minor The game has minor graphical errors</string>
</property>
</widget>
</item>
- <item row="7" column="1">
- <widget class="QLabel" name="lbl_IntroMenu">
+ <item row="6" column="0">
+ <widget class="QRadioButton" name="radioButton_Graphical_No">
+ <property name="text">
+ <string>None Everything is rendered as it looks on the Nintendo Switch</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" colspan="2">
+ <widget class="QLabel" name="lbl_Independent5">
+ <property name="font">
+ <font>
+ <pointsize>10</pointsize>
+ </font>
+ </property>
<property name="text">
- <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>
+ <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>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
- <item row="8" column="0">
- <widget class="QRadioButton" name="radioButton_WontBoot">
- <property name="text">
- <string>Won't Boot</string>
+ <item row="1" column="0" colspan="2">
+ <spacer name="verticalSpacer5">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
</property>
- <property name="checkable">
- <bool>true</bool>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
</property>
- <property name="checked">
- <bool>false</bool>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWizardPage" name="wizard_Audio">
+ <property name="title">
+ <string>Report Game Compatibility</string>
+ </property>
+ <attribute name="pageId">
+ <string notr="true">6</string>
+ </attribute>
+ <layout class="QFormLayout" name="formLayout6">
+ <item row="2" column="0">
+ <widget class="QRadioButton" name="radioButton_Audio_Major">
+ <property name="text">
+ <string>Major The game has major audio errors</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QRadioButton" name="radioButton_Audio_Minor">
+ <property name="text">
+ <string>Minor The game has minor audio errors</string>
</property>
</widget>
</item>
- <item row="8" column="1">
- <widget class="QLabel" name="lbl_WontBoot">
+ <item row="6" column="0">
+ <widget class="QRadioButton" name="radioButton_Audio_No">
<property name="text">
- <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>
+ <string>None Audio is played perfectly</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
- <widget class="QLabel" name="lbl_Independent">
+ <widget class="QLabel" name="lbl_Independent6">
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
- <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>
+ <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>
</property>
<property name="wordWrap">
<bool>true</bool>
@@ -187,7 +370,7 @@
</widget>
</item>
<item row="1" column="0" colspan="2">
- <spacer name="verticalSpacer">
+ <spacer name="verticalSpacer6">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
@@ -206,7 +389,7 @@
<string>Thank you for your submission!</string>
</property>
<attribute name="pageId">
- <string notr="true">2</string>
+ <string notr="true">7</string>
</attribute>
</widget>
</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 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
+#include <functional>
+#include <QDialog>
+#include <QDialogButtonBox>
#include <QFileDialog>
#include <QGraphicsItem>
#include <QHeaderView>
@@ -108,9 +111,12 @@ ConfigureProfileManager::ConfigureProfileManager(const Core::System& system_, QW
connect(ui->pm_add, &QPushButton::clicked, this, &ConfigureProfileManager::AddUser);
connect(ui->pm_rename, &QPushButton::clicked, this, &ConfigureProfileManager::RenameUser);
- connect(ui->pm_remove, &QPushButton::clicked, this, &ConfigureProfileManager::DeleteUser);
+ connect(ui->pm_remove, &QPushButton::clicked, this,
+ &ConfigureProfileManager::ConfirmDeleteUser);
connect(ui->pm_set_image, &QPushButton::clicked, this, &ConfigureProfileManager::SetUserImage);
+ confirm_dialog = new ConfigureProfileManagerDeleteDialog(this);
+
scene = new QGraphicsScene;
ui->current_user_icon->setScene(scene);
@@ -230,26 +236,23 @@ void ConfigureProfileManager::RenameUser() {
UpdateCurrentUser();
}
-void ConfigureProfileManager::DeleteUser() {
+void ConfigureProfileManager::ConfirmDeleteUser() {
const auto index = tree_view->currentIndex().row();
const auto uuid = profile_manager->GetUser(index);
ASSERT(uuid);
const auto username = GetAccountUsername(*profile_manager, *uuid);
- const auto confirm = QMessageBox::question(
- this, tr("Confirm Delete"),
- tr("You are about to delete user with name \"%1\". Are you sure?").arg(username));
-
- if (confirm == QMessageBox::No) {
- return;
- }
+ confirm_dialog->SetInfo(username, *uuid, [this, uuid]() { DeleteUser(*uuid); });
+ confirm_dialog->show();
+}
+void ConfigureProfileManager::DeleteUser(const Common::UUID& uuid) {
if (Settings::values.current_user.GetValue() == tree_view->currentIndex().row()) {
Settings::values.current_user = 0;
}
UpdateCurrentUser();
- if (!profile_manager->RemoveUser(*uuid)) {
+ if (!profile_manager->RemoveUser(uuid)) {
return;
}
@@ -319,3 +322,47 @@ void ConfigureProfileManager::SetUserImage() {
new QStandardItem{GetIcon(*uuid), FormatUserEntryText(username, *uuid)});
UpdateCurrentUser();
}
+
+ConfigureProfileManagerDeleteDialog::ConfigureProfileManagerDeleteDialog(QWidget* parent)
+ : QDialog{parent} {
+ auto dialog_vbox_layout = new QVBoxLayout(this);
+ dialog_button_box =
+ new QDialogButtonBox(QDialogButtonBox::Yes | QDialogButtonBox::No, Qt::Horizontal, parent);
+ auto label_message =
+ new QLabel(tr("Delete this user? All of the user's save data will be deleted."), this);
+ label_info = new QLabel(this);
+ auto dialog_hbox_layout_widget = new QWidget(this);
+ auto dialog_hbox_layout = new QHBoxLayout(dialog_hbox_layout_widget);
+ icon_scene = new QGraphicsScene(0, 0, 64, 64, this);
+ auto icon_view = new QGraphicsView(icon_scene, this);
+
+ dialog_hbox_layout_widget->setLayout(dialog_hbox_layout);
+ icon_view->setMaximumSize(64, 64);
+ icon_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ icon_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ this->setLayout(dialog_vbox_layout);
+ this->setWindowTitle(tr("Confirm Delete"));
+ this->setSizeGripEnabled(false);
+ dialog_vbox_layout->addWidget(label_message);
+ dialog_vbox_layout->addWidget(dialog_hbox_layout_widget);
+ dialog_vbox_layout->addWidget(dialog_button_box);
+ dialog_hbox_layout->addWidget(icon_view);
+ dialog_hbox_layout->addWidget(label_info);
+
+ connect(dialog_button_box, &QDialogButtonBox::rejected, this, [this]() { close(); });
+}
+
+ConfigureProfileManagerDeleteDialog::~ConfigureProfileManagerDeleteDialog() = default;
+
+void ConfigureProfileManagerDeleteDialog::SetInfo(const QString& username, const Common::UUID& uuid,
+ std::function<void()> accept_callback) {
+ label_info->setText(
+ tr("Name: %1\nUUID: %2").arg(username, QString::fromStdString(uuid.FormattedString())));
+ icon_scene->clear();
+ icon_scene->addPixmap(GetIcon(uuid));
+
+ connect(dialog_button_box, &QDialogButtonBox::accepted, this, [this, accept_callback]() {
+ close();
+ accept_callback();
+ });
+}
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 @@
#pragma once
+#include <functional>
#include <memory>
+#include <QDialog>
#include <QList>
#include <QWidget>
+namespace Common {
+struct UUID;
+}
+
namespace Core {
class System;
}
class QGraphicsScene;
+class QDialogButtonBox;
+class QLabel;
class QStandardItem;
class QStandardItemModel;
class QTreeView;
@@ -26,6 +34,20 @@ namespace Ui {
class ConfigureProfileManager;
}
+class ConfigureProfileManagerDeleteDialog : public QDialog {
+public:
+ explicit ConfigureProfileManagerDeleteDialog(QWidget* parent);
+ ~ConfigureProfileManagerDeleteDialog();
+
+ void SetInfo(const QString& username, const Common::UUID& uuid,
+ std::function<void()> accept_callback);
+
+private:
+ QDialogButtonBox* dialog_button_box;
+ QGraphicsScene* icon_scene;
+ QLabel* label_info;
+};
+
class ConfigureProfileManager : public QWidget {
Q_OBJECT
@@ -47,7 +69,8 @@ private:
void SelectUser(const QModelIndex& index);
void AddUser();
void RenameUser();
- void DeleteUser();
+ void ConfirmDeleteUser();
+ void DeleteUser(const Common::UUID& uuid);
void SetUserImage();
QVBoxLayout* layout;
@@ -55,6 +78,8 @@ private:
QStandardItemModel* item_model;
QGraphicsScene* scene;
+ ConfigureProfileManagerDeleteDialog* confirm_dialog;
+
std::vector<QList<QStandardItem*>> list_items;
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 @@
<height>48</height>
</size>
</property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</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:
const char* tooltip;
};
// clang-format off
+ const auto ingame_status =
+ CompatStatus{QStringLiteral("#f2d624"), QT_TR_NOOP("Ingame"), QT_TR_NOOP("Game starts, but crashes or major glitches prevent it from being completed.")};
static const std::map<QString, CompatStatus> status_data = {
- {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.")}},
- {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.")}},
- {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.")}},
- {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.")}},
- {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.")}},
+ {QStringLiteral("0"), {QStringLiteral("#5c93ed"), QT_TR_NOOP("Perfect"), QT_TR_NOOP("Game can be played without issues.")}},
+ {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.")}},
+ {QStringLiteral("2"), ingame_status},
+ {QStringLiteral("3"), ingame_status}, // Fallback for the removed "Okay" category
+ {QStringLiteral("4"), {QStringLiteral("#FF0000"), QT_TR_NOOP("Intro/Menu"), QT_TR_NOOP("Game loads, but is unable to progress past the Start Screen.")}},
{QStringLiteral("5"), {QStringLiteral("#828282"), QT_TR_NOOP("Won't Boot"), QT_TR_NOOP("The game crashes when attempting to startup.")}},
{QStringLiteral("99"), {QStringLiteral("#000000"), QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}},
};
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
const auto override_build =
fmt::format(fmt::runtime(std::string(Common::g_title_bar_format_idle)), build_id);
const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build;
+ const auto processor_count = std::thread::hardware_concurrency();
LOG_INFO(Frontend, "yuzu Version: {}", yuzu_build_version);
LogRuntimes();
@@ -360,7 +361,11 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan
}
}
LOG_INFO(Frontend, "Host CPU: {}", cpu_string);
+ if (std::optional<int> processor_core = Common::GetProcessorCount()) {
+ LOG_INFO(Frontend, "Host CPU Cores: {}", *processor_core);
+ }
#endif
+ LOG_INFO(Frontend, "Host CPU Threads: {}", processor_count);
LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString());
LOG_INFO(Frontend, "Host RAM: {:.2f} GiB",
Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB});
@@ -1951,6 +1956,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
}
default:
UNIMPLEMENTED();
+ break;
}
const QString qpath = QString::fromStdString(Common::FS::PathToUTF8String(path));
@@ -2018,38 +2024,50 @@ static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src
return true;
}
+QString GMainWindow::GetGameListErrorRemoving(InstalledEntryType type) const {
+ switch (type) {
+ case InstalledEntryType::Game:
+ return tr("Error Removing Contents");
+ case InstalledEntryType::Update:
+ return tr("Error Removing Update");
+ case InstalledEntryType::AddOnContent:
+ return tr("Error Removing DLC");
+ default:
+ return QStringLiteral("Error Removing <Invalid Type>");
+ }
+}
void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type) {
- const QString entry_type = [type] {
+ const QString entry_question = [type] {
switch (type) {
case InstalledEntryType::Game:
- return tr("Contents");
+ return tr("Remove Installed Game Contents?");
case InstalledEntryType::Update:
- return tr("Update");
+ return tr("Remove Installed Game Update?");
case InstalledEntryType::AddOnContent:
- return tr("DLC");
+ return tr("Remove Installed Game DLC?");
default:
- return QString{};
+ return QStringLiteral("Remove Installed Game <Invalid Type>?");
}
}();
- if (QMessageBox::question(
- this, tr("Remove Entry"), tr("Remove Installed Game %1?").arg(entry_type),
- QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) {
+ if (QMessageBox::question(this, tr("Remove Entry"), entry_question,
+ QMessageBox::Yes | QMessageBox::No,
+ QMessageBox::No) != QMessageBox::Yes) {
return;
}
switch (type) {
case InstalledEntryType::Game:
- RemoveBaseContent(program_id, entry_type);
+ RemoveBaseContent(program_id, type);
[[fallthrough]];
case InstalledEntryType::Update:
- RemoveUpdateContent(program_id, entry_type);
+ RemoveUpdateContent(program_id, type);
if (type != InstalledEntryType::Game) {
break;
}
[[fallthrough]];
case InstalledEntryType::AddOnContent:
- RemoveAddOnContent(program_id, entry_type);
+ RemoveAddOnContent(program_id, type);
break;
}
Common::FS::RemoveDirRecursively(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) /
@@ -2057,7 +2075,7 @@ void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryT
game_list->PopulateAsync(UISettings::values.game_dirs);
}
-void GMainWindow::RemoveBaseContent(u64 program_id, const QString& entry_type) {
+void GMainWindow::RemoveBaseContent(u64 program_id, InstalledEntryType type) {
const auto& fs_controller = system->GetFileSystemController();
const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(program_id) ||
fs_controller.GetSDMCContents()->RemoveExistingEntry(program_id);
@@ -2067,12 +2085,12 @@ void GMainWindow::RemoveBaseContent(u64 program_id, const QString& entry_type) {
tr("Successfully removed the installed base game."));
} else {
QMessageBox::warning(
- this, tr("Error Removing %1").arg(entry_type),
+ this, GetGameListErrorRemoving(type),
tr("The base game is not installed in the NAND and cannot be removed."));
}
}
-void GMainWindow::RemoveUpdateContent(u64 program_id, const QString& entry_type) {
+void GMainWindow::RemoveUpdateContent(u64 program_id, InstalledEntryType type) {
const auto update_id = program_id | 0x800;
const auto& fs_controller = system->GetFileSystemController();
const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(update_id) ||
@@ -2082,12 +2100,12 @@ void GMainWindow::RemoveUpdateContent(u64 program_id, const QString& entry_type)
QMessageBox::information(this, tr("Successfully Removed"),
tr("Successfully removed the installed update."));
} else {
- QMessageBox::warning(this, tr("Error Removing %1").arg(entry_type),
+ QMessageBox::warning(this, GetGameListErrorRemoving(type),
tr("There is no update installed for this title."));
}
}
-void GMainWindow::RemoveAddOnContent(u64 program_id, const QString& entry_type) {
+void GMainWindow::RemoveAddOnContent(u64 program_id, InstalledEntryType type) {
u32 count{};
const auto& fs_controller = system->GetFileSystemController();
const auto dlc_entries = system->GetContentProvider().ListEntriesFilter(
@@ -2105,7 +2123,7 @@ void GMainWindow::RemoveAddOnContent(u64 program_id, const QString& entry_type)
}
if (count == 0) {
- QMessageBox::warning(this, tr("Error Removing %1").arg(entry_type),
+ QMessageBox::warning(this, GetGameListErrorRemoving(type),
tr("There are no DLC installed for this title."));
return;
}
@@ -2803,6 +2821,20 @@ void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_tex
}
void GMainWindow::OnMenuReportCompatibility() {
+ const auto& caps = Common::GetCPUCaps();
+ const bool has_fma = caps.fma || caps.fma4;
+ const auto processor_count = std::thread::hardware_concurrency();
+ const bool has_4threads = processor_count == 0 || processor_count >= 4;
+ const bool has_8gb_ram = Common::GetMemInfo().TotalPhysicalMemory >= 8_GiB;
+ const bool has_broken_vulkan = UISettings::values.has_broken_vulkan;
+
+ if (!has_fma || !has_4threads || !has_8gb_ram || has_broken_vulkan) {
+ QMessageBox::critical(this, tr("Hardware requirements not met"),
+ tr("Your system does not meet the recommended hardware requirements. "
+ "Compatibility reporting has been disabled."));
+ return;
+ }
+
if (!Settings::values.yuzu_token.GetValue().empty() &&
!Settings::values.yuzu_username.GetValue().empty()) {
CompatDB compatdb{system->TelemetrySession(), this};
@@ -3168,6 +3200,7 @@ void GMainWindow::OnToggleGpuAccuracy() {
case Settings::GPUAccuracy::Extreme:
default: {
Settings::values.gpu_accuracy.SetValue(Settings::GPUAccuracy::High);
+ break;
}
}
@@ -3500,6 +3533,7 @@ void GMainWindow::UpdateGPUAccuracyButton() {
default: {
gpu_accuracy_button->setText(tr("GPU ERROR"));
gpu_accuracy_button->setChecked(true);
+ break;
}
}
}
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:
void OnMouseActivity();
private:
- void RemoveBaseContent(u64 program_id, const QString& entry_type);
- void RemoveUpdateContent(u64 program_id, const QString& entry_type);
- void RemoveAddOnContent(u64 program_id, const QString& entry_type);
+ QString GetGameListErrorRemoving(InstalledEntryType type) const;
+ void RemoveBaseContent(u64 program_id, InstalledEntryType type);
+ void RemoveUpdateContent(u64 program_id, InstalledEntryType type);
+ void RemoveAddOnContent(u64 program_id, InstalledEntryType type);
void RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target);
void RemoveAllTransferableShaderCaches(u64 program_id);
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
default:
LOG_CRITICAL(Frontend, "Window manager subsystem not implemented");
std::exit(EXIT_FAILURE);
+ break;
}
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) {
"additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}",
loader_id, error_id, static_cast<Loader::ResultStatus>(error_id));
}
+ break;
}
system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", "SDL");