diff options
Diffstat (limited to 'src')
113 files changed, 2127 insertions, 667 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index 0a1f3bf18..8e3a8f5a8 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt | |||
| @@ -217,7 +217,7 @@ else() | |||
| 217 | endif() | 217 | endif() |
| 218 | 218 | ||
| 219 | target_link_libraries(audio_core PUBLIC common core) | 219 | target_link_libraries(audio_core PUBLIC common core) |
| 220 | if (ARCHITECTURE_x86_64) | 220 | if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) |
| 221 | target_link_libraries(audio_core PRIVATE dynarmic) | 221 | target_link_libraries(audio_core PRIVATE dynarmic) |
| 222 | endif() | 222 | endif() |
| 223 | 223 | ||
diff --git a/src/audio_core/renderer/performance/performance_manager.cpp b/src/audio_core/renderer/performance/performance_manager.cpp index fd5873e1e..8aa0f5ed0 100644 --- a/src/audio_core/renderer/performance/performance_manager.cpp +++ b/src/audio_core/renderer/performance/performance_manager.cpp | |||
| @@ -26,6 +26,7 @@ void PerformanceManager::CreateImpl(const size_t version) { | |||
| 26 | impl = std::make_unique< | 26 | impl = std::make_unique< |
| 27 | PerformanceManagerImpl<PerformanceVersion::Version1, PerformanceFrameHeaderVersion1, | 27 | PerformanceManagerImpl<PerformanceVersion::Version1, PerformanceFrameHeaderVersion1, |
| 28 | PerformanceEntryVersion1, PerformanceDetailVersion1>>(); | 28 | PerformanceEntryVersion1, PerformanceDetailVersion1>>(); |
| 29 | break; | ||
| 29 | } | 30 | } |
| 30 | } | 31 | } |
| 31 | 32 | ||
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index c0555f840..b7c15c191 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -34,6 +34,8 @@ add_library(common STATIC | |||
| 34 | bit_util.h | 34 | bit_util.h |
| 35 | cityhash.cpp | 35 | cityhash.cpp |
| 36 | cityhash.h | 36 | cityhash.h |
| 37 | cache_management.cpp | ||
| 38 | cache_management.h | ||
| 37 | common_funcs.h | 39 | common_funcs.h |
| 38 | common_types.h | 40 | common_types.h |
| 39 | concepts.h | 41 | concepts.h |
diff --git a/src/common/atomic_helpers.h b/src/common/atomic_helpers.h index bef5015c1..aef3b66a4 100644 --- a/src/common/atomic_helpers.h +++ b/src/common/atomic_helpers.h | |||
| @@ -156,6 +156,7 @@ AE_FORCEINLINE void compiler_fence(memory_order order) AE_NO_TSAN { | |||
| 156 | break; | 156 | break; |
| 157 | default: | 157 | default: |
| 158 | assert(false); | 158 | assert(false); |
| 159 | break; | ||
| 159 | } | 160 | } |
| 160 | } | 161 | } |
| 161 | 162 | ||
diff --git a/src/common/cache_management.cpp b/src/common/cache_management.cpp new file mode 100644 index 000000000..57810b76a --- /dev/null +++ b/src/common/cache_management.cpp | |||
| @@ -0,0 +1,60 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <cstring> | ||
| 5 | |||
| 6 | #include "alignment.h" | ||
| 7 | #include "cache_management.h" | ||
| 8 | #include "common_types.h" | ||
| 9 | |||
| 10 | namespace Common { | ||
| 11 | |||
| 12 | #if defined(ARCHITECTURE_x86_64) | ||
| 13 | |||
| 14 | // Most cache operations are no-ops on x86 | ||
| 15 | |||
| 16 | void DataCacheLineCleanByVAToPoU(void* start, size_t size) {} | ||
| 17 | void DataCacheLineCleanAndInvalidateByVAToPoC(void* start, size_t size) {} | ||
| 18 | void DataCacheLineCleanByVAToPoC(void* start, size_t size) {} | ||
| 19 | void DataCacheZeroByVA(void* start, size_t size) { | ||
| 20 | std::memset(start, 0, size); | ||
| 21 | } | ||
| 22 | |||
| 23 | #elif defined(ARCHITECTURE_arm64) | ||
| 24 | |||
| 25 | // BS/DminLine is log2(cache size in words), we want size in bytes | ||
| 26 | #define EXTRACT_DMINLINE(ctr_el0) (1 << ((((ctr_el0) >> 16) & 0xf) + 2)) | ||
| 27 | #define EXTRACT_BS(dczid_el0) (1 << (((dczid_el0)&0xf) + 2)) | ||
| 28 | |||
| 29 | #define DEFINE_DC_OP(op_name, function_name) \ | ||
| 30 | void function_name(void* start, size_t size) { \ | ||
| 31 | size_t ctr_el0; \ | ||
| 32 | asm volatile("mrs %[ctr_el0], ctr_el0\n\t" : [ctr_el0] "=r"(ctr_el0)); \ | ||
| 33 | size_t cacheline_size = EXTRACT_DMINLINE(ctr_el0); \ | ||
| 34 | uintptr_t va_start = reinterpret_cast<uintptr_t>(start); \ | ||
| 35 | uintptr_t va_end = va_start + size; \ | ||
| 36 | for (uintptr_t va = va_start; va < va_end; va += cacheline_size) { \ | ||
| 37 | asm volatile("dc " #op_name ", %[va]\n\t" : : [va] "r"(va) : "memory"); \ | ||
| 38 | } \ | ||
| 39 | } | ||
| 40 | |||
| 41 | #define DEFINE_DC_OP_DCZID(op_name, function_name) \ | ||
| 42 | void function_name(void* start, size_t size) { \ | ||
| 43 | size_t dczid_el0; \ | ||
| 44 | asm volatile("mrs %[dczid_el0], dczid_el0\n\t" : [dczid_el0] "=r"(dczid_el0)); \ | ||
| 45 | size_t cacheline_size = EXTRACT_BS(dczid_el0); \ | ||
| 46 | uintptr_t va_start = reinterpret_cast<uintptr_t>(start); \ | ||
| 47 | uintptr_t va_end = va_start + size; \ | ||
| 48 | for (uintptr_t va = va_start; va < va_end; va += cacheline_size) { \ | ||
| 49 | asm volatile("dc " #op_name ", %[va]\n\t" : : [va] "r"(va) : "memory"); \ | ||
| 50 | } \ | ||
| 51 | } | ||
| 52 | |||
| 53 | DEFINE_DC_OP(cvau, DataCacheLineCleanByVAToPoU); | ||
| 54 | DEFINE_DC_OP(civac, DataCacheLineCleanAndInvalidateByVAToPoC); | ||
| 55 | DEFINE_DC_OP(cvac, DataCacheLineCleanByVAToPoC); | ||
| 56 | DEFINE_DC_OP_DCZID(zva, DataCacheZeroByVA); | ||
| 57 | |||
| 58 | #endif | ||
| 59 | |||
| 60 | } // namespace Common | ||
diff --git a/src/common/cache_management.h b/src/common/cache_management.h new file mode 100644 index 000000000..e467b87e4 --- /dev/null +++ b/src/common/cache_management.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "stdlib.h" | ||
| 7 | |||
| 8 | namespace Common { | ||
| 9 | |||
| 10 | // Data cache instructions enabled at EL0 by SCTLR_EL1.UCI. | ||
| 11 | // VA = virtual address | ||
| 12 | // PoC = point of coherency | ||
| 13 | // PoU = point of unification | ||
| 14 | |||
| 15 | // dc cvau | ||
| 16 | void DataCacheLineCleanByVAToPoU(void* start, size_t size); | ||
| 17 | |||
| 18 | // dc civac | ||
| 19 | void DataCacheLineCleanAndInvalidateByVAToPoC(void* start, size_t size); | ||
| 20 | |||
| 21 | // dc cvac | ||
| 22 | void DataCacheLineCleanByVAToPoC(void* start, size_t size); | ||
| 23 | |||
| 24 | // dc zva | ||
| 25 | void DataCacheZeroByVA(void* start, size_t size); | ||
| 26 | |||
| 27 | } // namespace Common | ||
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index e1e2a90fc..0dad9338a 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h | |||
| @@ -31,8 +31,10 @@ | |||
| 31 | 31 | ||
| 32 | #ifndef _MSC_VER | 32 | #ifndef _MSC_VER |
| 33 | 33 | ||
| 34 | #ifdef ARCHITECTURE_x86_64 | 34 | #if defined(ARCHITECTURE_x86_64) |
| 35 | #define Crash() __asm__ __volatile__("int $3") | 35 | #define Crash() __asm__ __volatile__("int $3") |
| 36 | #elif defined(ARCHITECTURE_arm64) | ||
| 37 | #define Crash() __asm__ __volatile__("brk #0") | ||
| 36 | #else | 38 | #else |
| 37 | #define Crash() exit(1) | 39 | #define Crash() exit(1) |
| 38 | #endif | 40 | #endif |
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp index 7f9659612..909f6cf3f 100644 --- a/src/common/host_memory.cpp +++ b/src/common/host_memory.cpp | |||
| @@ -359,6 +359,12 @@ public: | |||
| 359 | } | 359 | } |
| 360 | }); | 360 | }); |
| 361 | 361 | ||
| 362 | long page_size = sysconf(_SC_PAGESIZE); | ||
| 363 | if (page_size != 0x1000) { | ||
| 364 | LOG_CRITICAL(HW_Memory, "page size {:#x} is incompatible with 4K paging", page_size); | ||
| 365 | throw std::bad_alloc{}; | ||
| 366 | } | ||
| 367 | |||
| 362 | // Backing memory initialization | 368 | // Backing memory initialization |
| 363 | #if defined(__FreeBSD__) && __FreeBSD__ < 13 | 369 | #if defined(__FreeBSD__) && __FreeBSD__ < 13 |
| 364 | // XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30 | 370 | // XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30 |
diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 0a560ebb7..8173462cb 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp | |||
| @@ -151,6 +151,7 @@ void UpdateRescalingInfo() { | |||
| 151 | ASSERT(false); | 151 | ASSERT(false); |
| 152 | info.up_scale = 1; | 152 | info.up_scale = 1; |
| 153 | info.down_shift = 0; | 153 | info.down_shift = 0; |
| 154 | break; | ||
| 154 | } | 155 | } |
| 155 | info.up_factor = static_cast<f32>(info.up_scale) / (1U << info.down_shift); | 156 | info.up_factor = static_cast<f32>(info.up_scale) / (1U << info.down_shift); |
| 156 | info.down_factor = static_cast<f32>(1U << info.down_shift) / info.up_scale; | 157 | info.down_factor = static_cast<f32>(1U << info.down_shift) / info.up_scale; |
diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp index 1a27532d4..e54383a4a 100644 --- a/src/common/x64/cpu_detect.cpp +++ b/src/common/x64/cpu_detect.cpp | |||
| @@ -4,14 +4,27 @@ | |||
| 4 | 4 | ||
| 5 | #include <array> | 5 | #include <array> |
| 6 | #include <cstring> | 6 | #include <cstring> |
| 7 | #include <fstream> | ||
| 7 | #include <iterator> | 8 | #include <iterator> |
| 9 | #include <optional> | ||
| 8 | #include <string_view> | 10 | #include <string_view> |
| 11 | #include <thread> | ||
| 12 | #include <vector> | ||
| 9 | #include "common/bit_util.h" | 13 | #include "common/bit_util.h" |
| 10 | #include "common/common_types.h" | 14 | #include "common/common_types.h" |
| 15 | #include "common/logging/log.h" | ||
| 11 | #include "common/x64/cpu_detect.h" | 16 | #include "common/x64/cpu_detect.h" |
| 12 | 17 | ||
| 18 | #ifdef _WIN32 | ||
| 19 | #include <windows.h> | ||
| 20 | #endif | ||
| 21 | |||
| 13 | #ifdef _MSC_VER | 22 | #ifdef _MSC_VER |
| 14 | #include <intrin.h> | 23 | #include <intrin.h> |
| 24 | |||
| 25 | static inline u64 xgetbv(u32 index) { | ||
| 26 | return _xgetbv(index); | ||
| 27 | } | ||
| 15 | #else | 28 | #else |
| 16 | 29 | ||
| 17 | #if defined(__DragonFly__) || defined(__FreeBSD__) | 30 | #if defined(__DragonFly__) || defined(__FreeBSD__) |
| @@ -39,12 +52,11 @@ static inline void __cpuid(int info[4], u32 function_id) { | |||
| 39 | } | 52 | } |
| 40 | 53 | ||
| 41 | #define _XCR_XFEATURE_ENABLED_MASK 0 | 54 | #define _XCR_XFEATURE_ENABLED_MASK 0 |
| 42 | static inline u64 _xgetbv(u32 index) { | 55 | static inline u64 xgetbv(u32 index) { |
| 43 | u32 eax, edx; | 56 | u32 eax, edx; |
| 44 | __asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(index)); | 57 | __asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(index)); |
| 45 | return ((u64)edx << 32) | eax; | 58 | return ((u64)edx << 32) | eax; |
| 46 | } | 59 | } |
| 47 | |||
| 48 | #endif // _MSC_VER | 60 | #endif // _MSC_VER |
| 49 | 61 | ||
| 50 | namespace Common { | 62 | namespace Common { |
| @@ -107,7 +119,7 @@ static CPUCaps Detect() { | |||
| 107 | // - Is the XSAVE bit set in CPUID? | 119 | // - Is the XSAVE bit set in CPUID? |
| 108 | // - XGETBV result has the XCR bit set. | 120 | // - XGETBV result has the XCR bit set. |
| 109 | if (Common::Bit<28>(cpu_id[2]) && Common::Bit<27>(cpu_id[2])) { | 121 | if (Common::Bit<28>(cpu_id[2]) && Common::Bit<27>(cpu_id[2])) { |
| 110 | if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) { | 122 | if ((xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) { |
| 111 | caps.avx = true; | 123 | caps.avx = true; |
| 112 | if (Common::Bit<12>(cpu_id[2])) | 124 | if (Common::Bit<12>(cpu_id[2])) |
| 113 | caps.fma = true; | 125 | caps.fma = true; |
| @@ -192,4 +204,45 @@ const CPUCaps& GetCPUCaps() { | |||
| 192 | return caps; | 204 | return caps; |
| 193 | } | 205 | } |
| 194 | 206 | ||
| 207 | std::optional<int> GetProcessorCount() { | ||
| 208 | #if defined(_WIN32) | ||
| 209 | // Get the buffer length. | ||
| 210 | DWORD length = 0; | ||
| 211 | GetLogicalProcessorInformation(nullptr, &length); | ||
| 212 | if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { | ||
| 213 | LOG_ERROR(Frontend, "Failed to query core count."); | ||
| 214 | return std::nullopt; | ||
| 215 | } | ||
| 216 | std::vector<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> buffer( | ||
| 217 | length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION)); | ||
| 218 | // Now query the core count. | ||
| 219 | if (!GetLogicalProcessorInformation(buffer.data(), &length)) { | ||
| 220 | LOG_ERROR(Frontend, "Failed to query core count."); | ||
| 221 | return std::nullopt; | ||
| 222 | } | ||
| 223 | return static_cast<int>( | ||
| 224 | std::count_if(buffer.cbegin(), buffer.cend(), [](const auto& proc_info) { | ||
| 225 | return proc_info.Relationship == RelationProcessorCore; | ||
| 226 | })); | ||
| 227 | #elif defined(__unix__) | ||
| 228 | const int thread_count = std::thread::hardware_concurrency(); | ||
| 229 | std::ifstream smt("/sys/devices/system/cpu/smt/active"); | ||
| 230 | char state = '0'; | ||
| 231 | if (smt) { | ||
| 232 | smt.read(&state, sizeof(state)); | ||
| 233 | } | ||
| 234 | switch (state) { | ||
| 235 | case '0': | ||
| 236 | return thread_count; | ||
| 237 | case '1': | ||
| 238 | return thread_count / 2; | ||
| 239 | default: | ||
| 240 | return std::nullopt; | ||
| 241 | } | ||
| 242 | #else | ||
| 243 | // Shame on you | ||
| 244 | return std::nullopt; | ||
| 245 | #endif | ||
| 246 | } | ||
| 247 | |||
| 195 | } // namespace Common | 248 | } // namespace Common |
diff --git a/src/common/x64/cpu_detect.h b/src/common/x64/cpu_detect.h index 6830f3795..ca8db19d6 100644 --- a/src/common/x64/cpu_detect.h +++ b/src/common/x64/cpu_detect.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <optional> | ||
| 7 | #include <string_view> | 8 | #include <string_view> |
| 8 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 9 | 10 | ||
| @@ -74,4 +75,7 @@ struct CPUCaps { | |||
| 74 | */ | 75 | */ |
| 75 | const CPUCaps& GetCPUCaps(); | 76 | const CPUCaps& GetCPUCaps(); |
| 76 | 77 | ||
| 78 | /// Detects CPU core count | ||
| 79 | std::optional<int> GetProcessorCount(); | ||
| 80 | |||
| 77 | } // namespace Common | 81 | } // namespace Common |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index f6e082c36..f67f1ce92 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -497,10 +497,6 @@ add_library(core STATIC | |||
| 497 | hle/service/hid/irsensor/processor_base.h | 497 | hle/service/hid/irsensor/processor_base.h |
| 498 | hle/service/hid/irsensor/tera_plugin_processor.cpp | 498 | hle/service/hid/irsensor/tera_plugin_processor.cpp |
| 499 | hle/service/hid/irsensor/tera_plugin_processor.h | 499 | hle/service/hid/irsensor/tera_plugin_processor.h |
| 500 | hle/service/jit/jit_context.cpp | ||
| 501 | hle/service/jit/jit_context.h | ||
| 502 | hle/service/jit/jit.cpp | ||
| 503 | hle/service/jit/jit.h | ||
| 504 | hle/service/lbl/lbl.cpp | 500 | hle/service/lbl/lbl.cpp |
| 505 | hle/service/lbl/lbl.h | 501 | hle/service/lbl/lbl.h |
| 506 | hle/service/ldn/lan_discovery.cpp | 502 | hle/service/ldn/lan_discovery.cpp |
| @@ -805,14 +801,18 @@ if (ENABLE_WEB_SERVICE) | |||
| 805 | target_link_libraries(core PRIVATE web_service) | 801 | target_link_libraries(core PRIVATE web_service) |
| 806 | endif() | 802 | endif() |
| 807 | 803 | ||
| 808 | if (ARCHITECTURE_x86_64) | 804 | if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) |
| 809 | target_sources(core PRIVATE | 805 | target_sources(core PRIVATE |
| 810 | arm/dynarmic/arm_dynarmic_32.cpp | ||
| 811 | arm/dynarmic/arm_dynarmic_32.h | ||
| 812 | arm/dynarmic/arm_dynarmic_64.cpp | 806 | arm/dynarmic/arm_dynarmic_64.cpp |
| 813 | arm/dynarmic/arm_dynarmic_64.h | 807 | arm/dynarmic/arm_dynarmic_64.h |
| 808 | arm/dynarmic/arm_dynarmic_32.cpp | ||
| 809 | arm/dynarmic/arm_dynarmic_32.h | ||
| 814 | arm/dynarmic/arm_dynarmic_cp15.cpp | 810 | arm/dynarmic/arm_dynarmic_cp15.cpp |
| 815 | arm/dynarmic/arm_dynarmic_cp15.h | 811 | arm/dynarmic/arm_dynarmic_cp15.h |
| 812 | hle/service/jit/jit_context.cpp | ||
| 813 | hle/service/jit/jit_context.h | ||
| 814 | hle/service/jit/jit.cpp | ||
| 815 | hle/service/jit/jit.h | ||
| 816 | ) | 816 | ) |
| 817 | target_link_libraries(core PRIVATE dynarmic) | 817 | target_link_libraries(core PRIVATE dynarmic) |
| 818 | endif() | 818 | 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* | |||
| 301 | } | 301 | } |
| 302 | } | 302 | } |
| 303 | 303 | ||
| 304 | #ifdef ARCHITECTURE_arm64 | ||
| 305 | // TODO: remove when fixed in dynarmic | ||
| 306 | config.optimizations &= ~Dynarmic::OptimizationFlag::BlockLinking; | ||
| 307 | #endif | ||
| 308 | |||
| 304 | return std::make_unique<Dynarmic::A32::Jit>(config); | 309 | return std::make_unique<Dynarmic::A32::Jit>(config); |
| 305 | } | 310 | } |
| 306 | 311 | ||
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp index 200efe4db..5a4eba3eb 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp | |||
| @@ -52,12 +52,16 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1 | |||
| 52 | case 4: | 52 | case 4: |
| 53 | // CP15_DATA_SYNC_BARRIER | 53 | // CP15_DATA_SYNC_BARRIER |
| 54 | return Callback{ | 54 | return Callback{ |
| 55 | [](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t { | 55 | [](void*, std::uint32_t, std::uint32_t) -> std::uint64_t { |
| 56 | #ifdef _MSC_VER | 56 | #if defined(_MSC_VER) && defined(ARCHITECTURE_x86_64) |
| 57 | _mm_mfence(); | 57 | _mm_mfence(); |
| 58 | _mm_lfence(); | 58 | _mm_lfence(); |
| 59 | #else | 59 | #elif defined(ARCHITECTURE_x86_64) |
| 60 | asm volatile("mfence\n\tlfence\n\t" : : : "memory"); | 60 | asm volatile("mfence\n\tlfence\n\t" : : : "memory"); |
| 61 | #elif defined(ARCHITECTURE_arm64) | ||
| 62 | asm volatile("dsb sy\n\t" : : : "memory"); | ||
| 63 | #else | ||
| 64 | #error Unsupported architecture | ||
| 61 | #endif | 65 | #endif |
| 62 | return 0; | 66 | return 0; |
| 63 | }, | 67 | }, |
| @@ -66,11 +70,15 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1 | |||
| 66 | case 5: | 70 | case 5: |
| 67 | // CP15_DATA_MEMORY_BARRIER | 71 | // CP15_DATA_MEMORY_BARRIER |
| 68 | return Callback{ | 72 | return Callback{ |
| 69 | [](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t { | 73 | [](void*, std::uint32_t, std::uint32_t) -> std::uint64_t { |
| 70 | #ifdef _MSC_VER | 74 | #if defined(_MSC_VER) && defined(ARCHITECTURE_x86_64) |
| 71 | _mm_mfence(); | 75 | _mm_mfence(); |
| 72 | #else | 76 | #elif defined(ARCHITECTURE_x86_64) |
| 73 | asm volatile("mfence\n\t" : : : "memory"); | 77 | asm volatile("mfence\n\t" : : : "memory"); |
| 78 | #elif defined(ARCHITECTURE_arm64) | ||
| 79 | asm volatile("dmb sy\n\t" : : : "memory"); | ||
| 80 | #else | ||
| 81 | #error Unsupported architecture | ||
| 74 | #endif | 82 | #endif |
| 75 | return 0; | 83 | return 0; |
| 76 | }, | 84 | }, |
| @@ -115,7 +123,7 @@ CallbackOrAccessOneWord DynarmicCP15::CompileGetOneWord(bool two, unsigned opc1, | |||
| 115 | CallbackOrAccessTwoWords DynarmicCP15::CompileGetTwoWords(bool two, unsigned opc, CoprocReg CRm) { | 123 | CallbackOrAccessTwoWords DynarmicCP15::CompileGetTwoWords(bool two, unsigned opc, CoprocReg CRm) { |
| 116 | if (!two && opc == 0 && CRm == CoprocReg::C14) { | 124 | if (!two && opc == 0 && CRm == CoprocReg::C14) { |
| 117 | // CNTPCT | 125 | // CNTPCT |
| 118 | const auto callback = [](Dynarmic::A32::Jit*, void* arg, u32, u32) -> u64 { | 126 | const auto callback = [](void* arg, u32, u32) -> u64 { |
| 119 | const auto& parent_arg = *static_cast<ARM_Dynarmic_32*>(arg); | 127 | const auto& parent_arg = *static_cast<ARM_Dynarmic_32*>(arg); |
| 120 | return parent_arg.system.CoreTiming().GetClockTicks(); | 128 | return parent_arg.system.CoreTiming().GetClockTicks(); |
| 121 | }; | 129 | }; |
diff --git a/src/core/arm/exclusive_monitor.cpp b/src/core/arm/exclusive_monitor.cpp index 2db0b035d..20550faeb 100644 --- a/src/core/arm/exclusive_monitor.cpp +++ b/src/core/arm/exclusive_monitor.cpp | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #ifdef ARCHITECTURE_x86_64 | 4 | #if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64) |
| 5 | #include "core/arm/dynarmic/arm_exclusive_monitor.h" | 5 | #include "core/arm/dynarmic/arm_exclusive_monitor.h" |
| 6 | #endif | 6 | #endif |
| 7 | #include "core/arm/exclusive_monitor.h" | 7 | #include "core/arm/exclusive_monitor.h" |
| @@ -13,7 +13,7 @@ ExclusiveMonitor::~ExclusiveMonitor() = default; | |||
| 13 | 13 | ||
| 14 | std::unique_ptr<Core::ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory, | 14 | std::unique_ptr<Core::ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory, |
| 15 | std::size_t num_cores) { | 15 | std::size_t num_cores) { |
| 16 | #ifdef ARCHITECTURE_x86_64 | 16 | #if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64) |
| 17 | return std::make_unique<Core::DynarmicExclusiveMonitor>(memory, num_cores); | 17 | return std::make_unique<Core::DynarmicExclusiveMonitor>(memory, num_cores); |
| 18 | #else | 18 | #else |
| 19 | // TODO(merry): Passthrough exclusive monitor | 19 | // TODO(merry): Passthrough exclusive monitor |
diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp index 339f971e6..1a8e02e6a 100644 --- a/src/core/debugger/debugger.cpp +++ b/src/core/debugger/debugger.cpp | |||
| @@ -27,12 +27,21 @@ static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) { | |||
| 27 | const u8* buffer_start = reinterpret_cast<const u8*>(&buffer); | 27 | const u8* buffer_start = reinterpret_cast<const u8*>(&buffer); |
| 28 | std::span<const u8> received_data{buffer_start, buffer_start + bytes_read}; | 28 | std::span<const u8> received_data{buffer_start, buffer_start + bytes_read}; |
| 29 | c(received_data); | 29 | c(received_data); |
| 30 | AsyncReceiveInto(r, buffer, c); | ||
| 30 | } | 31 | } |
| 31 | |||
| 32 | AsyncReceiveInto(r, buffer, c); | ||
| 33 | }); | 32 | }); |
| 34 | } | 33 | } |
| 35 | 34 | ||
| 35 | template <typename Callback> | ||
| 36 | static void AsyncAccept(boost::asio::ip::tcp::acceptor& acceptor, Callback&& c) { | ||
| 37 | acceptor.async_accept([&, c](const boost::system::error_code& error, auto&& peer_socket) { | ||
| 38 | if (!error.failed()) { | ||
| 39 | c(peer_socket); | ||
| 40 | AsyncAccept(acceptor, c); | ||
| 41 | } | ||
| 42 | }); | ||
| 43 | } | ||
| 44 | |||
| 36 | template <typename Readable, typename Buffer> | 45 | template <typename Readable, typename Buffer> |
| 37 | static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) { | 46 | static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) { |
| 38 | static_assert(std::is_trivial_v<Buffer>); | 47 | static_assert(std::is_trivial_v<Buffer>); |
| @@ -59,9 +68,7 @@ namespace Core { | |||
| 59 | 68 | ||
| 60 | class DebuggerImpl : public DebuggerBackend { | 69 | class DebuggerImpl : public DebuggerBackend { |
| 61 | public: | 70 | public: |
| 62 | explicit DebuggerImpl(Core::System& system_, u16 port) | 71 | explicit DebuggerImpl(Core::System& system_, u16 port) : system{system_} { |
| 63 | : system{system_}, signal_pipe{io_context}, client_socket{io_context} { | ||
| 64 | frontend = std::make_unique<GDBStub>(*this, system); | ||
| 65 | InitializeServer(port); | 72 | InitializeServer(port); |
| 66 | } | 73 | } |
| 67 | 74 | ||
| @@ -70,39 +77,42 @@ public: | |||
| 70 | } | 77 | } |
| 71 | 78 | ||
| 72 | bool SignalDebugger(SignalInfo signal_info) { | 79 | bool SignalDebugger(SignalInfo signal_info) { |
| 73 | { | 80 | std::scoped_lock lk{connection_lock}; |
| 74 | std::scoped_lock lk{connection_lock}; | ||
| 75 | 81 | ||
| 76 | if (stopped) { | 82 | if (stopped || !state) { |
| 77 | // Do not notify the debugger about another event. | 83 | // Do not notify the debugger about another event. |
| 78 | // It should be ignored. | 84 | // It should be ignored. |
| 79 | return false; | 85 | return false; |
| 80 | } | ||
| 81 | |||
| 82 | // Set up the state. | ||
| 83 | stopped = true; | ||
| 84 | info = signal_info; | ||
| 85 | } | 86 | } |
| 86 | 87 | ||
| 88 | // Set up the state. | ||
| 89 | stopped = true; | ||
| 90 | state->info = signal_info; | ||
| 91 | |||
| 87 | // Write a single byte into the pipe to wake up the debug interface. | 92 | // Write a single byte into the pipe to wake up the debug interface. |
| 88 | boost::asio::write(signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped))); | 93 | boost::asio::write(state->signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped))); |
| 94 | |||
| 89 | return true; | 95 | return true; |
| 90 | } | 96 | } |
| 91 | 97 | ||
| 98 | // These functions are callbacks from the frontend, and the lock will be held. | ||
| 99 | // There is no need to relock it. | ||
| 100 | |||
| 92 | std::span<const u8> ReadFromClient() override { | 101 | std::span<const u8> ReadFromClient() override { |
| 93 | return ReceiveInto(client_socket, client_data); | 102 | return ReceiveInto(state->client_socket, state->client_data); |
| 94 | } | 103 | } |
| 95 | 104 | ||
| 96 | void WriteToClient(std::span<const u8> data) override { | 105 | void WriteToClient(std::span<const u8> data) override { |
| 97 | boost::asio::write(client_socket, boost::asio::buffer(data.data(), data.size_bytes())); | 106 | boost::asio::write(state->client_socket, |
| 107 | boost::asio::buffer(data.data(), data.size_bytes())); | ||
| 98 | } | 108 | } |
| 99 | 109 | ||
| 100 | void SetActiveThread(Kernel::KThread* thread) override { | 110 | void SetActiveThread(Kernel::KThread* thread) override { |
| 101 | active_thread = thread; | 111 | state->active_thread = thread; |
| 102 | } | 112 | } |
| 103 | 113 | ||
| 104 | Kernel::KThread* GetActiveThread() override { | 114 | Kernel::KThread* GetActiveThread() override { |
| 105 | return active_thread; | 115 | return state->active_thread; |
| 106 | } | 116 | } |
| 107 | 117 | ||
| 108 | private: | 118 | private: |
| @@ -113,65 +123,78 @@ private: | |||
| 113 | 123 | ||
| 114 | // Run the connection thread. | 124 | // Run the connection thread. |
| 115 | connection_thread = std::jthread([&, port](std::stop_token stop_token) { | 125 | connection_thread = std::jthread([&, port](std::stop_token stop_token) { |
| 126 | Common::SetCurrentThreadName("Debugger"); | ||
| 127 | |||
| 116 | try { | 128 | try { |
| 117 | // Initialize the listening socket and accept a new client. | 129 | // Initialize the listening socket and accept a new client. |
| 118 | tcp::endpoint endpoint{boost::asio::ip::address_v4::any(), port}; | 130 | tcp::endpoint endpoint{boost::asio::ip::address_v4::any(), port}; |
| 119 | tcp::acceptor acceptor{io_context, endpoint}; | 131 | tcp::acceptor acceptor{io_context, endpoint}; |
| 120 | 132 | ||
| 121 | acceptor.async_accept(client_socket, [](const auto&) {}); | 133 | AsyncAccept(acceptor, [&](auto&& peer) { AcceptConnection(std::move(peer)); }); |
| 122 | io_context.run_one(); | ||
| 123 | io_context.restart(); | ||
| 124 | 134 | ||
| 125 | if (stop_token.stop_requested()) { | 135 | while (!stop_token.stop_requested() && io_context.run()) { |
| 126 | return; | ||
| 127 | } | 136 | } |
| 128 | |||
| 129 | ThreadLoop(stop_token); | ||
| 130 | } catch (const std::exception& ex) { | 137 | } catch (const std::exception& ex) { |
| 131 | LOG_CRITICAL(Debug_GDBStub, "Stopping server: {}", ex.what()); | 138 | LOG_CRITICAL(Debug_GDBStub, "Stopping server: {}", ex.what()); |
| 132 | } | 139 | } |
| 133 | }); | 140 | }); |
| 134 | } | 141 | } |
| 135 | 142 | ||
| 136 | void ShutdownServer() { | 143 | void AcceptConnection(boost::asio::ip::tcp::socket&& peer) { |
| 137 | connection_thread.request_stop(); | 144 | LOG_INFO(Debug_GDBStub, "Accepting new peer connection"); |
| 138 | io_context.stop(); | ||
| 139 | connection_thread.join(); | ||
| 140 | } | ||
| 141 | 145 | ||
| 142 | void ThreadLoop(std::stop_token stop_token) { | 146 | std::scoped_lock lk{connection_lock}; |
| 143 | Common::SetCurrentThreadName("Debugger"); | 147 | |
| 148 | // Ensure everything is stopped. | ||
| 149 | PauseEmulation(); | ||
| 150 | |||
| 151 | // Set up the new frontend. | ||
| 152 | frontend = std::make_unique<GDBStub>(*this, system); | ||
| 153 | |||
| 154 | // Set the new state. This will tear down any existing state. | ||
| 155 | state = ConnectionState{ | ||
| 156 | .client_socket{std::move(peer)}, | ||
| 157 | .signal_pipe{io_context}, | ||
| 158 | .info{}, | ||
| 159 | .active_thread{}, | ||
| 160 | .client_data{}, | ||
| 161 | .pipe_data{}, | ||
| 162 | }; | ||
| 144 | 163 | ||
| 145 | // Set up the client signals for new data. | 164 | // Set up the client signals for new data. |
| 146 | AsyncReceiveInto(signal_pipe, pipe_data, [&](auto d) { PipeData(d); }); | 165 | AsyncReceiveInto(state->signal_pipe, state->pipe_data, [&](auto d) { PipeData(d); }); |
| 147 | AsyncReceiveInto(client_socket, client_data, [&](auto d) { ClientData(d); }); | 166 | AsyncReceiveInto(state->client_socket, state->client_data, [&](auto d) { ClientData(d); }); |
| 148 | 167 | ||
| 149 | // Set the active thread. | 168 | // Set the active thread. |
| 150 | UpdateActiveThread(); | 169 | UpdateActiveThread(); |
| 151 | 170 | ||
| 152 | // Set up the frontend. | 171 | // Set up the frontend. |
| 153 | frontend->Connected(); | 172 | frontend->Connected(); |
| 173 | } | ||
| 154 | 174 | ||
| 155 | // Main event loop. | 175 | void ShutdownServer() { |
| 156 | while (!stop_token.stop_requested() && io_context.run()) { | 176 | connection_thread.request_stop(); |
| 157 | } | 177 | io_context.stop(); |
| 178 | connection_thread.join(); | ||
| 158 | } | 179 | } |
| 159 | 180 | ||
| 160 | void PipeData(std::span<const u8> data) { | 181 | void PipeData(std::span<const u8> data) { |
| 161 | switch (info.type) { | 182 | std::scoped_lock lk{connection_lock}; |
| 183 | |||
| 184 | switch (state->info.type) { | ||
| 162 | case SignalType::Stopped: | 185 | case SignalType::Stopped: |
| 163 | case SignalType::Watchpoint: | 186 | case SignalType::Watchpoint: |
| 164 | // Stop emulation. | 187 | // Stop emulation. |
| 165 | PauseEmulation(); | 188 | PauseEmulation(); |
| 166 | 189 | ||
| 167 | // Notify the client. | 190 | // Notify the client. |
| 168 | active_thread = info.thread; | 191 | state->active_thread = state->info.thread; |
| 169 | UpdateActiveThread(); | 192 | UpdateActiveThread(); |
| 170 | 193 | ||
| 171 | if (info.type == SignalType::Watchpoint) { | 194 | if (state->info.type == SignalType::Watchpoint) { |
| 172 | frontend->Watchpoint(active_thread, *info.watchpoint); | 195 | frontend->Watchpoint(state->active_thread, *state->info.watchpoint); |
| 173 | } else { | 196 | } else { |
| 174 | frontend->Stopped(active_thread); | 197 | frontend->Stopped(state->active_thread); |
| 175 | } | 198 | } |
| 176 | 199 | ||
| 177 | break; | 200 | break; |
| @@ -179,8 +202,8 @@ private: | |||
| 179 | frontend->ShuttingDown(); | 202 | frontend->ShuttingDown(); |
| 180 | 203 | ||
| 181 | // Wait for emulation to shut down gracefully now. | 204 | // Wait for emulation to shut down gracefully now. |
| 182 | signal_pipe.close(); | 205 | state->signal_pipe.close(); |
| 183 | client_socket.shutdown(boost::asio::socket_base::shutdown_both); | 206 | state->client_socket.shutdown(boost::asio::socket_base::shutdown_both); |
| 184 | LOG_INFO(Debug_GDBStub, "Shut down server"); | 207 | LOG_INFO(Debug_GDBStub, "Shut down server"); |
| 185 | 208 | ||
| 186 | break; | 209 | break; |
| @@ -188,17 +211,16 @@ private: | |||
| 188 | } | 211 | } |
| 189 | 212 | ||
| 190 | void ClientData(std::span<const u8> data) { | 213 | void ClientData(std::span<const u8> data) { |
| 214 | std::scoped_lock lk{connection_lock}; | ||
| 215 | |||
| 191 | const auto actions{frontend->ClientData(data)}; | 216 | const auto actions{frontend->ClientData(data)}; |
| 192 | for (const auto action : actions) { | 217 | for (const auto action : actions) { |
| 193 | switch (action) { | 218 | switch (action) { |
| 194 | case DebuggerAction::Interrupt: { | 219 | case DebuggerAction::Interrupt: { |
| 195 | { | 220 | stopped = true; |
| 196 | std::scoped_lock lk{connection_lock}; | ||
| 197 | stopped = true; | ||
| 198 | } | ||
| 199 | PauseEmulation(); | 221 | PauseEmulation(); |
| 200 | UpdateActiveThread(); | 222 | UpdateActiveThread(); |
| 201 | frontend->Stopped(active_thread); | 223 | frontend->Stopped(state->active_thread); |
| 202 | break; | 224 | break; |
| 203 | } | 225 | } |
| 204 | case DebuggerAction::Continue: | 226 | case DebuggerAction::Continue: |
| @@ -206,15 +228,15 @@ private: | |||
| 206 | break; | 228 | break; |
| 207 | case DebuggerAction::StepThreadUnlocked: | 229 | case DebuggerAction::StepThreadUnlocked: |
| 208 | MarkResumed([&] { | 230 | MarkResumed([&] { |
| 209 | active_thread->SetStepState(Kernel::StepState::StepPending); | 231 | state->active_thread->SetStepState(Kernel::StepState::StepPending); |
| 210 | active_thread->Resume(Kernel::SuspendType::Debug); | 232 | state->active_thread->Resume(Kernel::SuspendType::Debug); |
| 211 | ResumeEmulation(active_thread); | 233 | ResumeEmulation(state->active_thread); |
| 212 | }); | 234 | }); |
| 213 | break; | 235 | break; |
| 214 | case DebuggerAction::StepThreadLocked: { | 236 | case DebuggerAction::StepThreadLocked: { |
| 215 | MarkResumed([&] { | 237 | MarkResumed([&] { |
| 216 | active_thread->SetStepState(Kernel::StepState::StepPending); | 238 | state->active_thread->SetStepState(Kernel::StepState::StepPending); |
| 217 | active_thread->Resume(Kernel::SuspendType::Debug); | 239 | state->active_thread->Resume(Kernel::SuspendType::Debug); |
| 218 | }); | 240 | }); |
| 219 | break; | 241 | break; |
| 220 | } | 242 | } |
| @@ -254,15 +276,14 @@ private: | |||
| 254 | template <typename Callback> | 276 | template <typename Callback> |
| 255 | void MarkResumed(Callback&& cb) { | 277 | void MarkResumed(Callback&& cb) { |
| 256 | Kernel::KScopedSchedulerLock sl{system.Kernel()}; | 278 | Kernel::KScopedSchedulerLock sl{system.Kernel()}; |
| 257 | std::scoped_lock cl{connection_lock}; | ||
| 258 | stopped = false; | 279 | stopped = false; |
| 259 | cb(); | 280 | cb(); |
| 260 | } | 281 | } |
| 261 | 282 | ||
| 262 | void UpdateActiveThread() { | 283 | void UpdateActiveThread() { |
| 263 | const auto& threads{ThreadList()}; | 284 | const auto& threads{ThreadList()}; |
| 264 | if (std::find(threads.begin(), threads.end(), active_thread) == threads.end()) { | 285 | if (std::find(threads.begin(), threads.end(), state->active_thread) == threads.end()) { |
| 265 | active_thread = threads[0]; | 286 | state->active_thread = threads[0]; |
| 266 | } | 287 | } |
| 267 | } | 288 | } |
| 268 | 289 | ||
| @@ -274,18 +295,22 @@ private: | |||
| 274 | System& system; | 295 | System& system; |
| 275 | std::unique_ptr<DebuggerFrontend> frontend; | 296 | std::unique_ptr<DebuggerFrontend> frontend; |
| 276 | 297 | ||
| 298 | boost::asio::io_context io_context; | ||
| 277 | std::jthread connection_thread; | 299 | std::jthread connection_thread; |
| 278 | std::mutex connection_lock; | 300 | std::mutex connection_lock; |
| 279 | boost::asio::io_context io_context; | ||
| 280 | boost::process::async_pipe signal_pipe; | ||
| 281 | boost::asio::ip::tcp::socket client_socket; | ||
| 282 | 301 | ||
| 283 | SignalInfo info; | 302 | struct ConnectionState { |
| 284 | Kernel::KThread* active_thread; | 303 | boost::asio::ip::tcp::socket client_socket; |
| 285 | bool pipe_data; | 304 | boost::process::async_pipe signal_pipe; |
| 286 | bool stopped; | 305 | |
| 306 | SignalInfo info; | ||
| 307 | Kernel::KThread* active_thread; | ||
| 308 | std::array<u8, 4096> client_data; | ||
| 309 | bool pipe_data; | ||
| 310 | }; | ||
| 287 | 311 | ||
| 288 | std::array<u8, 4096> client_data; | 312 | std::optional<ConnectionState> state{}; |
| 313 | bool stopped{}; | ||
| 289 | }; | 314 | }; |
| 290 | 315 | ||
| 291 | Debugger::Debugger(Core::System& system, u16 port) { | 316 | 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) { | |||
| 606 | } else if (command.starts_with("StartNoAckMode")) { | 606 | } else if (command.starts_with("StartNoAckMode")) { |
| 607 | no_ack = true; | 607 | no_ack = true; |
| 608 | SendReply(GDB_STUB_REPLY_OK); | 608 | SendReply(GDB_STUB_REPLY_OK); |
| 609 | } else if (command.starts_with("Rcmd,")) { | ||
| 610 | HandleRcmd(Common::HexStringToVector(command.substr(5), false)); | ||
| 609 | } else { | 611 | } else { |
| 610 | SendReply(GDB_STUB_REPLY_EMPTY); | 612 | SendReply(GDB_STUB_REPLY_EMPTY); |
| 611 | } | 613 | } |
| @@ -645,6 +647,155 @@ void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>& | |||
| 645 | } | 647 | } |
| 646 | } | 648 | } |
| 647 | 649 | ||
| 650 | constexpr std::array<std::pair<const char*, Kernel::Svc::MemoryState>, 22> MemoryStateNames{{ | ||
| 651 | {"----- Free -----", Kernel::Svc::MemoryState::Free}, | ||
| 652 | {"Io ", Kernel::Svc::MemoryState::Io}, | ||
| 653 | {"Static ", Kernel::Svc::MemoryState::Static}, | ||
| 654 | {"Code ", Kernel::Svc::MemoryState::Code}, | ||
| 655 | {"CodeData ", Kernel::Svc::MemoryState::CodeData}, | ||
| 656 | {"Normal ", Kernel::Svc::MemoryState::Normal}, | ||
| 657 | {"Shared ", Kernel::Svc::MemoryState::Shared}, | ||
| 658 | {"AliasCode ", Kernel::Svc::MemoryState::AliasCode}, | ||
| 659 | {"AliasCodeData ", Kernel::Svc::MemoryState::AliasCodeData}, | ||
| 660 | {"Ipc ", Kernel::Svc::MemoryState::Ipc}, | ||
| 661 | {"Stack ", Kernel::Svc::MemoryState::Stack}, | ||
| 662 | {"ThreadLocal ", Kernel::Svc::MemoryState::ThreadLocal}, | ||
| 663 | {"Transfered ", Kernel::Svc::MemoryState::Transfered}, | ||
| 664 | {"SharedTransfered", Kernel::Svc::MemoryState::SharedTransfered}, | ||
| 665 | {"SharedCode ", Kernel::Svc::MemoryState::SharedCode}, | ||
| 666 | {"Inaccessible ", Kernel::Svc::MemoryState::Inaccessible}, | ||
| 667 | {"NonSecureIpc ", Kernel::Svc::MemoryState::NonSecureIpc}, | ||
| 668 | {"NonDeviceIpc ", Kernel::Svc::MemoryState::NonDeviceIpc}, | ||
| 669 | {"Kernel ", Kernel::Svc::MemoryState::Kernel}, | ||
| 670 | {"GeneratedCode ", Kernel::Svc::MemoryState::GeneratedCode}, | ||
| 671 | {"CodeOut ", Kernel::Svc::MemoryState::CodeOut}, | ||
| 672 | {"Coverage ", Kernel::Svc::MemoryState::Coverage}, | ||
| 673 | }}; | ||
| 674 | |||
| 675 | static constexpr const char* GetMemoryStateName(Kernel::Svc::MemoryState state) { | ||
| 676 | for (size_t i = 0; i < MemoryStateNames.size(); i++) { | ||
| 677 | if (std::get<1>(MemoryStateNames[i]) == state) { | ||
| 678 | return std::get<0>(MemoryStateNames[i]); | ||
| 679 | } | ||
| 680 | } | ||
| 681 | return "Unknown "; | ||
| 682 | } | ||
| 683 | |||
| 684 | static constexpr const char* GetMemoryPermissionString(const Kernel::Svc::MemoryInfo& info) { | ||
| 685 | if (info.state == Kernel::Svc::MemoryState::Free) { | ||
| 686 | return " "; | ||
| 687 | } | ||
| 688 | |||
| 689 | switch (info.permission) { | ||
| 690 | case Kernel::Svc::MemoryPermission::ReadExecute: | ||
| 691 | return "r-x"; | ||
| 692 | case Kernel::Svc::MemoryPermission::Read: | ||
| 693 | return "r--"; | ||
| 694 | case Kernel::Svc::MemoryPermission::ReadWrite: | ||
| 695 | return "rw-"; | ||
| 696 | default: | ||
| 697 | return "---"; | ||
| 698 | } | ||
| 699 | } | ||
| 700 | |||
| 701 | static VAddr GetModuleEnd(Kernel::KPageTable& page_table, VAddr base) { | ||
| 702 | Kernel::Svc::MemoryInfo mem_info; | ||
| 703 | VAddr cur_addr{base}; | ||
| 704 | |||
| 705 | // Expect: r-x Code (.text) | ||
| 706 | mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); | ||
| 707 | cur_addr = mem_info.base_address + mem_info.size; | ||
| 708 | if (mem_info.state != Kernel::Svc::MemoryState::Code || | ||
| 709 | mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) { | ||
| 710 | return cur_addr - 1; | ||
| 711 | } | ||
| 712 | |||
| 713 | // Expect: r-- Code (.rodata) | ||
| 714 | mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); | ||
| 715 | cur_addr = mem_info.base_address + mem_info.size; | ||
| 716 | if (mem_info.state != Kernel::Svc::MemoryState::Code || | ||
| 717 | mem_info.permission != Kernel::Svc::MemoryPermission::Read) { | ||
| 718 | return cur_addr - 1; | ||
| 719 | } | ||
| 720 | |||
| 721 | // Expect: rw- CodeData (.data) | ||
| 722 | mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); | ||
| 723 | cur_addr = mem_info.base_address + mem_info.size; | ||
| 724 | return cur_addr - 1; | ||
| 725 | } | ||
| 726 | |||
| 727 | void GDBStub::HandleRcmd(const std::vector<u8>& command) { | ||
| 728 | std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()}; | ||
| 729 | std::string reply; | ||
| 730 | |||
| 731 | auto* process = system.CurrentProcess(); | ||
| 732 | auto& page_table = process->PageTable(); | ||
| 733 | |||
| 734 | if (command_str == "get info") { | ||
| 735 | Loader::AppLoader::Modules modules; | ||
| 736 | system.GetAppLoader().ReadNSOModules(modules); | ||
| 737 | |||
| 738 | reply = fmt::format("Process: {:#x} ({})\n" | ||
| 739 | "Program Id: {:#018x}\n", | ||
| 740 | process->GetProcessID(), process->GetName(), process->GetProgramID()); | ||
| 741 | reply += | ||
| 742 | fmt::format("Layout:\n" | ||
| 743 | " Alias: {:#012x} - {:#012x}\n" | ||
| 744 | " Heap: {:#012x} - {:#012x}\n" | ||
| 745 | " Aslr: {:#012x} - {:#012x}\n" | ||
| 746 | " Stack: {:#012x} - {:#012x}\n" | ||
| 747 | "Modules:\n", | ||
| 748 | page_table.GetAliasRegionStart(), page_table.GetAliasRegionEnd(), | ||
| 749 | page_table.GetHeapRegionStart(), page_table.GetHeapRegionEnd(), | ||
| 750 | page_table.GetAliasCodeRegionStart(), page_table.GetAliasCodeRegionEnd(), | ||
| 751 | page_table.GetStackRegionStart(), page_table.GetStackRegionEnd()); | ||
| 752 | |||
| 753 | for (const auto& [vaddr, name] : modules) { | ||
| 754 | reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr, | ||
| 755 | GetModuleEnd(page_table, vaddr), name); | ||
| 756 | } | ||
| 757 | } else if (command_str == "get mappings") { | ||
| 758 | reply = "Mappings:\n"; | ||
| 759 | VAddr cur_addr = 0; | ||
| 760 | |||
| 761 | while (true) { | ||
| 762 | using MemoryAttribute = Kernel::Svc::MemoryAttribute; | ||
| 763 | |||
| 764 | auto mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); | ||
| 765 | |||
| 766 | if (mem_info.state != Kernel::Svc::MemoryState::Inaccessible || | ||
| 767 | mem_info.base_address + mem_info.size - 1 != std::numeric_limits<u64>::max()) { | ||
| 768 | const char* state = GetMemoryStateName(mem_info.state); | ||
| 769 | const char* perm = GetMemoryPermissionString(mem_info); | ||
| 770 | |||
| 771 | const char l = True(mem_info.attribute & MemoryAttribute::Locked) ? 'L' : '-'; | ||
| 772 | const char i = True(mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-'; | ||
| 773 | const char d = True(mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-'; | ||
| 774 | const char u = True(mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-'; | ||
| 775 | |||
| 776 | reply += | ||
| 777 | fmt::format(" {:#012x} - {:#012x} {} {} {}{}{}{} [{}, {}]\n", | ||
| 778 | mem_info.base_address, mem_info.base_address + mem_info.size - 1, | ||
| 779 | perm, state, l, i, d, u, mem_info.ipc_count, mem_info.device_count); | ||
| 780 | } | ||
| 781 | |||
| 782 | const uintptr_t next_address = mem_info.base_address + mem_info.size; | ||
| 783 | if (next_address <= cur_addr) { | ||
| 784 | break; | ||
| 785 | } | ||
| 786 | |||
| 787 | cur_addr = next_address; | ||
| 788 | } | ||
| 789 | } else if (command_str == "help") { | ||
| 790 | reply = "Commands:\n get info\n get mappings\n"; | ||
| 791 | } else { | ||
| 792 | reply = "Unknown command.\nCommands:\n get info\n get mappings\n"; | ||
| 793 | } | ||
| 794 | |||
| 795 | std::span<const u8> reply_span{reinterpret_cast<u8*>(&reply.front()), reply.size()}; | ||
| 796 | SendReply(Common::HexToString(reply_span, false)); | ||
| 797 | } | ||
| 798 | |||
| 648 | Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) { | 799 | Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) { |
| 649 | const auto& threads{system.GlobalSchedulerContext().GetThreadList()}; | 800 | const auto& threads{system.GlobalSchedulerContext().GetThreadList()}; |
| 650 | for (auto* thread : threads) { | 801 | for (auto* thread : threads) { |
diff --git a/src/core/debugger/gdbstub.h b/src/core/debugger/gdbstub.h index 0b0f56e4b..368197920 100644 --- a/src/core/debugger/gdbstub.h +++ b/src/core/debugger/gdbstub.h | |||
| @@ -32,6 +32,7 @@ private: | |||
| 32 | void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions); | 32 | void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions); |
| 33 | void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions); | 33 | void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions); |
| 34 | void HandleQuery(std::string_view command); | 34 | void HandleQuery(std::string_view command); |
| 35 | void HandleRcmd(const std::vector<u8>& command); | ||
| 35 | void HandleBreakpointInsert(std::string_view command); | 36 | void HandleBreakpointInsert(std::string_view command); |
| 36 | void HandleBreakpointRemove(std::string_view command); | 37 | void HandleBreakpointRemove(std::string_view command); |
| 37 | std::vector<char>::const_iterator CommandEnd() const; | 38 | std::vector<char>::const_iterator CommandEnd() const; |
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 3bb111748..a86bec252 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h | |||
| @@ -149,7 +149,7 @@ public: | |||
| 149 | context->AddDomainObject(std::move(iface)); | 149 | context->AddDomainObject(std::move(iface)); |
| 150 | } else { | 150 | } else { |
| 151 | kernel.CurrentProcess()->GetResourceLimit()->Reserve( | 151 | kernel.CurrentProcess()->GetResourceLimit()->Reserve( |
| 152 | Kernel::LimitableResource::Sessions, 1); | 152 | Kernel::LimitableResource::SessionCountMax, 1); |
| 153 | 153 | ||
| 154 | auto* session = Kernel::KSession::Create(kernel); | 154 | auto* session = Kernel::KSession::Create(kernel); |
| 155 | session->Initialize(nullptr, iface->GetServiceName()); | 155 | session->Initialize(nullptr, iface->GetServiceName()); |
diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp index aa2dddcc6..7b363eb1e 100644 --- a/src/core/hle/kernel/init/init_slab_setup.cpp +++ b/src/core/hle/kernel/init/init_slab_setup.cpp | |||
| @@ -243,6 +243,7 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) { | |||
| 243 | // If we somehow get an invalid type, abort. | 243 | // If we somehow get an invalid type, abort. |
| 244 | default: | 244 | default: |
| 245 | ASSERT_MSG(false, "Unknown slab type: {}", slab_types[i]); | 245 | ASSERT_MSG(false, "Unknown slab type: {}", slab_types[i]); |
| 246 | break; | ||
| 246 | } | 247 | } |
| 247 | 248 | ||
| 248 | // If we've hit the end of a gap, free it. | 249 | // If we've hit the end of a gap, free it. |
| @@ -265,7 +266,8 @@ void KPageBufferSlabHeap::Initialize(Core::System& system) { | |||
| 265 | const size_t slab_size = num_pages * PageSize; | 266 | const size_t slab_size = num_pages * PageSize; |
| 266 | 267 | ||
| 267 | // Reserve memory from the system resource limit. | 268 | // Reserve memory from the system resource limit. |
| 268 | ASSERT(kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemory, slab_size)); | 269 | ASSERT( |
| 270 | kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemoryMax, slab_size)); | ||
| 269 | 271 | ||
| 270 | // Allocate memory for the slab. | 272 | // Allocate memory for the slab. |
| 271 | constexpr auto AllocateOption = KMemoryManager::EncodeOption( | 273 | constexpr auto AllocateOption = KMemoryManager::EncodeOption( |
diff --git a/src/core/hle/kernel/k_client_port.cpp b/src/core/hle/kernel/k_client_port.cpp index eaa2e094c..2ec623a58 100644 --- a/src/core/hle/kernel/k_client_port.cpp +++ b/src/core/hle/kernel/k_client_port.cpp | |||
| @@ -61,7 +61,7 @@ bool KClientPort::IsSignaled() const { | |||
| 61 | Result KClientPort::CreateSession(KClientSession** out) { | 61 | Result KClientPort::CreateSession(KClientSession** out) { |
| 62 | // Reserve a new session from the resource limit. | 62 | // Reserve a new session from the resource limit. |
| 63 | KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(), | 63 | KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(), |
| 64 | LimitableResource::Sessions); | 64 | LimitableResource::SessionCountMax); |
| 65 | R_UNLESS(session_reservation.Succeeded(), ResultLimitReached); | 65 | R_UNLESS(session_reservation.Succeeded(), ResultLimitReached); |
| 66 | 66 | ||
| 67 | // Update the session counts. | 67 | // Update the session counts. |
diff --git a/src/core/hle/kernel/k_event.cpp b/src/core/hle/kernel/k_event.cpp index 78ca59463..d973853ab 100644 --- a/src/core/hle/kernel/k_event.cpp +++ b/src/core/hle/kernel/k_event.cpp | |||
| @@ -20,8 +20,12 @@ void KEvent::Initialize(KProcess* owner) { | |||
| 20 | m_readable_event.Initialize(this); | 20 | m_readable_event.Initialize(this); |
| 21 | 21 | ||
| 22 | // Set our owner process. | 22 | // Set our owner process. |
| 23 | m_owner = owner; | 23 | // HACK: this should never be nullptr, but service threads don't have a |
| 24 | m_owner->Open(); | 24 | // proper parent process yet. |
| 25 | if (owner != nullptr) { | ||
| 26 | m_owner = owner; | ||
| 27 | m_owner->Open(); | ||
| 28 | } | ||
| 25 | 29 | ||
| 26 | // Mark initialized. | 30 | // Mark initialized. |
| 27 | m_initialized = true; | 31 | m_initialized = true; |
| @@ -50,8 +54,11 @@ Result KEvent::Clear() { | |||
| 50 | void KEvent::PostDestroy(uintptr_t arg) { | 54 | void KEvent::PostDestroy(uintptr_t arg) { |
| 51 | // Release the event count resource the owner process holds. | 55 | // Release the event count resource the owner process holds. |
| 52 | KProcess* owner = reinterpret_cast<KProcess*>(arg); | 56 | KProcess* owner = reinterpret_cast<KProcess*>(arg); |
| 53 | owner->GetResourceLimit()->Release(LimitableResource::Events, 1); | 57 | |
| 54 | owner->Close(); | 58 | if (owner != nullptr) { |
| 59 | owner->GetResourceLimit()->Release(LimitableResource::EventCountMax, 1); | ||
| 60 | owner->Close(); | ||
| 61 | } | ||
| 55 | } | 62 | } |
| 56 | 63 | ||
| 57 | } // namespace Kernel | 64 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h index 6f845d675..3b6e7baff 100644 --- a/src/core/hle/kernel/k_memory_block.h +++ b/src/core/hle/kernel/k_memory_block.h | |||
| @@ -216,13 +216,15 @@ struct KMemoryInfo { | |||
| 216 | 216 | ||
| 217 | constexpr Svc::MemoryInfo GetSvcMemoryInfo() const { | 217 | constexpr Svc::MemoryInfo GetSvcMemoryInfo() const { |
| 218 | return { | 218 | return { |
| 219 | .addr = m_address, | 219 | .base_address = m_address, |
| 220 | .size = m_size, | 220 | .size = m_size, |
| 221 | .state = static_cast<Svc::MemoryState>(m_state & KMemoryState::Mask), | 221 | .state = static_cast<Svc::MemoryState>(m_state & KMemoryState::Mask), |
| 222 | .attr = static_cast<Svc::MemoryAttribute>(m_attribute & KMemoryAttribute::UserMask), | 222 | .attribute = |
| 223 | .perm = static_cast<Svc::MemoryPermission>(m_permission & KMemoryPermission::UserMask), | 223 | static_cast<Svc::MemoryAttribute>(m_attribute & KMemoryAttribute::UserMask), |
| 224 | .ipc_refcount = m_ipc_lock_count, | 224 | .permission = |
| 225 | .device_refcount = m_device_use_count, | 225 | static_cast<Svc::MemoryPermission>(m_permission & KMemoryPermission::UserMask), |
| 226 | .ipc_count = m_ipc_lock_count, | ||
| 227 | .device_count = m_device_use_count, | ||
| 226 | .padding = {}, | 228 | .padding = {}, |
| 227 | }; | 229 | }; |
| 228 | } | 230 | } |
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index fab55a057..612fc76fa 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp | |||
| @@ -920,8 +920,8 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add | |||
| 920 | 920 | ||
| 921 | // Reserve space for any partial pages we allocate. | 921 | // Reserve space for any partial pages we allocate. |
| 922 | const size_t unmapped_size = aligned_src_size - mapping_src_size; | 922 | const size_t unmapped_size = aligned_src_size - mapping_src_size; |
| 923 | KScopedResourceReservation memory_reservation(m_resource_limit, | 923 | KScopedResourceReservation memory_reservation( |
| 924 | LimitableResource::PhysicalMemory, unmapped_size); | 924 | m_resource_limit, LimitableResource::PhysicalMemoryMax, unmapped_size); |
| 925 | R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); | 925 | R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); |
| 926 | 926 | ||
| 927 | // Ensure that we manage page references correctly. | 927 | // Ensure that we manage page references correctly. |
| @@ -1227,7 +1227,7 @@ Result KPageTable::CleanupForIpcServer(VAddr address, size_t size, KMemoryState | |||
| 1227 | const VAddr mapping_start = Common::AlignUp((address), PageSize); | 1227 | const VAddr mapping_start = Common::AlignUp((address), PageSize); |
| 1228 | const VAddr mapping_end = Common::AlignDown((address) + size, PageSize); | 1228 | const VAddr mapping_end = Common::AlignDown((address) + size, PageSize); |
| 1229 | const size_t mapping_size = (mapping_start < mapping_end) ? mapping_end - mapping_start : 0; | 1229 | const size_t mapping_size = (mapping_start < mapping_end) ? mapping_end - mapping_start : 0; |
| 1230 | m_resource_limit->Release(LimitableResource::PhysicalMemory, aligned_size - mapping_size); | 1230 | m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, aligned_size - mapping_size); |
| 1231 | 1231 | ||
| 1232 | R_SUCCEED(); | 1232 | R_SUCCEED(); |
| 1233 | } | 1233 | } |
| @@ -1568,7 +1568,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) { | |||
| 1568 | { | 1568 | { |
| 1569 | // Reserve the memory from the process resource limit. | 1569 | // Reserve the memory from the process resource limit. |
| 1570 | KScopedResourceReservation memory_reservation( | 1570 | KScopedResourceReservation memory_reservation( |
| 1571 | m_resource_limit, LimitableResource::PhysicalMemory, size - mapped_size); | 1571 | m_resource_limit, LimitableResource::PhysicalMemoryMax, size - mapped_size); |
| 1572 | R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); | 1572 | R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); |
| 1573 | 1573 | ||
| 1574 | // Allocate pages for the new memory. | 1574 | // Allocate pages for the new memory. |
| @@ -1908,7 +1908,7 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) { | |||
| 1908 | 1908 | ||
| 1909 | // Release the memory resource. | 1909 | // Release the memory resource. |
| 1910 | m_mapped_physical_memory_size -= mapped_size; | 1910 | m_mapped_physical_memory_size -= mapped_size; |
| 1911 | m_resource_limit->Release(LimitableResource::PhysicalMemory, mapped_size); | 1911 | m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, mapped_size); |
| 1912 | 1912 | ||
| 1913 | // Update memory blocks. | 1913 | // Update memory blocks. |
| 1914 | m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize, | 1914 | m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize, |
| @@ -2301,6 +2301,7 @@ Result KPageTable::SetProcessMemoryPermission(VAddr addr, size_t size, | |||
| 2301 | break; | 2301 | break; |
| 2302 | default: | 2302 | default: |
| 2303 | ASSERT(false); | 2303 | ASSERT(false); |
| 2304 | break; | ||
| 2304 | } | 2305 | } |
| 2305 | } | 2306 | } |
| 2306 | 2307 | ||
| @@ -2492,7 +2493,7 @@ Result KPageTable::SetHeapSize(VAddr* out, size_t size) { | |||
| 2492 | OperationType::Unmap)); | 2493 | OperationType::Unmap)); |
| 2493 | 2494 | ||
| 2494 | // Release the memory from the resource limit. | 2495 | // Release the memory from the resource limit. |
| 2495 | m_resource_limit->Release(LimitableResource::PhysicalMemory, num_pages * PageSize); | 2496 | m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, num_pages * PageSize); |
| 2496 | 2497 | ||
| 2497 | // Apply the memory block update. | 2498 | // Apply the memory block update. |
| 2498 | m_memory_block_manager.Update(std::addressof(allocator), m_heap_region_start + size, | 2499 | m_memory_block_manager.Update(std::addressof(allocator), m_heap_region_start + size, |
| @@ -2522,7 +2523,7 @@ Result KPageTable::SetHeapSize(VAddr* out, size_t size) { | |||
| 2522 | 2523 | ||
| 2523 | // Reserve memory for the heap extension. | 2524 | // Reserve memory for the heap extension. |
| 2524 | KScopedResourceReservation memory_reservation( | 2525 | KScopedResourceReservation memory_reservation( |
| 2525 | m_resource_limit, LimitableResource::PhysicalMemory, allocation_size); | 2526 | m_resource_limit, LimitableResource::PhysicalMemoryMax, allocation_size); |
| 2526 | R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); | 2527 | R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); |
| 2527 | 2528 | ||
| 2528 | // Allocate pages for the heap extension. | 2529 | // Allocate pages for the heap extension. |
| @@ -2803,6 +2804,7 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, const KPageGroup& page_ | |||
| 2803 | break; | 2804 | break; |
| 2804 | default: | 2805 | default: |
| 2805 | ASSERT(false); | 2806 | ASSERT(false); |
| 2807 | break; | ||
| 2806 | } | 2808 | } |
| 2807 | 2809 | ||
| 2808 | addr += size; | 2810 | addr += size; |
| @@ -2838,6 +2840,7 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, KMemoryPermission perm, | |||
| 2838 | break; | 2840 | break; |
| 2839 | default: | 2841 | default: |
| 2840 | ASSERT(false); | 2842 | ASSERT(false); |
| 2843 | break; | ||
| 2841 | } | 2844 | } |
| 2842 | R_SUCCEED(); | 2845 | R_SUCCEED(); |
| 2843 | } | 2846 | } |
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index 950850291..f1ca785d7 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h | |||
| @@ -320,6 +320,9 @@ public: | |||
| 320 | constexpr VAddr GetAliasCodeRegionStart() const { | 320 | constexpr VAddr GetAliasCodeRegionStart() const { |
| 321 | return m_alias_code_region_start; | 321 | return m_alias_code_region_start; |
| 322 | } | 322 | } |
| 323 | constexpr VAddr GetAliasCodeRegionEnd() const { | ||
| 324 | return m_alias_code_region_end; | ||
| 325 | } | ||
| 323 | constexpr VAddr GetAliasCodeRegionSize() const { | 326 | constexpr VAddr GetAliasCodeRegionSize() const { |
| 324 | return m_alias_code_region_end - m_alias_code_region_start; | 327 | return m_alias_code_region_end - m_alias_code_region_start; |
| 325 | } | 328 | } |
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 4ddeea73b..d1dc62401 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp | |||
| @@ -38,7 +38,7 @@ namespace { | |||
| 38 | */ | 38 | */ |
| 39 | void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority, VAddr stack_top) { | 39 | void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority, VAddr stack_top) { |
| 40 | const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart(); | 40 | const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart(); |
| 41 | ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::Threads, 1)); | 41 | ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::ThreadCountMax, 1)); |
| 42 | 42 | ||
| 43 | KThread* thread = KThread::Create(system.Kernel()); | 43 | KThread* thread = KThread::Create(system.Kernel()); |
| 44 | SCOPE_EXIT({ thread->Close(); }); | 44 | SCOPE_EXIT({ thread->Close(); }); |
| @@ -124,7 +124,7 @@ void KProcess::DecrementRunningThreadCount() { | |||
| 124 | } | 124 | } |
| 125 | 125 | ||
| 126 | u64 KProcess::GetTotalPhysicalMemoryAvailable() { | 126 | u64 KProcess::GetTotalPhysicalMemoryAvailable() { |
| 127 | const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) + | 127 | const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemoryMax) + |
| 128 | page_table.GetNormalMemorySize() + GetSystemResourceSize() + image_size + | 128 | page_table.GetNormalMemorySize() + GetSystemResourceSize() + image_size + |
| 129 | main_thread_stack_size}; | 129 | main_thread_stack_size}; |
| 130 | if (const auto pool_size = kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application); | 130 | if (const auto pool_size = kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application); |
| @@ -349,8 +349,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std: | |||
| 349 | // We currently do not support process-specific system resource | 349 | // We currently do not support process-specific system resource |
| 350 | UNIMPLEMENTED_IF(system_resource_size != 0); | 350 | UNIMPLEMENTED_IF(system_resource_size != 0); |
| 351 | 351 | ||
| 352 | KScopedResourceReservation memory_reservation(resource_limit, LimitableResource::PhysicalMemory, | 352 | KScopedResourceReservation memory_reservation( |
| 353 | code_size + system_resource_size); | 353 | resource_limit, LimitableResource::PhysicalMemoryMax, code_size + system_resource_size); |
| 354 | if (!memory_reservation.Succeeded()) { | 354 | if (!memory_reservation.Succeeded()) { |
| 355 | LOG_ERROR(Kernel, "Could not reserve process memory requirements of size {:X} bytes", | 355 | LOG_ERROR(Kernel, "Could not reserve process memory requirements of size {:X} bytes", |
| 356 | code_size + system_resource_size); | 356 | code_size + system_resource_size); |
| @@ -395,6 +395,7 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std: | |||
| 395 | 395 | ||
| 396 | default: | 396 | default: |
| 397 | ASSERT(false); | 397 | ASSERT(false); |
| 398 | break; | ||
| 398 | } | 399 | } |
| 399 | 400 | ||
| 400 | // Create TLS region | 401 | // Create TLS region |
| @@ -406,8 +407,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std: | |||
| 406 | 407 | ||
| 407 | void KProcess::Run(s32 main_thread_priority, u64 stack_size) { | 408 | void KProcess::Run(s32 main_thread_priority, u64 stack_size) { |
| 408 | AllocateMainThreadStack(stack_size); | 409 | AllocateMainThreadStack(stack_size); |
| 409 | resource_limit->Reserve(LimitableResource::Threads, 1); | 410 | resource_limit->Reserve(LimitableResource::ThreadCountMax, 1); |
| 410 | resource_limit->Reserve(LimitableResource::PhysicalMemory, main_thread_stack_size); | 411 | resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, main_thread_stack_size); |
| 411 | 412 | ||
| 412 | const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)}; | 413 | const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)}; |
| 413 | ASSERT(!page_table.SetMaxHeapSize(heap_capacity).IsError()); | 414 | ASSERT(!page_table.SetMaxHeapSize(heap_capacity).IsError()); |
| @@ -442,7 +443,7 @@ void KProcess::PrepareForTermination() { | |||
| 442 | plr_address = 0; | 443 | plr_address = 0; |
| 443 | 444 | ||
| 444 | if (resource_limit) { | 445 | if (resource_limit) { |
| 445 | resource_limit->Release(LimitableResource::PhysicalMemory, | 446 | resource_limit->Release(LimitableResource::PhysicalMemoryMax, |
| 446 | main_thread_stack_size + image_size); | 447 | main_thread_stack_size + image_size); |
| 447 | } | 448 | } |
| 448 | 449 | ||
diff --git a/src/core/hle/kernel/k_resource_limit.cpp b/src/core/hle/kernel/k_resource_limit.cpp index 010dcf99e..b9d22b414 100644 --- a/src/core/hle/kernel/k_resource_limit.cpp +++ b/src/core/hle/kernel/k_resource_limit.cpp | |||
| @@ -159,12 +159,13 @@ KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical | |||
| 159 | // TODO(bunnei): These values are the system defaults, the limits for service processes are | 159 | // TODO(bunnei): These values are the system defaults, the limits for service processes are |
| 160 | // lower. These should use the correct limit values. | 160 | // lower. These should use the correct limit values. |
| 161 | 161 | ||
| 162 | ASSERT(resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, physical_memory_size) | 162 | ASSERT(resource_limit->SetLimitValue(LimitableResource::PhysicalMemoryMax, physical_memory_size) |
| 163 | .IsSuccess()); | 163 | .IsSuccess()); |
| 164 | ASSERT(resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess()); | 164 | ASSERT(resource_limit->SetLimitValue(LimitableResource::ThreadCountMax, 800).IsSuccess()); |
| 165 | ASSERT(resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess()); | 165 | ASSERT(resource_limit->SetLimitValue(LimitableResource::EventCountMax, 900).IsSuccess()); |
| 166 | ASSERT(resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200).IsSuccess()); | 166 | ASSERT( |
| 167 | ASSERT(resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess()); | 167 | resource_limit->SetLimitValue(LimitableResource::TransferMemoryCountMax, 200).IsSuccess()); |
| 168 | ASSERT(resource_limit->SetLimitValue(LimitableResource::SessionCountMax, 1133).IsSuccess()); | ||
| 168 | 169 | ||
| 169 | return resource_limit; | 170 | return resource_limit; |
| 170 | } | 171 | } |
diff --git a/src/core/hle/kernel/k_resource_limit.h b/src/core/hle/kernel/k_resource_limit.h index 65c98c979..2573d1b7c 100644 --- a/src/core/hle/kernel/k_resource_limit.h +++ b/src/core/hle/kernel/k_resource_limit.h | |||
| @@ -16,15 +16,8 @@ class CoreTiming; | |||
| 16 | 16 | ||
| 17 | namespace Kernel { | 17 | namespace Kernel { |
| 18 | class KernelCore; | 18 | class KernelCore; |
| 19 | enum class LimitableResource : u32 { | 19 | |
| 20 | PhysicalMemory = 0, | 20 | using LimitableResource = Svc::LimitableResource; |
| 21 | Threads = 1, | ||
| 22 | Events = 2, | ||
| 23 | TransferMemory = 3, | ||
| 24 | Sessions = 4, | ||
| 25 | |||
| 26 | Count, | ||
| 27 | }; | ||
| 28 | 21 | ||
| 29 | constexpr bool IsValidResourceType(LimitableResource type) { | 22 | constexpr bool IsValidResourceType(LimitableResource type) { |
| 30 | return type < LimitableResource::Count; | 23 | return type < LimitableResource::Count; |
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index b1cabbca0..d6676904b 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp | |||
| @@ -384,7 +384,8 @@ void KScheduler::SwitchThread(KThread* next_thread) { | |||
| 384 | 384 | ||
| 385 | void KScheduler::ScheduleImpl() { | 385 | void KScheduler::ScheduleImpl() { |
| 386 | // First, clear the needs scheduling bool. | 386 | // First, clear the needs scheduling bool. |
| 387 | m_state.needs_scheduling.store(false, std::memory_order_seq_cst); | 387 | m_state.needs_scheduling.store(false, std::memory_order_relaxed); |
| 388 | std::atomic_thread_fence(std::memory_order_seq_cst); | ||
| 388 | 389 | ||
| 389 | // Load the appropriate thread pointers for scheduling. | 390 | // Load the appropriate thread pointers for scheduling. |
| 390 | KThread* const cur_thread{GetCurrentThreadPointer(kernel)}; | 391 | KThread* const cur_thread{GetCurrentThreadPointer(kernel)}; |
| @@ -400,7 +401,8 @@ void KScheduler::ScheduleImpl() { | |||
| 400 | // If there aren't, we want to check if the highest priority thread is the same as the current | 401 | // If there aren't, we want to check if the highest priority thread is the same as the current |
| 401 | // thread. | 402 | // thread. |
| 402 | if (highest_priority_thread == cur_thread) { | 403 | if (highest_priority_thread == cur_thread) { |
| 403 | // If they're the same, then we can just return. | 404 | // If they're the same, then we can just issue a memory barrier and return. |
| 405 | std::atomic_thread_fence(std::memory_order_seq_cst); | ||
| 404 | return; | 406 | return; |
| 405 | } | 407 | } |
| 406 | 408 | ||
| @@ -476,7 +478,8 @@ void KScheduler::ScheduleImplFiber() { | |||
| 476 | 478 | ||
| 477 | // We failed to successfully do the context switch, and need to retry. | 479 | // We failed to successfully do the context switch, and need to retry. |
| 478 | // Clear needs_scheduling. | 480 | // Clear needs_scheduling. |
| 479 | m_state.needs_scheduling.store(false, std::memory_order_seq_cst); | 481 | m_state.needs_scheduling.store(false, std::memory_order_relaxed); |
| 482 | std::atomic_thread_fence(std::memory_order_seq_cst); | ||
| 480 | 483 | ||
| 481 | // Refresh the highest priority thread. | 484 | // Refresh the highest priority thread. |
| 482 | highest_priority_thread = m_state.highest_priority_thread; | 485 | highest_priority_thread = m_state.highest_priority_thread; |
diff --git a/src/core/hle/kernel/k_scheduler_lock.h b/src/core/hle/kernel/k_scheduler_lock.h index 73314b45e..129d60472 100644 --- a/src/core/hle/kernel/k_scheduler_lock.h +++ b/src/core/hle/kernel/k_scheduler_lock.h | |||
| @@ -60,6 +60,9 @@ public: | |||
| 60 | 60 | ||
| 61 | // Release an instance of the lock. | 61 | // Release an instance of the lock. |
| 62 | if ((--lock_count) == 0) { | 62 | if ((--lock_count) == 0) { |
| 63 | // Perform a memory barrier here. | ||
| 64 | std::atomic_thread_fence(std::memory_order_seq_cst); | ||
| 65 | |||
| 63 | // We're no longer going to hold the lock. Take note of what cores need scheduling. | 66 | // We're no longer going to hold the lock. Take note of what cores need scheduling. |
| 64 | const u64 cores_needing_scheduling = | 67 | const u64 cores_needing_scheduling = |
| 65 | SchedulerType::UpdateHighestPriorityThreads(kernel); | 68 | SchedulerType::UpdateHighestPriorityThreads(kernel); |
diff --git a/src/core/hle/kernel/k_session.cpp b/src/core/hle/kernel/k_session.cpp index 7a6534ac3..b6f6fe9d9 100644 --- a/src/core/hle/kernel/k_session.cpp +++ b/src/core/hle/kernel/k_session.cpp | |||
| @@ -76,7 +76,7 @@ void KSession::OnClientClosed() { | |||
| 76 | void KSession::PostDestroy(uintptr_t arg) { | 76 | void KSession::PostDestroy(uintptr_t arg) { |
| 77 | // Release the session count resource the owner process holds. | 77 | // Release the session count resource the owner process holds. |
| 78 | KProcess* owner = reinterpret_cast<KProcess*>(arg); | 78 | KProcess* owner = reinterpret_cast<KProcess*>(arg); |
| 79 | owner->GetResourceLimit()->Release(LimitableResource::Sessions, 1); | 79 | owner->GetResourceLimit()->Release(LimitableResource::SessionCountMax, 1); |
| 80 | owner->Close(); | 80 | owner->Close(); |
| 81 | } | 81 | } |
| 82 | 82 | ||
diff --git a/src/core/hle/kernel/k_shared_memory.cpp b/src/core/hle/kernel/k_shared_memory.cpp index a039cc591..10cd4c43d 100644 --- a/src/core/hle/kernel/k_shared_memory.cpp +++ b/src/core/hle/kernel/k_shared_memory.cpp | |||
| @@ -14,7 +14,7 @@ namespace Kernel { | |||
| 14 | KSharedMemory::KSharedMemory(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_} {} | 14 | KSharedMemory::KSharedMemory(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_} {} |
| 15 | 15 | ||
| 16 | KSharedMemory::~KSharedMemory() { | 16 | KSharedMemory::~KSharedMemory() { |
| 17 | kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemory, size); | 17 | kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemoryMax, size); |
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_, | 20 | Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_, |
| @@ -35,7 +35,7 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o | |||
| 35 | KResourceLimit* reslimit = kernel.GetSystemResourceLimit(); | 35 | KResourceLimit* reslimit = kernel.GetSystemResourceLimit(); |
| 36 | 36 | ||
| 37 | // Reserve memory for ourselves. | 37 | // Reserve memory for ourselves. |
| 38 | KScopedResourceReservation memory_reservation(reslimit, LimitableResource::PhysicalMemory, | 38 | KScopedResourceReservation memory_reservation(reslimit, LimitableResource::PhysicalMemoryMax, |
| 39 | size_); | 39 | size_); |
| 40 | R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); | 40 | R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); |
| 41 | 41 | ||
| @@ -57,7 +57,7 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o | |||
| 57 | 57 | ||
| 58 | void KSharedMemory::Finalize() { | 58 | void KSharedMemory::Finalize() { |
| 59 | // Release the memory reservation. | 59 | // Release the memory reservation. |
| 60 | resource_limit->Release(LimitableResource::PhysicalMemory, size); | 60 | resource_limit->Release(LimitableResource::PhysicalMemoryMax, size); |
| 61 | resource_limit->Close(); | 61 | resource_limit->Close(); |
| 62 | 62 | ||
| 63 | // Perform inherited finalization. | 63 | // Perform inherited finalization. |
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index cc88d08f0..21207fe99 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp | |||
| @@ -263,9 +263,9 @@ Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_ | |||
| 263 | R_SUCCEED(); | 263 | R_SUCCEED(); |
| 264 | } | 264 | } |
| 265 | 265 | ||
| 266 | Result KThread::InitializeDummyThread(KThread* thread) { | 266 | Result KThread::InitializeDummyThread(KThread* thread, KProcess* owner) { |
| 267 | // Initialize the thread. | 267 | // Initialize the thread. |
| 268 | R_TRY(thread->Initialize({}, {}, {}, DummyThreadPriority, 3, {}, ThreadType::Dummy)); | 268 | R_TRY(thread->Initialize({}, {}, {}, DummyThreadPriority, 3, owner, ThreadType::Dummy)); |
| 269 | 269 | ||
| 270 | // Initialize emulation parameters. | 270 | // Initialize emulation parameters. |
| 271 | thread->stack_parameters.disable_count = 0; | 271 | thread->stack_parameters.disable_count = 0; |
| @@ -303,7 +303,7 @@ void KThread::PostDestroy(uintptr_t arg) { | |||
| 303 | const bool resource_limit_release_hint = (arg & 1); | 303 | const bool resource_limit_release_hint = (arg & 1); |
| 304 | const s64 hint_value = (resource_limit_release_hint ? 0 : 1); | 304 | const s64 hint_value = (resource_limit_release_hint ? 0 : 1); |
| 305 | if (owner != nullptr) { | 305 | if (owner != nullptr) { |
| 306 | owner->GetResourceLimit()->Release(LimitableResource::Threads, 1, hint_value); | 306 | owner->GetResourceLimit()->Release(LimitableResource::ThreadCountMax, 1, hint_value); |
| 307 | owner->Close(); | 307 | owner->Close(); |
| 308 | } | 308 | } |
| 309 | } | 309 | } |
| @@ -1054,7 +1054,7 @@ void KThread::Exit() { | |||
| 1054 | 1054 | ||
| 1055 | // Release the thread resource hint, running thread count from parent. | 1055 | // Release the thread resource hint, running thread count from parent. |
| 1056 | if (parent != nullptr) { | 1056 | if (parent != nullptr) { |
| 1057 | parent->GetResourceLimit()->Release(Kernel::LimitableResource::Threads, 0, 1); | 1057 | parent->GetResourceLimit()->Release(Kernel::LimitableResource::ThreadCountMax, 0, 1); |
| 1058 | resource_limit_release_hint = true; | 1058 | resource_limit_release_hint = true; |
| 1059 | parent->DecrementRunningThreadCount(); | 1059 | parent->DecrementRunningThreadCount(); |
| 1060 | } | 1060 | } |
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index 30aa10c9a..f38c92bff 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h | |||
| @@ -415,7 +415,7 @@ public: | |||
| 415 | 415 | ||
| 416 | static void PostDestroy(uintptr_t arg); | 416 | static void PostDestroy(uintptr_t arg); |
| 417 | 417 | ||
| 418 | [[nodiscard]] static Result InitializeDummyThread(KThread* thread); | 418 | [[nodiscard]] static Result InitializeDummyThread(KThread* thread, KProcess* owner); |
| 419 | 419 | ||
| 420 | [[nodiscard]] static Result InitializeMainThread(Core::System& system, KThread* thread, | 420 | [[nodiscard]] static Result InitializeMainThread(Core::System& system, KThread* thread, |
| 421 | s32 virt_core); | 421 | s32 virt_core); |
diff --git a/src/core/hle/kernel/k_transfer_memory.cpp b/src/core/hle/kernel/k_transfer_memory.cpp index b0320eb73..9f34c2d46 100644 --- a/src/core/hle/kernel/k_transfer_memory.cpp +++ b/src/core/hle/kernel/k_transfer_memory.cpp | |||
| @@ -37,7 +37,7 @@ void KTransferMemory::Finalize() { | |||
| 37 | 37 | ||
| 38 | void KTransferMemory::PostDestroy(uintptr_t arg) { | 38 | void KTransferMemory::PostDestroy(uintptr_t arg) { |
| 39 | KProcess* owner = reinterpret_cast<KProcess*>(arg); | 39 | KProcess* owner = reinterpret_cast<KProcess*>(arg); |
| 40 | owner->GetResourceLimit()->Release(LimitableResource::TransferMemory, 1); | 40 | owner->GetResourceLimit()->Release(LimitableResource::TransferMemoryCountMax, 1); |
| 41 | owner->Close(); | 41 | owner->Close(); |
| 42 | } | 42 | } |
| 43 | 43 | ||
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index abff14079..b77723503 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -91,7 +91,7 @@ struct KernelCore::Impl { | |||
| 91 | pt_heap_region.GetSize()); | 91 | pt_heap_region.GetSize()); |
| 92 | } | 92 | } |
| 93 | 93 | ||
| 94 | RegisterHostThread(); | 94 | RegisterHostThread(nullptr); |
| 95 | 95 | ||
| 96 | default_service_thread = &CreateServiceThread(kernel, "DefaultServiceThread"); | 96 | default_service_thread = &CreateServiceThread(kernel, "DefaultServiceThread"); |
| 97 | } | 97 | } |
| @@ -229,18 +229,22 @@ struct KernelCore::Impl { | |||
| 229 | const auto kernel_size{sizes.second}; | 229 | const auto kernel_size{sizes.second}; |
| 230 | 230 | ||
| 231 | // If setting the default system values fails, then something seriously wrong has occurred. | 231 | // If setting the default system values fails, then something seriously wrong has occurred. |
| 232 | ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, total_size) | 232 | ASSERT( |
| 233 | system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemoryMax, total_size) | ||
| 234 | .IsSuccess()); | ||
| 235 | ASSERT(system_resource_limit->SetLimitValue(LimitableResource::ThreadCountMax, 800) | ||
| 233 | .IsSuccess()); | 236 | .IsSuccess()); |
| 234 | ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess()); | 237 | ASSERT(system_resource_limit->SetLimitValue(LimitableResource::EventCountMax, 900) |
| 235 | ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess()); | ||
| 236 | ASSERT(system_resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200) | ||
| 237 | .IsSuccess()); | 238 | .IsSuccess()); |
| 238 | ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess()); | 239 | ASSERT(system_resource_limit->SetLimitValue(LimitableResource::TransferMemoryCountMax, 200) |
| 239 | system_resource_limit->Reserve(LimitableResource::PhysicalMemory, kernel_size); | 240 | .IsSuccess()); |
| 241 | ASSERT(system_resource_limit->SetLimitValue(LimitableResource::SessionCountMax, 1133) | ||
| 242 | .IsSuccess()); | ||
| 243 | system_resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, kernel_size); | ||
| 240 | 244 | ||
| 241 | // Reserve secure applet memory, introduced in firmware 5.0.0 | 245 | // Reserve secure applet memory, introduced in firmware 5.0.0 |
| 242 | constexpr u64 secure_applet_memory_size{4_MiB}; | 246 | constexpr u64 secure_applet_memory_size{4_MiB}; |
| 243 | ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory, | 247 | ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, |
| 244 | secure_applet_memory_size)); | 248 | secure_applet_memory_size)); |
| 245 | } | 249 | } |
| 246 | 250 | ||
| @@ -373,15 +377,18 @@ struct KernelCore::Impl { | |||
| 373 | } | 377 | } |
| 374 | 378 | ||
| 375 | // Gets the dummy KThread for the caller, allocating a new one if this is the first time | 379 | // Gets the dummy KThread for the caller, allocating a new one if this is the first time |
| 376 | KThread* GetHostDummyThread() { | 380 | KThread* GetHostDummyThread(KThread* existing_thread) { |
| 377 | auto initialize = [this](KThread* thread) { | 381 | auto initialize = [this](KThread* thread) { |
| 378 | ASSERT(KThread::InitializeDummyThread(thread).IsSuccess()); | 382 | ASSERT(KThread::InitializeDummyThread(thread, nullptr).IsSuccess()); |
| 379 | thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId())); | 383 | thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId())); |
| 380 | return thread; | 384 | return thread; |
| 381 | }; | 385 | }; |
| 382 | 386 | ||
| 383 | thread_local auto raw_thread = KThread(system.Kernel()); | 387 | thread_local KThread raw_thread{system.Kernel()}; |
| 384 | thread_local auto thread = initialize(&raw_thread); | 388 | thread_local KThread* thread = nullptr; |
| 389 | if (thread == nullptr) { | ||
| 390 | thread = (existing_thread == nullptr) ? initialize(&raw_thread) : existing_thread; | ||
| 391 | } | ||
| 385 | 392 | ||
| 386 | return thread; | 393 | return thread; |
| 387 | } | 394 | } |
| @@ -396,9 +403,9 @@ struct KernelCore::Impl { | |||
| 396 | } | 403 | } |
| 397 | 404 | ||
| 398 | /// Registers a new host thread by allocating a host thread ID for it | 405 | /// Registers a new host thread by allocating a host thread ID for it |
| 399 | void RegisterHostThread() { | 406 | void RegisterHostThread(KThread* existing_thread) { |
| 400 | [[maybe_unused]] const auto this_id = GetHostThreadId(); | 407 | [[maybe_unused]] const auto this_id = GetHostThreadId(); |
| 401 | [[maybe_unused]] const auto dummy_thread = GetHostDummyThread(); | 408 | [[maybe_unused]] const auto dummy_thread = GetHostDummyThread(existing_thread); |
| 402 | } | 409 | } |
| 403 | 410 | ||
| 404 | [[nodiscard]] u32 GetCurrentHostThreadID() { | 411 | [[nodiscard]] u32 GetCurrentHostThreadID() { |
| @@ -429,7 +436,7 @@ struct KernelCore::Impl { | |||
| 429 | KThread* GetCurrentEmuThread() { | 436 | KThread* GetCurrentEmuThread() { |
| 430 | const auto thread_id = GetCurrentHostThreadID(); | 437 | const auto thread_id = GetCurrentHostThreadID(); |
| 431 | if (thread_id >= Core::Hardware::NUM_CPU_CORES) { | 438 | if (thread_id >= Core::Hardware::NUM_CPU_CORES) { |
| 432 | return GetHostDummyThread(); | 439 | return GetHostDummyThread(nullptr); |
| 433 | } | 440 | } |
| 434 | 441 | ||
| 435 | return current_thread; | 442 | return current_thread; |
| @@ -1120,8 +1127,12 @@ void KernelCore::RegisterCoreThread(std::size_t core_id) { | |||
| 1120 | impl->RegisterCoreThread(core_id); | 1127 | impl->RegisterCoreThread(core_id); |
| 1121 | } | 1128 | } |
| 1122 | 1129 | ||
| 1123 | void KernelCore::RegisterHostThread() { | 1130 | void KernelCore::RegisterHostThread(KThread* existing_thread) { |
| 1124 | impl->RegisterHostThread(); | 1131 | impl->RegisterHostThread(existing_thread); |
| 1132 | |||
| 1133 | if (existing_thread != nullptr) { | ||
| 1134 | ASSERT(GetCurrentEmuThread() == existing_thread); | ||
| 1135 | } | ||
| 1125 | } | 1136 | } |
| 1126 | 1137 | ||
| 1127 | u32 KernelCore::GetCurrentHostThreadID() const { | 1138 | u32 KernelCore::GetCurrentHostThreadID() const { |
| @@ -1196,16 +1207,28 @@ void KernelCore::Suspend(bool suspended) { | |||
| 1196 | const bool should_suspend{exception_exited || suspended}; | 1207 | const bool should_suspend{exception_exited || suspended}; |
| 1197 | const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable; | 1208 | const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable; |
| 1198 | 1209 | ||
| 1199 | for (auto* process : GetProcessList()) { | 1210 | std::vector<KScopedAutoObject<KThread>> process_threads; |
| 1200 | process->SetActivity(activity); | 1211 | { |
| 1212 | KScopedSchedulerLock sl{*this}; | ||
| 1213 | |||
| 1214 | if (auto* process = CurrentProcess(); process != nullptr) { | ||
| 1215 | process->SetActivity(activity); | ||
| 1216 | |||
| 1217 | if (!should_suspend) { | ||
| 1218 | // Runnable now; no need to wait. | ||
| 1219 | return; | ||
| 1220 | } | ||
| 1201 | 1221 | ||
| 1202 | if (should_suspend) { | ||
| 1203 | // Wait for execution to stop | ||
| 1204 | for (auto* thread : process->GetThreadList()) { | 1222 | for (auto* thread : process->GetThreadList()) { |
| 1205 | thread->WaitUntilSuspended(); | 1223 | process_threads.emplace_back(thread); |
| 1206 | } | 1224 | } |
| 1207 | } | 1225 | } |
| 1208 | } | 1226 | } |
| 1227 | |||
| 1228 | // Wait for execution to stop. | ||
| 1229 | for (auto& thread : process_threads) { | ||
| 1230 | thread->WaitUntilSuspended(); | ||
| 1231 | } | ||
| 1209 | } | 1232 | } |
| 1210 | 1233 | ||
| 1211 | void KernelCore::ShutdownCores() { | 1234 | 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: | |||
| 240 | void RegisterCoreThread(std::size_t core_id); | 240 | void RegisterCoreThread(std::size_t core_id); |
| 241 | 241 | ||
| 242 | /// Register the current thread as a non CPU core thread. | 242 | /// Register the current thread as a non CPU core thread. |
| 243 | void RegisterHostThread(); | 243 | void RegisterHostThread(KThread* existing_thread = nullptr); |
| 244 | 244 | ||
| 245 | /// Gets the virtual memory manager for the kernel. | 245 | /// Gets the virtual memory manager for the kernel. |
| 246 | KMemoryManager& MemoryManager(); | 246 | KMemoryManager& MemoryManager(); |
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp index d4375962f..3044922ac 100644 --- a/src/core/hle/kernel/physical_core.cpp +++ b/src/core/hle/kernel/physical_core.cpp | |||
| @@ -12,7 +12,7 @@ namespace Kernel { | |||
| 12 | 12 | ||
| 13 | PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_) | 13 | PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_) |
| 14 | : core_index{core_index_}, system{system_}, scheduler{scheduler_} { | 14 | : core_index{core_index_}, system{system_}, scheduler{scheduler_} { |
| 15 | #ifdef ARCHITECTURE_x86_64 | 15 | #if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64) |
| 16 | // TODO(bunnei): Initialization relies on a core being available. We may later replace this with | 16 | // TODO(bunnei): Initialization relies on a core being available. We may later replace this with |
| 17 | // a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager. | 17 | // a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager. |
| 18 | auto& kernel = system.Kernel(); | 18 | auto& kernel = system.Kernel(); |
| @@ -26,7 +26,7 @@ PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KSche | |||
| 26 | PhysicalCore::~PhysicalCore() = default; | 26 | PhysicalCore::~PhysicalCore() = default; |
| 27 | 27 | ||
| 28 | void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) { | 28 | void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) { |
| 29 | #ifdef ARCHITECTURE_x86_64 | 29 | #if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64) |
| 30 | auto& kernel = system.Kernel(); | 30 | auto& kernel = system.Kernel(); |
| 31 | if (!is_64_bit) { | 31 | if (!is_64_bit) { |
| 32 | // We already initialized a 64-bit core, replace with a 32-bit one. | 32 | // We already initialized a 64-bit core, replace with a 32-bit one. |
diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp index c8fe42537..e6e41ac34 100644 --- a/src/core/hle/kernel/service_thread.cpp +++ b/src/core/hle/kernel/service_thread.cpp | |||
| @@ -36,11 +36,11 @@ public: | |||
| 36 | private: | 36 | private: |
| 37 | KernelCore& kernel; | 37 | KernelCore& kernel; |
| 38 | 38 | ||
| 39 | std::jthread m_thread; | 39 | std::jthread m_host_thread; |
| 40 | std::mutex m_session_mutex; | 40 | std::mutex m_session_mutex; |
| 41 | std::map<KServerSession*, std::shared_ptr<SessionRequestManager>> m_sessions; | 41 | std::map<KServerSession*, std::shared_ptr<SessionRequestManager>> m_sessions; |
| 42 | KEvent* m_wakeup_event; | 42 | KEvent* m_wakeup_event; |
| 43 | KProcess* m_process; | 43 | KThread* m_thread; |
| 44 | std::atomic<bool> m_shutdown_requested; | 44 | std::atomic<bool> m_shutdown_requested; |
| 45 | const std::string m_service_name; | 45 | const std::string m_service_name; |
| 46 | }; | 46 | }; |
| @@ -132,7 +132,7 @@ void ServiceThread::Impl::SessionClosed(KServerSession* server_session, | |||
| 132 | void ServiceThread::Impl::LoopProcess() { | 132 | void ServiceThread::Impl::LoopProcess() { |
| 133 | Common::SetCurrentThreadName(m_service_name.c_str()); | 133 | Common::SetCurrentThreadName(m_service_name.c_str()); |
| 134 | 134 | ||
| 135 | kernel.RegisterHostThread(); | 135 | kernel.RegisterHostThread(m_thread); |
| 136 | 136 | ||
| 137 | while (!m_shutdown_requested.load()) { | 137 | while (!m_shutdown_requested.load()) { |
| 138 | WaitAndProcessImpl(); | 138 | WaitAndProcessImpl(); |
| @@ -160,7 +160,7 @@ ServiceThread::Impl::~Impl() { | |||
| 160 | // Shut down the processing thread. | 160 | // Shut down the processing thread. |
| 161 | m_shutdown_requested.store(true); | 161 | m_shutdown_requested.store(true); |
| 162 | m_wakeup_event->Signal(); | 162 | m_wakeup_event->Signal(); |
| 163 | m_thread.join(); | 163 | m_host_thread.join(); |
| 164 | 164 | ||
| 165 | // Lock mutex. | 165 | // Lock mutex. |
| 166 | m_session_mutex.lock(); | 166 | m_session_mutex.lock(); |
| @@ -177,33 +177,22 @@ ServiceThread::Impl::~Impl() { | |||
| 177 | m_wakeup_event->GetReadableEvent().Close(); | 177 | m_wakeup_event->GetReadableEvent().Close(); |
| 178 | m_wakeup_event->Close(); | 178 | m_wakeup_event->Close(); |
| 179 | 179 | ||
| 180 | // Close process. | 180 | // Close thread. |
| 181 | m_process->Close(); | 181 | m_thread->Close(); |
| 182 | } | 182 | } |
| 183 | 183 | ||
| 184 | ServiceThread::Impl::Impl(KernelCore& kernel_, const std::string& service_name) | 184 | ServiceThread::Impl::Impl(KernelCore& kernel_, const std::string& service_name) |
| 185 | : kernel{kernel_}, m_service_name{service_name} { | 185 | : kernel{kernel_}, m_service_name{service_name} { |
| 186 | // Initialize process. | ||
| 187 | m_process = KProcess::Create(kernel); | ||
| 188 | KProcess::Initialize(m_process, kernel.System(), service_name, | ||
| 189 | KProcess::ProcessType::KernelInternal, kernel.GetSystemResourceLimit()); | ||
| 190 | |||
| 191 | // Reserve a new event from the process resource limit | ||
| 192 | KScopedResourceReservation event_reservation(m_process, LimitableResource::Events); | ||
| 193 | ASSERT(event_reservation.Succeeded()); | ||
| 194 | |||
| 195 | // Initialize event. | 186 | // Initialize event. |
| 196 | m_wakeup_event = KEvent::Create(kernel); | 187 | m_wakeup_event = KEvent::Create(kernel); |
| 197 | m_wakeup_event->Initialize(m_process); | 188 | m_wakeup_event->Initialize(nullptr); |
| 198 | |||
| 199 | // Commit the event reservation. | ||
| 200 | event_reservation.Commit(); | ||
| 201 | 189 | ||
| 202 | // Register the event. | 190 | // Initialize thread. |
| 203 | KEvent::Register(kernel, m_wakeup_event); | 191 | m_thread = KThread::Create(kernel); |
| 192 | ASSERT(KThread::InitializeDummyThread(m_thread, nullptr).IsSuccess()); | ||
| 204 | 193 | ||
| 205 | // Start thread. | 194 | // Start thread. |
| 206 | m_thread = std::jthread([this] { LoopProcess(); }); | 195 | m_host_thread = std::jthread([this] { LoopProcess(); }); |
| 207 | } | 196 | } |
| 208 | 197 | ||
| 209 | ServiceThread::ServiceThread(KernelCore& kernel, const std::string& name) | 198 | 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 | |||
| 267 | 267 | ||
| 268 | // Reserve a new session from the process resource limit. | 268 | // Reserve a new session from the process resource limit. |
| 269 | // FIXME: LimitableResource_SessionCountMax | 269 | // FIXME: LimitableResource_SessionCountMax |
| 270 | KScopedResourceReservation session_reservation(&process, LimitableResource::Sessions); | 270 | KScopedResourceReservation session_reservation(&process, LimitableResource::SessionCountMax); |
| 271 | if (session_reservation.Succeeded()) { | 271 | if (session_reservation.Succeeded()) { |
| 272 | session = T::Create(system.Kernel()); | 272 | session = T::Create(system.Kernel()); |
| 273 | } else { | 273 | } else { |
| @@ -298,7 +298,7 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien | |||
| 298 | 298 | ||
| 299 | // We successfully allocated a session, so add the object we allocated to the resource | 299 | // We successfully allocated a session, so add the object we allocated to the resource |
| 300 | // limit. | 300 | // limit. |
| 301 | // system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::Sessions, 1); | 301 | // system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::SessionCountMax, 1); |
| 302 | } | 302 | } |
| 303 | 303 | ||
| 304 | // Check that we successfully created a session. | 304 | // Check that we successfully created a session. |
| @@ -656,27 +656,12 @@ static Result ArbitrateUnlock32(Core::System& system, u32 address) { | |||
| 656 | return ArbitrateUnlock(system, address); | 656 | return ArbitrateUnlock(system, address); |
| 657 | } | 657 | } |
| 658 | 658 | ||
| 659 | enum class BreakType : u32 { | ||
| 660 | Panic = 0, | ||
| 661 | AssertionFailed = 1, | ||
| 662 | PreNROLoad = 3, | ||
| 663 | PostNROLoad = 4, | ||
| 664 | PreNROUnload = 5, | ||
| 665 | PostNROUnload = 6, | ||
| 666 | CppException = 7, | ||
| 667 | }; | ||
| 668 | |||
| 669 | struct BreakReason { | ||
| 670 | union { | ||
| 671 | u32 raw; | ||
| 672 | BitField<0, 30, BreakType> break_type; | ||
| 673 | BitField<31, 1, u32> signal_debugger; | ||
| 674 | }; | ||
| 675 | }; | ||
| 676 | |||
| 677 | /// Break program execution | 659 | /// Break program execution |
| 678 | static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { | 660 | static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { |
| 679 | BreakReason break_reason{reason}; | 661 | BreakReason break_reason = |
| 662 | static_cast<BreakReason>(reason & ~static_cast<u32>(BreakReason::NotificationOnlyFlag)); | ||
| 663 | bool notification_only = (reason & static_cast<u32>(BreakReason::NotificationOnlyFlag)) != 0; | ||
| 664 | |||
| 680 | bool has_dumped_buffer{}; | 665 | bool has_dumped_buffer{}; |
| 681 | std::vector<u8> debug_buffer; | 666 | std::vector<u8> debug_buffer; |
| 682 | 667 | ||
| @@ -705,57 +690,56 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { | |||
| 705 | } | 690 | } |
| 706 | has_dumped_buffer = true; | 691 | has_dumped_buffer = true; |
| 707 | }; | 692 | }; |
| 708 | switch (break_reason.break_type) { | 693 | switch (break_reason) { |
| 709 | case BreakType::Panic: | 694 | case BreakReason::Panic: |
| 710 | LOG_CRITICAL(Debug_Emulated, "Signalling debugger, PANIC! info1=0x{:016X}, info2=0x{:016X}", | 695 | LOG_CRITICAL(Debug_Emulated, "Userspace PANIC! info1=0x{:016X}, info2=0x{:016X}", info1, |
| 711 | info1, info2); | 696 | info2); |
| 712 | handle_debug_buffer(info1, info2); | 697 | handle_debug_buffer(info1, info2); |
| 713 | break; | 698 | break; |
| 714 | case BreakType::AssertionFailed: | 699 | case BreakReason::Assert: |
| 715 | LOG_CRITICAL(Debug_Emulated, | 700 | LOG_CRITICAL(Debug_Emulated, "Userspace Assertion failed! info1=0x{:016X}, info2=0x{:016X}", |
| 716 | "Signalling debugger, Assertion failed! info1=0x{:016X}, info2=0x{:016X}", | ||
| 717 | info1, info2); | 701 | info1, info2); |
| 718 | handle_debug_buffer(info1, info2); | 702 | handle_debug_buffer(info1, info2); |
| 719 | break; | 703 | break; |
| 720 | case BreakType::PreNROLoad: | 704 | case BreakReason::User: |
| 721 | LOG_WARNING( | 705 | LOG_WARNING(Debug_Emulated, "Userspace Break! 0x{:016X} with size 0x{:016X}", info1, info2); |
| 722 | Debug_Emulated, | 706 | handle_debug_buffer(info1, info2); |
| 723 | "Signalling debugger, Attempting to load an NRO at 0x{:016X} with size 0x{:016X}", | ||
| 724 | info1, info2); | ||
| 725 | break; | 707 | break; |
| 726 | case BreakType::PostNROLoad: | 708 | case BreakReason::PreLoadDll: |
| 727 | LOG_WARNING(Debug_Emulated, | 709 | LOG_INFO(Debug_Emulated, |
| 728 | "Signalling debugger, Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1, | 710 | "Userspace Attempting to load an NRO at 0x{:016X} with size 0x{:016X}", info1, |
| 729 | info2); | 711 | info2); |
| 730 | break; | 712 | break; |
| 731 | case BreakType::PreNROUnload: | 713 | case BreakReason::PostLoadDll: |
| 732 | LOG_WARNING( | 714 | LOG_INFO(Debug_Emulated, "Userspace Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1, |
| 733 | Debug_Emulated, | 715 | info2); |
| 734 | "Signalling debugger, Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}", | ||
| 735 | info1, info2); | ||
| 736 | break; | 716 | break; |
| 737 | case BreakType::PostNROUnload: | 717 | case BreakReason::PreUnloadDll: |
| 738 | LOG_WARNING(Debug_Emulated, | 718 | LOG_INFO(Debug_Emulated, |
| 739 | "Signalling debugger, Unloaded an NRO at 0x{:016X} with size 0x{:016X}", info1, | 719 | "Userspace Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}", info1, |
| 740 | info2); | 720 | info2); |
| 741 | break; | 721 | break; |
| 742 | case BreakType::CppException: | 722 | case BreakReason::PostUnloadDll: |
| 723 | LOG_INFO(Debug_Emulated, "Userspace Unloaded an NRO at 0x{:016X} with size 0x{:016X}", | ||
| 724 | info1, info2); | ||
| 725 | break; | ||
| 726 | case BreakReason::CppException: | ||
| 743 | LOG_CRITICAL(Debug_Emulated, "Signalling debugger. Uncaught C++ exception encountered."); | 727 | LOG_CRITICAL(Debug_Emulated, "Signalling debugger. Uncaught C++ exception encountered."); |
| 744 | break; | 728 | break; |
| 745 | default: | 729 | default: |
| 746 | LOG_WARNING( | 730 | LOG_WARNING( |
| 747 | Debug_Emulated, | 731 | Debug_Emulated, |
| 748 | "Signalling debugger, Unknown break reason {}, info1=0x{:016X}, info2=0x{:016X}", | 732 | "Signalling debugger, Unknown break reason {:#X}, info1=0x{:016X}, info2=0x{:016X}", |
| 749 | static_cast<u32>(break_reason.break_type.Value()), info1, info2); | 733 | reason, info1, info2); |
| 750 | handle_debug_buffer(info1, info2); | 734 | handle_debug_buffer(info1, info2); |
| 751 | break; | 735 | break; |
| 752 | } | 736 | } |
| 753 | 737 | ||
| 754 | system.GetReporter().SaveSvcBreakReport( | 738 | system.GetReporter().SaveSvcBreakReport(reason, notification_only, info1, info2, |
| 755 | static_cast<u32>(break_reason.break_type.Value()), break_reason.signal_debugger.As<bool>(), | 739 | has_dumped_buffer ? std::make_optional(debug_buffer) |
| 756 | info1, info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt); | 740 | : std::nullopt); |
| 757 | 741 | ||
| 758 | if (!break_reason.signal_debugger) { | 742 | if (!notification_only) { |
| 759 | LOG_CRITICAL( | 743 | LOG_CRITICAL( |
| 760 | Debug_Emulated, | 744 | Debug_Emulated, |
| 761 | "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", | 745 | "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", |
| @@ -1716,13 +1700,13 @@ static Result QueryProcessMemory(Core::System& system, VAddr memory_info_address | |||
| 1716 | auto& memory{system.Memory()}; | 1700 | auto& memory{system.Memory()}; |
| 1717 | const auto memory_info{process->PageTable().QueryInfo(address).GetSvcMemoryInfo()}; | 1701 | const auto memory_info{process->PageTable().QueryInfo(address).GetSvcMemoryInfo()}; |
| 1718 | 1702 | ||
| 1719 | memory.Write64(memory_info_address + 0x00, memory_info.addr); | 1703 | memory.Write64(memory_info_address + 0x00, memory_info.base_address); |
| 1720 | memory.Write64(memory_info_address + 0x08, memory_info.size); | 1704 | memory.Write64(memory_info_address + 0x08, memory_info.size); |
| 1721 | memory.Write32(memory_info_address + 0x10, static_cast<u32>(memory_info.state) & 0xff); | 1705 | memory.Write32(memory_info_address + 0x10, static_cast<u32>(memory_info.state) & 0xff); |
| 1722 | memory.Write32(memory_info_address + 0x14, static_cast<u32>(memory_info.attr)); | 1706 | memory.Write32(memory_info_address + 0x14, static_cast<u32>(memory_info.attribute)); |
| 1723 | memory.Write32(memory_info_address + 0x18, static_cast<u32>(memory_info.perm)); | 1707 | memory.Write32(memory_info_address + 0x18, static_cast<u32>(memory_info.permission)); |
| 1724 | memory.Write32(memory_info_address + 0x1c, memory_info.ipc_refcount); | 1708 | memory.Write32(memory_info_address + 0x1c, memory_info.ipc_count); |
| 1725 | memory.Write32(memory_info_address + 0x20, memory_info.device_refcount); | 1709 | memory.Write32(memory_info_address + 0x20, memory_info.device_count); |
| 1726 | memory.Write32(memory_info_address + 0x24, 0); | 1710 | memory.Write32(memory_info_address + 0x24, 0); |
| 1727 | 1711 | ||
| 1728 | // Page info appears to be currently unused by the kernel and is always set to zero. | 1712 | // Page info appears to be currently unused by the kernel and is always set to zero. |
| @@ -1943,7 +1927,7 @@ static Result CreateThread(Core::System& system, Handle* out_handle, VAddr entry | |||
| 1943 | 1927 | ||
| 1944 | // Reserve a new thread from the process resource limit (waiting up to 100ms). | 1928 | // Reserve a new thread from the process resource limit (waiting up to 100ms). |
| 1945 | KScopedResourceReservation thread_reservation( | 1929 | KScopedResourceReservation thread_reservation( |
| 1946 | kernel.CurrentProcess(), LimitableResource::Threads, 1, | 1930 | kernel.CurrentProcess(), LimitableResource::ThreadCountMax, 1, |
| 1947 | system.CoreTiming().GetGlobalTimeNs().count() + 100000000); | 1931 | system.CoreTiming().GetGlobalTimeNs().count() + 100000000); |
| 1948 | if (!thread_reservation.Succeeded()) { | 1932 | if (!thread_reservation.Succeeded()) { |
| 1949 | LOG_ERROR(Kernel_SVC, "Could not reserve a new thread"); | 1933 | LOG_ERROR(Kernel_SVC, "Could not reserve a new thread"); |
| @@ -2344,7 +2328,7 @@ static Result CreateTransferMemory(Core::System& system, Handle* out, VAddr addr | |||
| 2344 | 2328 | ||
| 2345 | // Reserve a new transfer memory from the process resource limit. | 2329 | // Reserve a new transfer memory from the process resource limit. |
| 2346 | KScopedResourceReservation trmem_reservation(kernel.CurrentProcess(), | 2330 | KScopedResourceReservation trmem_reservation(kernel.CurrentProcess(), |
| 2347 | LimitableResource::TransferMemory); | 2331 | LimitableResource::TransferMemoryCountMax); |
| 2348 | R_UNLESS(trmem_reservation.Succeeded(), ResultLimitReached); | 2332 | R_UNLESS(trmem_reservation.Succeeded(), ResultLimitReached); |
| 2349 | 2333 | ||
| 2350 | // Create the transfer memory. | 2334 | // Create the transfer memory. |
| @@ -2496,7 +2480,7 @@ static Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_r | |||
| 2496 | 2480 | ||
| 2497 | // Reserve a new event from the process resource limit | 2481 | // Reserve a new event from the process resource limit |
| 2498 | KScopedResourceReservation event_reservation(kernel.CurrentProcess(), | 2482 | KScopedResourceReservation event_reservation(kernel.CurrentProcess(), |
| 2499 | LimitableResource::Events); | 2483 | LimitableResource::EventCountMax); |
| 2500 | R_UNLESS(event_reservation.Succeeded(), ResultLimitReached); | 2484 | R_UNLESS(event_reservation.Succeeded(), ResultLimitReached); |
| 2501 | 2485 | ||
| 2502 | // Create a new event. | 2486 | // Create a new event. |
| @@ -2539,11 +2523,6 @@ static Result CreateEvent32(Core::System& system, Handle* out_write, Handle* out | |||
| 2539 | static Result GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) { | 2523 | static Result GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) { |
| 2540 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type); | 2524 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type); |
| 2541 | 2525 | ||
| 2542 | // This function currently only allows retrieving a process' status. | ||
| 2543 | enum class InfoType { | ||
| 2544 | Status, | ||
| 2545 | }; | ||
| 2546 | |||
| 2547 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | 2526 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 2548 | KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle); | 2527 | KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle); |
| 2549 | if (process.IsNull()) { | 2528 | if (process.IsNull()) { |
| @@ -2552,9 +2531,9 @@ static Result GetProcessInfo(Core::System& system, u64* out, Handle process_hand | |||
| 2552 | return ResultInvalidHandle; | 2531 | return ResultInvalidHandle; |
| 2553 | } | 2532 | } |
| 2554 | 2533 | ||
| 2555 | const auto info_type = static_cast<InfoType>(type); | 2534 | const auto info_type = static_cast<ProcessInfoType>(type); |
| 2556 | if (info_type != InfoType::Status) { | 2535 | if (info_type != ProcessInfoType::ProcessState) { |
| 2557 | LOG_ERROR(Kernel_SVC, "Expected info_type to be Status but got {} instead", type); | 2536 | LOG_ERROR(Kernel_SVC, "Expected info_type to be ProcessState but got {} instead", type); |
| 2558 | return ResultInvalidEnumValue; | 2537 | return ResultInvalidEnumValue; |
| 2559 | } | 2538 | } |
| 2560 | 2539 | ||
| @@ -2722,14 +2701,24 @@ static Result GetThreadList(Core::System& system, u32* out_num_threads, VAddr ou | |||
| 2722 | return ResultSuccess; | 2701 | return ResultSuccess; |
| 2723 | } | 2702 | } |
| 2724 | 2703 | ||
| 2725 | static Result FlushProcessDataCache32([[maybe_unused]] Core::System& system, | 2704 | static Result FlushProcessDataCache32(Core::System& system, Handle process_handle, u64 address, |
| 2726 | [[maybe_unused]] Handle handle, [[maybe_unused]] u32 address, | 2705 | u64 size) { |
| 2727 | [[maybe_unused]] u32 size) { | 2706 | // Validate address/size. |
| 2728 | // Note(Blinkhawk): For emulation purposes of the data cache this is mostly a no-op, | 2707 | R_UNLESS(size > 0, ResultInvalidSize); |
| 2729 | // as all emulation is done in the same cache level in host architecture, thus data cache | 2708 | R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory); |
| 2730 | // does not need flushing. | 2709 | R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory); |
| 2731 | LOG_DEBUG(Kernel_SVC, "called"); | 2710 | |
| 2732 | return ResultSuccess; | 2711 | // Get the process from its handle. |
| 2712 | KScopedAutoObject process = | ||
| 2713 | system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle); | ||
| 2714 | R_UNLESS(process.IsNotNull(), ResultInvalidHandle); | ||
| 2715 | |||
| 2716 | // Verify the region is within range. | ||
| 2717 | auto& page_table = process->PageTable(); | ||
| 2718 | R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); | ||
| 2719 | |||
| 2720 | // Perform the operation. | ||
| 2721 | R_RETURN(system.Memory().FlushDataCache(*process, address, size)); | ||
| 2733 | } | 2722 | } |
| 2734 | 2723 | ||
| 2735 | namespace { | 2724 | 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 @@ | |||
| 8 | 8 | ||
| 9 | namespace Kernel::Svc { | 9 | namespace Kernel::Svc { |
| 10 | 10 | ||
| 11 | using Handle = u32; | ||
| 12 | |||
| 11 | enum class MemoryState : u32 { | 13 | enum class MemoryState : u32 { |
| 12 | Free = 0x00, | 14 | Free = 0x00, |
| 13 | Io = 0x01, | 15 | Io = 0x01, |
| @@ -55,17 +57,6 @@ enum class MemoryPermission : u32 { | |||
| 55 | }; | 57 | }; |
| 56 | DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission); | 58 | DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission); |
| 57 | 59 | ||
| 58 | struct MemoryInfo { | ||
| 59 | u64 addr{}; | ||
| 60 | u64 size{}; | ||
| 61 | MemoryState state{}; | ||
| 62 | MemoryAttribute attr{}; | ||
| 63 | MemoryPermission perm{}; | ||
| 64 | u32 ipc_refcount{}; | ||
| 65 | u32 device_refcount{}; | ||
| 66 | u32 padding{}; | ||
| 67 | }; | ||
| 68 | |||
| 69 | enum class SignalType : u32 { | 60 | enum class SignalType : u32 { |
| 70 | Signal = 0, | 61 | Signal = 0, |
| 71 | SignalAndIncrementIfEqual = 1, | 62 | SignalAndIncrementIfEqual = 1, |
| @@ -124,7 +115,57 @@ enum class ProcessExitReason : u32 { | |||
| 124 | 115 | ||
| 125 | constexpr inline size_t ThreadLocalRegionSize = 0x200; | 116 | constexpr inline size_t ThreadLocalRegionSize = 0x200; |
| 126 | 117 | ||
| 127 | // Debug types. | 118 | struct PageInfo { |
| 119 | u32 flags; | ||
| 120 | }; | ||
| 121 | |||
| 122 | // Info Types. | ||
| 123 | enum class InfoType : u32 { | ||
| 124 | CoreMask = 0, | ||
| 125 | PriorityMask = 1, | ||
| 126 | AliasRegionAddress = 2, | ||
| 127 | AliasRegionSize = 3, | ||
| 128 | HeapRegionAddress = 4, | ||
| 129 | HeapRegionSize = 5, | ||
| 130 | TotalMemorySize = 6, | ||
| 131 | UsedMemorySize = 7, | ||
| 132 | DebuggerAttached = 8, | ||
| 133 | ResourceLimit = 9, | ||
| 134 | IdleTickCount = 10, | ||
| 135 | RandomEntropy = 11, | ||
| 136 | AslrRegionAddress = 12, | ||
| 137 | AslrRegionSize = 13, | ||
| 138 | StackRegionAddress = 14, | ||
| 139 | StackRegionSize = 15, | ||
| 140 | SystemResourceSizeTotal = 16, | ||
| 141 | SystemResourceSizeUsed = 17, | ||
| 142 | ProgramId = 18, | ||
| 143 | InitialProcessIdRange = 19, | ||
| 144 | UserExceptionContextAddress = 20, | ||
| 145 | TotalNonSystemMemorySize = 21, | ||
| 146 | UsedNonSystemMemorySize = 22, | ||
| 147 | IsApplication = 23, | ||
| 148 | FreeThreadCount = 24, | ||
| 149 | ThreadTickCount = 25, | ||
| 150 | IsSvcPermitted = 26, | ||
| 151 | |||
| 152 | MesosphereMeta = 65000, | ||
| 153 | MesosphereCurrentProcess = 65001, | ||
| 154 | }; | ||
| 155 | |||
| 156 | enum class BreakReason : u32 { | ||
| 157 | Panic = 0, | ||
| 158 | Assert = 1, | ||
| 159 | User = 2, | ||
| 160 | PreLoadDll = 3, | ||
| 161 | PostLoadDll = 4, | ||
| 162 | PreUnloadDll = 5, | ||
| 163 | PostUnloadDll = 6, | ||
| 164 | CppException = 7, | ||
| 165 | |||
| 166 | NotificationOnlyFlag = 0x80000000, | ||
| 167 | }; | ||
| 168 | |||
| 128 | enum class DebugEvent : u32 { | 169 | enum class DebugEvent : u32 { |
| 129 | CreateProcess = 0, | 170 | CreateProcess = 0, |
| 130 | CreateThread = 1, | 171 | CreateThread = 1, |
| @@ -133,6 +174,14 @@ enum class DebugEvent : u32 { | |||
| 133 | Exception = 4, | 174 | Exception = 4, |
| 134 | }; | 175 | }; |
| 135 | 176 | ||
| 177 | enum class DebugThreadParam : u32 { | ||
| 178 | Priority = 0, | ||
| 179 | State = 1, | ||
| 180 | IdealCore = 2, | ||
| 181 | CurrentCore = 3, | ||
| 182 | AffinityMask = 4, | ||
| 183 | }; | ||
| 184 | |||
| 136 | enum class DebugException : u32 { | 185 | enum class DebugException : u32 { |
| 137 | UndefinedInstruction = 0, | 186 | UndefinedInstruction = 0, |
| 138 | InstructionAbort = 1, | 187 | InstructionAbort = 1, |
| @@ -146,4 +195,401 @@ enum class DebugException : u32 { | |||
| 146 | MemorySystemError = 9, | 195 | MemorySystemError = 9, |
| 147 | }; | 196 | }; |
| 148 | 197 | ||
| 198 | enum class DebugEventFlag : u32 { | ||
| 199 | Stopped = (1u << 0), | ||
| 200 | }; | ||
| 201 | |||
| 202 | enum class BreakPointType : u32 { | ||
| 203 | HardwareInstruction = 0, | ||
| 204 | HardwareData = 1, | ||
| 205 | }; | ||
| 206 | |||
| 207 | enum class HardwareBreakPointRegisterName : u32 { | ||
| 208 | I0 = 0, | ||
| 209 | I1 = 1, | ||
| 210 | I2 = 2, | ||
| 211 | I3 = 3, | ||
| 212 | I4 = 4, | ||
| 213 | I5 = 5, | ||
| 214 | I6 = 6, | ||
| 215 | I7 = 7, | ||
| 216 | I8 = 8, | ||
| 217 | I9 = 9, | ||
| 218 | I10 = 10, | ||
| 219 | I11 = 11, | ||
| 220 | I12 = 12, | ||
| 221 | I13 = 13, | ||
| 222 | I14 = 14, | ||
| 223 | I15 = 15, | ||
| 224 | D0 = 16, | ||
| 225 | D1 = 17, | ||
| 226 | D2 = 18, | ||
| 227 | D3 = 19, | ||
| 228 | D4 = 20, | ||
| 229 | D5 = 21, | ||
| 230 | D6 = 22, | ||
| 231 | D7 = 23, | ||
| 232 | D8 = 24, | ||
| 233 | D9 = 25, | ||
| 234 | D10 = 26, | ||
| 235 | D11 = 27, | ||
| 236 | D12 = 28, | ||
| 237 | D13 = 29, | ||
| 238 | D14 = 30, | ||
| 239 | D15 = 31, | ||
| 240 | }; | ||
| 241 | |||
| 242 | namespace lp64 { | ||
| 243 | struct LastThreadContext { | ||
| 244 | u64 fp; | ||
| 245 | u64 sp; | ||
| 246 | u64 lr; | ||
| 247 | u64 pc; | ||
| 248 | }; | ||
| 249 | |||
| 250 | struct PhysicalMemoryInfo { | ||
| 251 | PAddr physical_address; | ||
| 252 | u64 virtual_address; | ||
| 253 | u64 size; | ||
| 254 | }; | ||
| 255 | |||
| 256 | struct DebugInfoCreateProcess { | ||
| 257 | u64 program_id; | ||
| 258 | u64 process_id; | ||
| 259 | std::array<char, 0xC> name; | ||
| 260 | u32 flags; | ||
| 261 | u64 user_exception_context_address; // 5.0.0+ | ||
| 262 | }; | ||
| 263 | |||
| 264 | struct DebugInfoCreateThread { | ||
| 265 | u64 thread_id; | ||
| 266 | u64 tls_address; | ||
| 267 | // Removed in 11.0.0 u64 entrypoint; | ||
| 268 | }; | ||
| 269 | |||
| 270 | struct DebugInfoExitProcess { | ||
| 271 | ProcessExitReason reason; | ||
| 272 | }; | ||
| 273 | |||
| 274 | struct DebugInfoExitThread { | ||
| 275 | ThreadExitReason reason; | ||
| 276 | }; | ||
| 277 | |||
| 278 | struct DebugInfoUndefinedInstructionException { | ||
| 279 | u32 insn; | ||
| 280 | }; | ||
| 281 | |||
| 282 | struct DebugInfoDataAbortException { | ||
| 283 | u64 address; | ||
| 284 | }; | ||
| 285 | |||
| 286 | struct DebugInfoAlignmentFaultException { | ||
| 287 | u64 address; | ||
| 288 | }; | ||
| 289 | |||
| 290 | struct DebugInfoBreakPointException { | ||
| 291 | BreakPointType type; | ||
| 292 | u64 address; | ||
| 293 | }; | ||
| 294 | |||
| 295 | struct DebugInfoUserBreakException { | ||
| 296 | BreakReason break_reason; | ||
| 297 | u64 address; | ||
| 298 | u64 size; | ||
| 299 | }; | ||
| 300 | |||
| 301 | struct DebugInfoDebuggerBreakException { | ||
| 302 | std::array<u64, 4> active_thread_ids; | ||
| 303 | }; | ||
| 304 | |||
| 305 | struct DebugInfoUndefinedSystemCallException { | ||
| 306 | u32 id; | ||
| 307 | }; | ||
| 308 | |||
| 309 | union DebugInfoSpecificException { | ||
| 310 | DebugInfoUndefinedInstructionException undefined_instruction; | ||
| 311 | DebugInfoDataAbortException data_abort; | ||
| 312 | DebugInfoAlignmentFaultException alignment_fault; | ||
| 313 | DebugInfoBreakPointException break_point; | ||
| 314 | DebugInfoUserBreakException user_break; | ||
| 315 | DebugInfoDebuggerBreakException debugger_break; | ||
| 316 | DebugInfoUndefinedSystemCallException undefined_system_call; | ||
| 317 | u64 raw; | ||
| 318 | }; | ||
| 319 | |||
| 320 | struct DebugInfoException { | ||
| 321 | DebugException type; | ||
| 322 | u64 address; | ||
| 323 | DebugInfoSpecificException specific; | ||
| 324 | }; | ||
| 325 | |||
| 326 | union DebugInfo { | ||
| 327 | DebugInfoCreateProcess create_process; | ||
| 328 | DebugInfoCreateThread create_thread; | ||
| 329 | DebugInfoExitProcess exit_process; | ||
| 330 | DebugInfoExitThread exit_thread; | ||
| 331 | DebugInfoException exception; | ||
| 332 | }; | ||
| 333 | |||
| 334 | struct DebugEventInfo { | ||
| 335 | DebugEvent type; | ||
| 336 | u32 flags; | ||
| 337 | u64 thread_id; | ||
| 338 | DebugInfo info; | ||
| 339 | }; | ||
| 340 | static_assert(sizeof(DebugEventInfo) >= 0x40); | ||
| 341 | |||
| 342 | struct SecureMonitorArguments { | ||
| 343 | std::array<u64, 8> r; | ||
| 344 | }; | ||
| 345 | static_assert(sizeof(SecureMonitorArguments) == 0x40); | ||
| 346 | } // namespace lp64 | ||
| 347 | |||
| 348 | namespace ilp32 { | ||
| 349 | struct LastThreadContext { | ||
| 350 | u32 fp; | ||
| 351 | u32 sp; | ||
| 352 | u32 lr; | ||
| 353 | u32 pc; | ||
| 354 | }; | ||
| 355 | |||
| 356 | struct PhysicalMemoryInfo { | ||
| 357 | PAddr physical_address; | ||
| 358 | u32 virtual_address; | ||
| 359 | u32 size; | ||
| 360 | }; | ||
| 361 | |||
| 362 | struct DebugInfoCreateProcess { | ||
| 363 | u64 program_id; | ||
| 364 | u64 process_id; | ||
| 365 | std::array<char, 0xC> name; | ||
| 366 | u32 flags; | ||
| 367 | u32 user_exception_context_address; // 5.0.0+ | ||
| 368 | }; | ||
| 369 | |||
| 370 | struct DebugInfoCreateThread { | ||
| 371 | u64 thread_id; | ||
| 372 | u32 tls_address; | ||
| 373 | // Removed in 11.0.0 u32 entrypoint; | ||
| 374 | }; | ||
| 375 | |||
| 376 | struct DebugInfoExitProcess { | ||
| 377 | ProcessExitReason reason; | ||
| 378 | }; | ||
| 379 | |||
| 380 | struct DebugInfoExitThread { | ||
| 381 | ThreadExitReason reason; | ||
| 382 | }; | ||
| 383 | |||
| 384 | struct DebugInfoUndefinedInstructionException { | ||
| 385 | u32 insn; | ||
| 386 | }; | ||
| 387 | |||
| 388 | struct DebugInfoDataAbortException { | ||
| 389 | u32 address; | ||
| 390 | }; | ||
| 391 | |||
| 392 | struct DebugInfoAlignmentFaultException { | ||
| 393 | u32 address; | ||
| 394 | }; | ||
| 395 | |||
| 396 | struct DebugInfoBreakPointException { | ||
| 397 | BreakPointType type; | ||
| 398 | u32 address; | ||
| 399 | }; | ||
| 400 | |||
| 401 | struct DebugInfoUserBreakException { | ||
| 402 | BreakReason break_reason; | ||
| 403 | u32 address; | ||
| 404 | u32 size; | ||
| 405 | }; | ||
| 406 | |||
| 407 | struct DebugInfoDebuggerBreakException { | ||
| 408 | std::array<u64, 4> active_thread_ids; | ||
| 409 | }; | ||
| 410 | |||
| 411 | struct DebugInfoUndefinedSystemCallException { | ||
| 412 | u32 id; | ||
| 413 | }; | ||
| 414 | |||
| 415 | union DebugInfoSpecificException { | ||
| 416 | DebugInfoUndefinedInstructionException undefined_instruction; | ||
| 417 | DebugInfoDataAbortException data_abort; | ||
| 418 | DebugInfoAlignmentFaultException alignment_fault; | ||
| 419 | DebugInfoBreakPointException break_point; | ||
| 420 | DebugInfoUserBreakException user_break; | ||
| 421 | DebugInfoDebuggerBreakException debugger_break; | ||
| 422 | DebugInfoUndefinedSystemCallException undefined_system_call; | ||
| 423 | u64 raw; | ||
| 424 | }; | ||
| 425 | |||
| 426 | struct DebugInfoException { | ||
| 427 | DebugException type; | ||
| 428 | u32 address; | ||
| 429 | DebugInfoSpecificException specific; | ||
| 430 | }; | ||
| 431 | |||
| 432 | union DebugInfo { | ||
| 433 | DebugInfoCreateProcess create_process; | ||
| 434 | DebugInfoCreateThread create_thread; | ||
| 435 | DebugInfoExitProcess exit_process; | ||
| 436 | DebugInfoExitThread exit_thread; | ||
| 437 | DebugInfoException exception; | ||
| 438 | }; | ||
| 439 | |||
| 440 | struct DebugEventInfo { | ||
| 441 | DebugEvent type; | ||
| 442 | u32 flags; | ||
| 443 | u64 thread_id; | ||
| 444 | DebugInfo info; | ||
| 445 | }; | ||
| 446 | |||
| 447 | struct SecureMonitorArguments { | ||
| 448 | std::array<u32, 8> r; | ||
| 449 | }; | ||
| 450 | static_assert(sizeof(SecureMonitorArguments) == 0x20); | ||
| 451 | } // namespace ilp32 | ||
| 452 | |||
| 453 | struct ThreadContext { | ||
| 454 | std::array<u64, 29> r; | ||
| 455 | u64 fp; | ||
| 456 | u64 lr; | ||
| 457 | u64 sp; | ||
| 458 | u64 pc; | ||
| 459 | u32 pstate; | ||
| 460 | u32 padding; | ||
| 461 | std::array<u128, 32> v; | ||
| 462 | u32 fpcr; | ||
| 463 | u32 fpsr; | ||
| 464 | u64 tpidr; | ||
| 465 | }; | ||
| 466 | static_assert(sizeof(ThreadContext) == 0x320); | ||
| 467 | |||
| 468 | struct MemoryInfo { | ||
| 469 | u64 base_address; | ||
| 470 | u64 size; | ||
| 471 | MemoryState state; | ||
| 472 | MemoryAttribute attribute; | ||
| 473 | MemoryPermission permission; | ||
| 474 | u32 ipc_count; | ||
| 475 | u32 device_count; | ||
| 476 | u32 padding; | ||
| 477 | }; | ||
| 478 | |||
| 479 | enum class LimitableResource : u32 { | ||
| 480 | PhysicalMemoryMax = 0, | ||
| 481 | ThreadCountMax = 1, | ||
| 482 | EventCountMax = 2, | ||
| 483 | TransferMemoryCountMax = 3, | ||
| 484 | SessionCountMax = 4, | ||
| 485 | Count, | ||
| 486 | }; | ||
| 487 | |||
| 488 | enum class IoPoolType : u32 { | ||
| 489 | // Not supported. | ||
| 490 | Count = 0, | ||
| 491 | }; | ||
| 492 | |||
| 493 | enum class MemoryMapping : u32 { | ||
| 494 | IoRegister = 0, | ||
| 495 | Uncached = 1, | ||
| 496 | Memory = 2, | ||
| 497 | }; | ||
| 498 | |||
| 499 | enum class KernelDebugType : u32 { | ||
| 500 | Thread = 0, | ||
| 501 | ThreadCallStack = 1, | ||
| 502 | KernelObject = 2, | ||
| 503 | Handle_ = 3, | ||
| 504 | Memory = 4, | ||
| 505 | PageTable = 5, | ||
| 506 | CpuUtilization = 6, | ||
| 507 | Process = 7, | ||
| 508 | SuspendProcess = 8, | ||
| 509 | ResumeProcess = 9, | ||
| 510 | Port = 10, | ||
| 511 | }; | ||
| 512 | |||
| 513 | enum class KernelTraceState : u32 { | ||
| 514 | Disabled = 0, | ||
| 515 | Enabled = 1, | ||
| 516 | }; | ||
| 517 | |||
| 518 | enum class CodeMemoryOperation : u32 { | ||
| 519 | Map = 0, | ||
| 520 | MapToOwner = 1, | ||
| 521 | Unmap = 2, | ||
| 522 | UnmapFromOwner = 3, | ||
| 523 | }; | ||
| 524 | |||
| 525 | enum class InterruptType : u32 { | ||
| 526 | Edge = 0, | ||
| 527 | Level = 1, | ||
| 528 | }; | ||
| 529 | |||
| 530 | enum class DeviceName { | ||
| 531 | Afi = 0, | ||
| 532 | Avpc = 1, | ||
| 533 | Dc = 2, | ||
| 534 | Dcb = 3, | ||
| 535 | Hc = 4, | ||
| 536 | Hda = 5, | ||
| 537 | Isp2 = 6, | ||
| 538 | MsencNvenc = 7, | ||
| 539 | Nv = 8, | ||
| 540 | Nv2 = 9, | ||
| 541 | Ppcs = 10, | ||
| 542 | Sata = 11, | ||
| 543 | Vi = 12, | ||
| 544 | Vic = 13, | ||
| 545 | XusbHost = 14, | ||
| 546 | XusbDev = 15, | ||
| 547 | Tsec = 16, | ||
| 548 | Ppcs1 = 17, | ||
| 549 | Dc1 = 18, | ||
| 550 | Sdmmc1a = 19, | ||
| 551 | Sdmmc2a = 20, | ||
| 552 | Sdmmc3a = 21, | ||
| 553 | Sdmmc4a = 22, | ||
| 554 | Isp2b = 23, | ||
| 555 | Gpu = 24, | ||
| 556 | Gpub = 25, | ||
| 557 | Ppcs2 = 26, | ||
| 558 | Nvdec = 27, | ||
| 559 | Ape = 28, | ||
| 560 | Se = 29, | ||
| 561 | Nvjpg = 30, | ||
| 562 | Hc1 = 31, | ||
| 563 | Se1 = 32, | ||
| 564 | Axiap = 33, | ||
| 565 | Etr = 34, | ||
| 566 | Tsecb = 35, | ||
| 567 | Tsec1 = 36, | ||
| 568 | Tsecb1 = 37, | ||
| 569 | Nvdec1 = 38, | ||
| 570 | Count, | ||
| 571 | }; | ||
| 572 | |||
| 573 | enum class SystemInfoType : u32 { | ||
| 574 | TotalPhysicalMemorySize = 0, | ||
| 575 | UsedPhysicalMemorySize = 1, | ||
| 576 | InitialProcessIdRange = 2, | ||
| 577 | }; | ||
| 578 | |||
| 579 | enum class ProcessInfoType : u32 { | ||
| 580 | ProcessState = 0, | ||
| 581 | }; | ||
| 582 | |||
| 583 | struct CreateProcessParameter { | ||
| 584 | std::array<char, 12> name; | ||
| 585 | u32 version; | ||
| 586 | u64 program_id; | ||
| 587 | u64 code_address; | ||
| 588 | s32 code_num_pages; | ||
| 589 | u32 flags; | ||
| 590 | Handle reslimit; | ||
| 591 | s32 system_resource_num_pages; | ||
| 592 | }; | ||
| 593 | static_assert(sizeof(CreateProcessParameter) == 0x30); | ||
| 594 | |||
| 149 | } // namespace Kernel::Svc | 595 | } // namespace Kernel::Svc |
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index 272c54cf7..3730937fe 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h | |||
| @@ -722,4 +722,12 @@ void SvcWrap32(Core::System& system) { | |||
| 722 | FuncReturn(system, retval); | 722 | FuncReturn(system, retval); |
| 723 | } | 723 | } |
| 724 | 724 | ||
| 725 | // Used by Invalidate/Store/FlushProcessDataCache32 | ||
| 726 | template <Result func(Core::System&, Handle, u64, u64)> | ||
| 727 | void SvcWrap32(Core::System& system) { | ||
| 728 | const u64 address = (Param(system, 3) << 32) | Param(system, 2); | ||
| 729 | const u64 size = (Param(system, 4) << 32) | Param(system, 1); | ||
| 730 | FuncReturn32(system, func(system, Param32(system, 0), address, size).raw); | ||
| 731 | } | ||
| 732 | |||
| 725 | } // namespace Kernel | 733 | } // namespace Kernel |
diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 56c990728..240f06689 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h | |||
| @@ -28,30 +28,49 @@ enum class ErrorModule : u32 { | |||
| 28 | Loader = 9, | 28 | Loader = 9, |
| 29 | CMIF = 10, | 29 | CMIF = 10, |
| 30 | HIPC = 11, | 30 | HIPC = 11, |
| 31 | TMA = 12, | ||
| 32 | DMNT = 13, | ||
| 33 | GDS = 14, | ||
| 31 | PM = 15, | 34 | PM = 15, |
| 32 | NS = 16, | 35 | NS = 16, |
| 36 | BSDSockets = 17, | ||
| 33 | HTC = 18, | 37 | HTC = 18, |
| 38 | TSC = 19, | ||
| 34 | NCMContent = 20, | 39 | NCMContent = 20, |
| 35 | SM = 21, | 40 | SM = 21, |
| 36 | RO = 22, | 41 | RO = 22, |
| 42 | GC = 23, | ||
| 37 | SDMMC = 24, | 43 | SDMMC = 24, |
| 38 | OVLN = 25, | 44 | OVLN = 25, |
| 39 | SPL = 26, | 45 | SPL = 26, |
| 46 | Socket = 27, | ||
| 47 | HTCLOW = 29, | ||
| 48 | DDSF = 30, | ||
| 49 | HTCFS = 31, | ||
| 50 | Async = 32, | ||
| 51 | Util = 33, | ||
| 52 | TIPC = 35, | ||
| 53 | ANIF = 37, | ||
| 40 | ETHC = 100, | 54 | ETHC = 100, |
| 41 | I2C = 101, | 55 | I2C = 101, |
| 42 | GPIO = 102, | 56 | GPIO = 102, |
| 43 | UART = 103, | 57 | UART = 103, |
| 58 | CPAD = 104, | ||
| 44 | Settings = 105, | 59 | Settings = 105, |
| 60 | FTM = 106, | ||
| 45 | WLAN = 107, | 61 | WLAN = 107, |
| 46 | XCD = 108, | 62 | XCD = 108, |
| 63 | TMP451 = 109, | ||
| 47 | NIFM = 110, | 64 | NIFM = 110, |
| 48 | Hwopus = 111, | 65 | Hwopus = 111, |
| 66 | LSM6DS3 = 112, | ||
| 49 | Bluetooth = 113, | 67 | Bluetooth = 113, |
| 50 | VI = 114, | 68 | VI = 114, |
| 51 | NFP = 115, | 69 | NFP = 115, |
| 52 | Time = 116, | 70 | Time = 116, |
| 53 | FGM = 117, | 71 | FGM = 117, |
| 54 | OE = 118, | 72 | OE = 118, |
| 73 | BH1730FVC = 119, | ||
| 55 | PCIe = 120, | 74 | PCIe = 120, |
| 56 | Friends = 121, | 75 | Friends = 121, |
| 57 | BCAT = 122, | 76 | BCAT = 122, |
| @@ -65,7 +84,7 @@ enum class ErrorModule : u32 { | |||
| 65 | AHID = 130, | 84 | AHID = 130, |
| 66 | Qlaunch = 132, | 85 | Qlaunch = 132, |
| 67 | PCV = 133, | 86 | PCV = 133, |
| 68 | OMM = 134, | 87 | USBPD = 134, |
| 69 | BPC = 135, | 88 | BPC = 135, |
| 70 | PSM = 136, | 89 | PSM = 136, |
| 71 | NIM = 137, | 90 | NIM = 137, |
| @@ -75,18 +94,22 @@ enum class ErrorModule : u32 { | |||
| 75 | NSD = 141, | 94 | NSD = 141, |
| 76 | PCTL = 142, | 95 | PCTL = 142, |
| 77 | BTM = 143, | 96 | BTM = 143, |
| 97 | LA = 144, | ||
| 78 | ETicket = 145, | 98 | ETicket = 145, |
| 79 | NGC = 146, | 99 | NGC = 146, |
| 80 | ERPT = 147, | 100 | ERPT = 147, |
| 81 | APM = 148, | 101 | APM = 148, |
| 102 | CEC = 149, | ||
| 82 | Profiler = 150, | 103 | Profiler = 150, |
| 83 | ErrorUpload = 151, | 104 | ErrorUpload = 151, |
| 105 | LIDBE = 152, | ||
| 84 | Audio = 153, | 106 | Audio = 153, |
| 85 | NPNS = 154, | 107 | NPNS = 154, |
| 86 | NPNSHTTPSTREAM = 155, | 108 | NPNSHTTPSTREAM = 155, |
| 87 | ARP = 157, | 109 | ARP = 157, |
| 88 | SWKBD = 158, | 110 | SWKBD = 158, |
| 89 | BOOT = 159, | 111 | BOOT = 159, |
| 112 | NetDiag = 160, | ||
| 90 | NFCMifare = 161, | 113 | NFCMifare = 161, |
| 91 | UserlandAssert = 162, | 114 | UserlandAssert = 162, |
| 92 | Fatal = 163, | 115 | Fatal = 163, |
| @@ -94,17 +117,68 @@ enum class ErrorModule : u32 { | |||
| 94 | SPSM = 165, | 117 | SPSM = 165, |
| 95 | BGTC = 167, | 118 | BGTC = 167, |
| 96 | UserlandCrash = 168, | 119 | UserlandCrash = 168, |
| 120 | SASBUS = 169, | ||
| 121 | PI = 170, | ||
| 122 | AudioCtrl = 172, | ||
| 123 | LBL = 173, | ||
| 124 | JIT = 175, | ||
| 125 | HDCP = 176, | ||
| 126 | OMM = 177, | ||
| 127 | PDM = 178, | ||
| 128 | OLSC = 179, | ||
| 97 | SREPO = 180, | 129 | SREPO = 180, |
| 98 | Dauth = 181, | 130 | Dauth = 181, |
| 131 | STDFU = 182, | ||
| 132 | DBG = 183, | ||
| 133 | DHCPS = 186, | ||
| 134 | SPI = 187, | ||
| 135 | AVM = 188, | ||
| 136 | PWM = 189, | ||
| 137 | RTC = 191, | ||
| 138 | Regulator = 192, | ||
| 139 | LED = 193, | ||
| 140 | SIO = 195, | ||
| 141 | PCM = 196, | ||
| 142 | CLKRST = 197, | ||
| 143 | POWCTL = 198, | ||
| 144 | AudioOld = 201, | ||
| 99 | HID = 202, | 145 | HID = 202, |
| 100 | LDN = 203, | 146 | LDN = 203, |
| 147 | CS = 204, | ||
| 101 | Irsensor = 205, | 148 | Irsensor = 205, |
| 102 | Capture = 206, | 149 | Capture = 206, |
| 103 | Manu = 208, | 150 | Manu = 208, |
| 104 | ATK = 209, | 151 | ATK = 209, |
| 152 | WEB = 210, | ||
| 153 | LCS = 211, | ||
| 105 | GRC = 212, | 154 | GRC = 212, |
| 155 | Repair = 213, | ||
| 156 | Album = 214, | ||
| 157 | RID = 215, | ||
| 106 | Migration = 216, | 158 | Migration = 216, |
| 107 | MigrationLdcServ = 217, | 159 | MigrationLdcServ = 217, |
| 160 | HIDBUS = 218, | ||
| 161 | ENS = 219, | ||
| 162 | WebSocket = 223, | ||
| 163 | DCDMTP = 227, | ||
| 164 | PGL = 228, | ||
| 165 | Notification = 229, | ||
| 166 | INS = 230, | ||
| 167 | LP2P = 231, | ||
| 168 | RCD = 232, | ||
| 169 | LCM40607 = 233, | ||
| 170 | PRC = 235, | ||
| 171 | TMAHTC = 237, | ||
| 172 | ECTX = 238, | ||
| 173 | MNPP = 239, | ||
| 174 | HSHL = 240, | ||
| 175 | CAPMTP = 242, | ||
| 176 | DP2HDMI = 244, | ||
| 177 | Cradle = 245, | ||
| 178 | SProfile = 246, | ||
| 179 | NDRM = 250, | ||
| 180 | TSPM = 499, | ||
| 181 | DevMenu = 500, | ||
| 108 | GeneralWebApplet = 800, | 182 | GeneralWebApplet = 800, |
| 109 | WifiWebAuthApplet = 809, | 183 | WifiWebAuthApplet = 809, |
| 110 | WhitelistedApplet = 810, | 184 | WhitelistedApplet = 810, |
diff --git a/src/core/hle/service/am/applets/applet_error.cpp b/src/core/hle/service/am/applets/applet_error.cpp index fcf34bf7e..bae0d99a6 100644 --- a/src/core/hle/service/am/applets/applet_error.cpp +++ b/src/core/hle/service/am/applets/applet_error.cpp | |||
| @@ -144,6 +144,7 @@ void Error::Initialize() { | |||
| 144 | break; | 144 | break; |
| 145 | default: | 145 | default: |
| 146 | UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", mode); | 146 | UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", mode); |
| 147 | break; | ||
| 147 | } | 148 | } |
| 148 | } | 149 | } |
| 149 | 150 | ||
diff --git a/src/core/hle/service/am/applets/applet_general_backend.cpp b/src/core/hle/service/am/applets/applet_general_backend.cpp index c34ef08b3..e50acdaf6 100644 --- a/src/core/hle/service/am/applets/applet_general_backend.cpp +++ b/src/core/hle/service/am/applets/applet_general_backend.cpp | |||
| @@ -129,6 +129,7 @@ void Auth::Execute() { | |||
| 129 | } | 129 | } |
| 130 | default: | 130 | default: |
| 131 | unimplemented_log(); | 131 | unimplemented_log(); |
| 132 | break; | ||
| 132 | } | 133 | } |
| 133 | } | 134 | } |
| 134 | 135 | ||
| @@ -192,6 +193,7 @@ void PhotoViewer::Execute() { | |||
| 192 | break; | 193 | break; |
| 193 | default: | 194 | default: |
| 194 | UNIMPLEMENTED_MSG("Unimplemented PhotoViewer applet mode={:02X}!", mode); | 195 | UNIMPLEMENTED_MSG("Unimplemented PhotoViewer applet mode={:02X}!", mode); |
| 196 | break; | ||
| 195 | } | 197 | } |
| 196 | } | 198 | } |
| 197 | 199 | ||
diff --git a/src/core/hle/service/kernel_helpers.cpp b/src/core/hle/service/kernel_helpers.cpp index af133af93..42991928e 100644 --- a/src/core/hle/service/kernel_helpers.cpp +++ b/src/core/hle/service/kernel_helpers.cpp | |||
| @@ -31,7 +31,7 @@ ServiceContext::~ServiceContext() { | |||
| 31 | Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) { | 31 | Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) { |
| 32 | // Reserve a new event from the process resource limit | 32 | // Reserve a new event from the process resource limit |
| 33 | Kernel::KScopedResourceReservation event_reservation(process, | 33 | Kernel::KScopedResourceReservation event_reservation(process, |
| 34 | Kernel::LimitableResource::Events); | 34 | Kernel::LimitableResource::EventCountMax); |
| 35 | if (!event_reservation.Succeeded()) { | 35 | if (!event_reservation.Succeeded()) { |
| 36 | LOG_CRITICAL(Service, "Resource limit reached!"); | 36 | LOG_CRITICAL(Service, "Resource limit reached!"); |
| 37 | return {}; | 37 | return {}; |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index ced57dfe6..b97813fbc 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp | |||
| @@ -300,11 +300,10 @@ Kernel::KEvent* nvhost_ctrl_gpu::QueryEvent(u32 event_id) { | |||
| 300 | return error_notifier_event; | 300 | return error_notifier_event; |
| 301 | case 2: | 301 | case 2: |
| 302 | return unknown_event; | 302 | return unknown_event; |
| 303 | default: { | 303 | default: |
| 304 | LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id); | 304 | LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id); |
| 305 | return nullptr; | ||
| 305 | } | 306 | } |
| 306 | } | ||
| 307 | return nullptr; | ||
| 308 | } | 307 | } |
| 309 | 308 | ||
| 310 | } // namespace Service::Nvidia::Devices | 309 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 45a759fa8..e123564c6 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp | |||
| @@ -364,11 +364,10 @@ Kernel::KEvent* nvhost_gpu::QueryEvent(u32 event_id) { | |||
| 364 | return sm_exception_breakpoint_pause_report_event; | 364 | return sm_exception_breakpoint_pause_report_event; |
| 365 | case 3: | 365 | case 3: |
| 366 | return error_notifier_event; | 366 | return error_notifier_event; |
| 367 | default: { | 367 | default: |
| 368 | LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id); | 368 | LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id); |
| 369 | return nullptr; | ||
| 369 | } | 370 | } |
| 370 | } | ||
| 371 | return nullptr; | ||
| 372 | } | 371 | } |
| 373 | 372 | ||
| 374 | } // namespace Service::Nvidia::Devices | 373 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.cpp b/src/core/hle/service/nvflinger/buffer_queue_core.cpp index ea4a14ea4..3d1338e66 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_core.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue_core.cpp | |||
| @@ -23,15 +23,17 @@ void BufferQueueCore::NotifyShutdown() { | |||
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | void BufferQueueCore::SignalDequeueCondition() { | 25 | void BufferQueueCore::SignalDequeueCondition() { |
| 26 | dequeue_possible.store(true); | ||
| 26 | dequeue_condition.notify_all(); | 27 | dequeue_condition.notify_all(); |
| 27 | } | 28 | } |
| 28 | 29 | ||
| 29 | bool BufferQueueCore::WaitForDequeueCondition() { | 30 | bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock<std::mutex>& lk) { |
| 30 | if (is_shutting_down) { | 31 | if (is_shutting_down) { |
| 31 | return false; | 32 | return false; |
| 32 | } | 33 | } |
| 33 | 34 | ||
| 34 | dequeue_condition.wait(mutex); | 35 | dequeue_condition.wait(lk, [&] { return dequeue_possible.load(); }); |
| 36 | dequeue_possible.store(false); | ||
| 35 | 37 | ||
| 36 | return true; | 38 | return true; |
| 37 | } | 39 | } |
diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.h b/src/core/hle/service/nvflinger/buffer_queue_core.h index ca6baefaf..85b3bc4c1 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_core.h +++ b/src/core/hle/service/nvflinger/buffer_queue_core.h | |||
| @@ -38,7 +38,7 @@ public: | |||
| 38 | 38 | ||
| 39 | private: | 39 | private: |
| 40 | void SignalDequeueCondition(); | 40 | void SignalDequeueCondition(); |
| 41 | bool WaitForDequeueCondition(); | 41 | bool WaitForDequeueCondition(std::unique_lock<std::mutex>& lk); |
| 42 | 42 | ||
| 43 | s32 GetMinUndequeuedBufferCountLocked(bool async) const; | 43 | s32 GetMinUndequeuedBufferCountLocked(bool async) const; |
| 44 | s32 GetMinMaxBufferCountLocked(bool async) const; | 44 | s32 GetMinMaxBufferCountLocked(bool async) const; |
| @@ -60,7 +60,8 @@ private: | |||
| 60 | BufferQueueDefs::SlotsType slots{}; | 60 | BufferQueueDefs::SlotsType slots{}; |
| 61 | std::vector<BufferItem> queue; | 61 | std::vector<BufferItem> queue; |
| 62 | s32 override_max_buffer_count{}; | 62 | s32 override_max_buffer_count{}; |
| 63 | mutable std::condition_variable_any dequeue_condition; | 63 | std::condition_variable dequeue_condition; |
| 64 | std::atomic<bool> dequeue_possible{}; | ||
| 64 | const bool use_async_buffer{}; // This is always disabled on HOS | 65 | const bool use_async_buffer{}; // This is always disabled on HOS |
| 65 | bool dequeue_buffer_cannot_block{}; | 66 | bool dequeue_buffer_cannot_block{}; |
| 66 | PixelFormat default_buffer_format{PixelFormat::Rgba8888}; | 67 | PixelFormat default_buffer_format{PixelFormat::Rgba8888}; |
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp index 41ba44b21..e601b5da1 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp | |||
| @@ -121,8 +121,8 @@ Status BufferQueueProducer::SetBufferCount(s32 buffer_count) { | |||
| 121 | return Status::NoError; | 121 | return Status::NoError; |
| 122 | } | 122 | } |
| 123 | 123 | ||
| 124 | Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, | 124 | Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags, |
| 125 | Status* return_flags) const { | 125 | std::unique_lock<std::mutex>& lk) const { |
| 126 | bool try_again = true; | 126 | bool try_again = true; |
| 127 | 127 | ||
| 128 | while (try_again) { | 128 | while (try_again) { |
| @@ -214,7 +214,7 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, | |||
| 214 | return Status::WouldBlock; | 214 | return Status::WouldBlock; |
| 215 | } | 215 | } |
| 216 | 216 | ||
| 217 | if (!core->WaitForDequeueCondition()) { | 217 | if (!core->WaitForDequeueCondition(lk)) { |
| 218 | // We are no longer running | 218 | // We are no longer running |
| 219 | return Status::NoError; | 219 | return Status::NoError; |
| 220 | } | 220 | } |
| @@ -237,7 +237,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool | |||
| 237 | Status return_flags = Status::NoError; | 237 | Status return_flags = Status::NoError; |
| 238 | bool attached_by_consumer = false; | 238 | bool attached_by_consumer = false; |
| 239 | { | 239 | { |
| 240 | std::scoped_lock lock{core->mutex}; | 240 | std::unique_lock lock{core->mutex}; |
| 241 | core->WaitWhileAllocatingLocked(); | 241 | core->WaitWhileAllocatingLocked(); |
| 242 | 242 | ||
| 243 | if (format == PixelFormat::NoFormat) { | 243 | if (format == PixelFormat::NoFormat) { |
| @@ -248,7 +248,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool | |||
| 248 | usage |= core->consumer_usage_bit; | 248 | usage |= core->consumer_usage_bit; |
| 249 | 249 | ||
| 250 | s32 found{}; | 250 | s32 found{}; |
| 251 | Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags); | 251 | Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags, lock); |
| 252 | if (status != Status::NoError) { | 252 | if (status != Status::NoError) { |
| 253 | return status; | 253 | return status; |
| 254 | } | 254 | } |
| @@ -400,13 +400,13 @@ Status BufferQueueProducer::AttachBuffer(s32* out_slot, | |||
| 400 | return Status::BadValue; | 400 | return Status::BadValue; |
| 401 | } | 401 | } |
| 402 | 402 | ||
| 403 | std::scoped_lock lock{core->mutex}; | 403 | std::unique_lock lock{core->mutex}; |
| 404 | core->WaitWhileAllocatingLocked(); | 404 | core->WaitWhileAllocatingLocked(); |
| 405 | 405 | ||
| 406 | Status return_flags = Status::NoError; | 406 | Status return_flags = Status::NoError; |
| 407 | s32 found{}; | 407 | s32 found{}; |
| 408 | 408 | ||
| 409 | const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags); | 409 | const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags, lock); |
| 410 | if (status != Status::NoError) { | 410 | if (status != Status::NoError) { |
| 411 | return status; | 411 | return status; |
| 412 | } | 412 | } |
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.h b/src/core/hle/service/nvflinger/buffer_queue_producer.h index 7526bf8ec..1d380480f 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_producer.h +++ b/src/core/hle/service/nvflinger/buffer_queue_producer.h | |||
| @@ -70,7 +70,8 @@ public: | |||
| 70 | private: | 70 | private: |
| 71 | BufferQueueProducer(const BufferQueueProducer&) = delete; | 71 | BufferQueueProducer(const BufferQueueProducer&) = delete; |
| 72 | 72 | ||
| 73 | Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags) const; | 73 | Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags, |
| 74 | std::unique_lock<std::mutex>& lk) const; | ||
| 74 | 75 | ||
| 75 | Kernel::KEvent* buffer_wait_event{}; | 76 | Kernel::KEvent* buffer_wait_event{}; |
| 76 | Service::KernelHelpers::ServiceContext& service_context; | 77 | Service::KernelHelpers::ServiceContext& service_context; |
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 5ab41c0c4..0de67f1e1 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp | |||
| @@ -228,6 +228,7 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session, | |||
| 228 | } | 228 | } |
| 229 | 229 | ||
| 230 | UNIMPLEMENTED_MSG("command_type={}", ctx.GetCommandType()); | 230 | UNIMPLEMENTED_MSG("command_type={}", ctx.GetCommandType()); |
| 231 | break; | ||
| 231 | } | 232 | } |
| 232 | 233 | ||
| 233 | // If emulation was shutdown, we are closing service threads, do not write the response back to | 234 | // If emulation was shutdown, we are closing service threads, do not write the response back to |
diff --git a/src/core/hle/service/sm/sm_controller.cpp b/src/core/hle/service/sm/sm_controller.cpp index 69e0fe808..1cf9dd1c4 100644 --- a/src/core/hle/service/sm/sm_controller.cpp +++ b/src/core/hle/service/sm/sm_controller.cpp | |||
| @@ -34,8 +34,8 @@ void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) { | |||
| 34 | // once this is a proper process | 34 | // once this is a proper process |
| 35 | 35 | ||
| 36 | // Reserve a new session from the process resource limit. | 36 | // Reserve a new session from the process resource limit. |
| 37 | Kernel::KScopedResourceReservation session_reservation(&process, | 37 | Kernel::KScopedResourceReservation session_reservation( |
| 38 | Kernel::LimitableResource::Sessions); | 38 | &process, Kernel::LimitableResource::SessionCountMax); |
| 39 | ASSERT(session_reservation.Succeeded()); | 39 | ASSERT(session_reservation.Succeeded()); |
| 40 | 40 | ||
| 41 | // Create the session. | 41 | // Create the session. |
diff --git a/src/core/hle/service/time/time_zone_manager.cpp b/src/core/hle/service/time/time_zone_manager.cpp index 2aa675df9..f9ada7c93 100644 --- a/src/core/hle/service/time/time_zone_manager.cpp +++ b/src/core/hle/service/time/time_zone_manager.cpp | |||
| @@ -280,6 +280,7 @@ static constexpr int TransitionTime(int year, Rule rule, int offset) { | |||
| 280 | } | 280 | } |
| 281 | default: | 281 | default: |
| 282 | ASSERT(false); | 282 | ASSERT(false); |
| 283 | break; | ||
| 283 | } | 284 | } |
| 284 | return value + rule.transition_time + offset; | 285 | return value + rule.transition_time + offset; |
| 285 | } | 286 | } |
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 3ca80c8ff..3141122f1 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 8 | #include "common/atomic_ops.h" | 8 | #include "common/atomic_ops.h" |
| 9 | #include "common/cache_management.h" | ||
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 10 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 11 | #include "common/page_table.h" | 12 | #include "common/page_table.h" |
| @@ -329,6 +330,55 @@ struct Memory::Impl { | |||
| 329 | }); | 330 | }); |
| 330 | } | 331 | } |
| 331 | 332 | ||
| 333 | template <typename Callback> | ||
| 334 | Result PerformCacheOperation(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size, | ||
| 335 | Callback&& cb) { | ||
| 336 | class InvalidMemoryException : public std::exception {}; | ||
| 337 | |||
| 338 | try { | ||
| 339 | WalkBlock( | ||
| 340 | process, dest_addr, size, | ||
| 341 | [&](const std::size_t block_size, const VAddr current_vaddr) { | ||
| 342 | LOG_ERROR(HW_Memory, "Unmapped cache maintenance @ {:#018X}", current_vaddr); | ||
| 343 | throw InvalidMemoryException(); | ||
| 344 | }, | ||
| 345 | [&](const std::size_t block_size, u8* const host_ptr) { cb(block_size, host_ptr); }, | ||
| 346 | [&](const VAddr current_vaddr, const std::size_t block_size, u8* const host_ptr) { | ||
| 347 | system.GPU().FlushRegion(current_vaddr, block_size); | ||
| 348 | cb(block_size, host_ptr); | ||
| 349 | }, | ||
| 350 | [](const std::size_t block_size) {}); | ||
| 351 | } catch (InvalidMemoryException&) { | ||
| 352 | return Kernel::ResultInvalidCurrentMemory; | ||
| 353 | } | ||
| 354 | |||
| 355 | return ResultSuccess; | ||
| 356 | } | ||
| 357 | |||
| 358 | Result InvalidateDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size) { | ||
| 359 | auto perform = [&](const std::size_t block_size, u8* const host_ptr) { | ||
| 360 | // Do nothing; this operation (dc ivac) cannot be supported | ||
| 361 | // from EL0 | ||
| 362 | }; | ||
| 363 | return PerformCacheOperation(process, dest_addr, size, perform); | ||
| 364 | } | ||
| 365 | |||
| 366 | Result StoreDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size) { | ||
| 367 | auto perform = [&](const std::size_t block_size, u8* const host_ptr) { | ||
| 368 | // dc cvac: Store to point of coherency | ||
| 369 | Common::DataCacheLineCleanByVAToPoC(host_ptr, block_size); | ||
| 370 | }; | ||
| 371 | return PerformCacheOperation(process, dest_addr, size, perform); | ||
| 372 | } | ||
| 373 | |||
| 374 | Result FlushDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size) { | ||
| 375 | auto perform = [&](const std::size_t block_size, u8* const host_ptr) { | ||
| 376 | // dc civac: Store to point of coherency, and invalidate from cache | ||
| 377 | Common::DataCacheLineCleanAndInvalidateByVAToPoC(host_ptr, block_size); | ||
| 378 | }; | ||
| 379 | return PerformCacheOperation(process, dest_addr, size, perform); | ||
| 380 | } | ||
| 381 | |||
| 332 | void MarkRegionDebug(VAddr vaddr, u64 size, bool debug) { | 382 | void MarkRegionDebug(VAddr vaddr, u64 size, bool debug) { |
| 333 | if (vaddr == 0) { | 383 | if (vaddr == 0) { |
| 334 | return; | 384 | return; |
| @@ -786,6 +836,21 @@ void Memory::ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, const s | |||
| 786 | impl->ZeroBlock(process, dest_addr, size); | 836 | impl->ZeroBlock(process, dest_addr, size); |
| 787 | } | 837 | } |
| 788 | 838 | ||
| 839 | Result Memory::InvalidateDataCache(const Kernel::KProcess& process, VAddr dest_addr, | ||
| 840 | const std::size_t size) { | ||
| 841 | return impl->InvalidateDataCache(process, dest_addr, size); | ||
| 842 | } | ||
| 843 | |||
| 844 | Result Memory::StoreDataCache(const Kernel::KProcess& process, VAddr dest_addr, | ||
| 845 | const std::size_t size) { | ||
| 846 | return impl->StoreDataCache(process, dest_addr, size); | ||
| 847 | } | ||
| 848 | |||
| 849 | Result Memory::FlushDataCache(const Kernel::KProcess& process, VAddr dest_addr, | ||
| 850 | const std::size_t size) { | ||
| 851 | return impl->FlushDataCache(process, dest_addr, size); | ||
| 852 | } | ||
| 853 | |||
| 789 | void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) { | 854 | void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) { |
| 790 | impl->RasterizerMarkRegionCached(vaddr, size, cached); | 855 | impl->RasterizerMarkRegionCached(vaddr, size, cached); |
| 791 | } | 856 | } |
diff --git a/src/core/memory.h b/src/core/memory.h index 81eac448b..31fe699d8 100644 --- a/src/core/memory.h +++ b/src/core/memory.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <string> | 8 | #include <string> |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "core/hle/result.h" | ||
| 10 | 11 | ||
| 11 | namespace Common { | 12 | namespace Common { |
| 12 | struct PageTable; | 13 | struct PageTable; |
| @@ -450,6 +451,39 @@ public: | |||
| 450 | void ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size); | 451 | void ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size); |
| 451 | 452 | ||
| 452 | /** | 453 | /** |
| 454 | * Invalidates a range of bytes within the current process' address space at the specified | ||
| 455 | * virtual address. | ||
| 456 | * | ||
| 457 | * @param process The process that will have data invalidated within its address space. | ||
| 458 | * @param dest_addr The destination virtual address to invalidate the data from. | ||
| 459 | * @param size The size of the range to invalidate, in bytes. | ||
| 460 | * | ||
| 461 | */ | ||
| 462 | Result InvalidateDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size); | ||
| 463 | |||
| 464 | /** | ||
| 465 | * Stores a range of bytes within the current process' address space at the specified | ||
| 466 | * virtual address. | ||
| 467 | * | ||
| 468 | * @param process The process that will have data stored within its address space. | ||
| 469 | * @param dest_addr The destination virtual address to store the data from. | ||
| 470 | * @param size The size of the range to store, in bytes. | ||
| 471 | * | ||
| 472 | */ | ||
| 473 | Result StoreDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size); | ||
| 474 | |||
| 475 | /** | ||
| 476 | * Flushes a range of bytes within the current process' address space at the specified | ||
| 477 | * virtual address. | ||
| 478 | * | ||
| 479 | * @param process The process that will have data flushed within its address space. | ||
| 480 | * @param dest_addr The destination virtual address to flush the data from. | ||
| 481 | * @param size The size of the range to flush, in bytes. | ||
| 482 | * | ||
| 483 | */ | ||
| 484 | Result FlushDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size); | ||
| 485 | |||
| 486 | /** | ||
| 453 | * Marks each page within the specified address range as cached or uncached. | 487 | * Marks each page within the specified address range as cached or uncached. |
| 454 | * | 488 | * |
| 455 | * @param vaddr The virtual address indicating the start of the address range. | 489 | * @param vaddr The virtual address indicating the start of the address range. |
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp index 9dbf9888d..f0bd84ab2 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp | |||
| @@ -382,6 +382,18 @@ void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) { | |||
| 382 | ctx.Add("MOV.S {}.x,primitive_invocation.x;", inst); | 382 | ctx.Add("MOV.S {}.x,primitive_invocation.x;", inst); |
| 383 | } | 383 | } |
| 384 | 384 | ||
| 385 | void EmitInvocationInfo(EmitContext& ctx, IR::Inst& inst) { | ||
| 386 | switch (ctx.stage) { | ||
| 387 | case Stage::TessellationControl: | ||
| 388 | case Stage::TessellationEval: | ||
| 389 | ctx.Add("SHL.U {}.x,primitive.vertexcount,16;", inst); | ||
| 390 | break; | ||
| 391 | default: | ||
| 392 | LOG_WARNING(Shader, "(STUBBED) called"); | ||
| 393 | ctx.Add("MOV.S {}.x,0x00ff0000;", inst); | ||
| 394 | } | ||
| 395 | } | ||
| 396 | |||
| 385 | void EmitSampleId(EmitContext& ctx, IR::Inst& inst) { | 397 | void EmitSampleId(EmitContext& ctx, IR::Inst& inst) { |
| 386 | ctx.Add("MOV.S {}.x,fragment.sampleid.x;", inst); | 398 | ctx.Add("MOV.S {}.x,fragment.sampleid.x;", inst); |
| 387 | } | 399 | } |
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h index d645fd532..eaaf9ba39 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h +++ b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h | |||
| @@ -69,6 +69,7 @@ void EmitSetOFlag(EmitContext& ctx); | |||
| 69 | void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst); | 69 | void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst); |
| 70 | void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst); | 70 | void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst); |
| 71 | void EmitInvocationId(EmitContext& ctx, IR::Inst& inst); | 71 | void EmitInvocationId(EmitContext& ctx, IR::Inst& inst); |
| 72 | void EmitInvocationInfo(EmitContext& ctx, IR::Inst& inst); | ||
| 72 | void EmitSampleId(EmitContext& ctx, IR::Inst& inst); | 73 | void EmitSampleId(EmitContext& ctx, IR::Inst& inst); |
| 73 | void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst); | 74 | void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst); |
| 74 | void EmitYDirection(EmitContext& ctx, IR::Inst& inst); | 75 | 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 | |||
| 95 | if (info.uses_invocation_id) { | 95 | if (info.uses_invocation_id) { |
| 96 | Add("ATTRIB primitive_invocation=primitive.invocation;"); | 96 | Add("ATTRIB primitive_invocation=primitive.invocation;"); |
| 97 | } | 97 | } |
| 98 | if (info.uses_invocation_info && | ||
| 99 | (stage == Stage::TessellationControl || stage == Stage::TessellationEval)) { | ||
| 100 | Add("ATTRIB primitive_vertexcount = primitive.vertexcount;"); | ||
| 101 | } | ||
| 98 | if (info.stores_tess_level_outer) { | 102 | if (info.stores_tess_level_outer) { |
| 99 | Add("OUTPUT result_patch_tessouter[]={{result.patch.tessouter[0..3]}};"); | 103 | Add("OUTPUT result_patch_tessouter[]={{result.patch.tessouter[0..3]}};"); |
| 100 | } | 104 | } |
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp index 02b84cbe6..39579cf5d 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp | |||
| @@ -402,6 +402,18 @@ void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) { | |||
| 402 | ctx.AddU32("{}=uint(gl_InvocationID);", inst); | 402 | ctx.AddU32("{}=uint(gl_InvocationID);", inst); |
| 403 | } | 403 | } |
| 404 | 404 | ||
| 405 | void EmitInvocationInfo(EmitContext& ctx, IR::Inst& inst) { | ||
| 406 | switch (ctx.stage) { | ||
| 407 | case Stage::TessellationControl: | ||
| 408 | case Stage::TessellationEval: | ||
| 409 | ctx.AddU32("{}=uint(gl_PatchVerticesIn)<<16;", inst); | ||
| 410 | break; | ||
| 411 | default: | ||
| 412 | LOG_WARNING(Shader, "(STUBBED) called"); | ||
| 413 | ctx.AddU32("{}=uint(0x00ff0000);", inst); | ||
| 414 | } | ||
| 415 | } | ||
| 416 | |||
| 405 | void EmitSampleId(EmitContext& ctx, IR::Inst& inst) { | 417 | void EmitSampleId(EmitContext& ctx, IR::Inst& inst) { |
| 406 | ctx.AddU32("{}=uint(gl_SampleID);", inst); | 418 | ctx.AddU32("{}=uint(gl_SampleID);", inst); |
| 407 | } | 419 | } |
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h index 96e683b5e..4151c89de 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h +++ b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h | |||
| @@ -83,6 +83,7 @@ void EmitSetOFlag(EmitContext& ctx); | |||
| 83 | void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst); | 83 | void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst); |
| 84 | void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst); | 84 | void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst); |
| 85 | void EmitInvocationId(EmitContext& ctx, IR::Inst& inst); | 85 | void EmitInvocationId(EmitContext& ctx, IR::Inst& inst); |
| 86 | void EmitInvocationInfo(EmitContext& ctx, IR::Inst& inst); | ||
| 86 | void EmitSampleId(EmitContext& ctx, IR::Inst& inst); | 87 | void EmitSampleId(EmitContext& ctx, IR::Inst& inst); |
| 87 | void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst); | 88 | void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst); |
| 88 | void EmitYDirection(EmitContext& ctx, IR::Inst& inst); | 89 | 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) { | |||
| 514 | return ctx.OpLoad(ctx.U32[1], ctx.invocation_id); | 514 | return ctx.OpLoad(ctx.U32[1], ctx.invocation_id); |
| 515 | } | 515 | } |
| 516 | 516 | ||
| 517 | Id EmitInvocationInfo(EmitContext& ctx) { | ||
| 518 | switch (ctx.stage) { | ||
| 519 | case Stage::TessellationControl: | ||
| 520 | case Stage::TessellationEval: | ||
| 521 | return ctx.OpShiftLeftLogical(ctx.U32[1], ctx.OpLoad(ctx.U32[1], ctx.patch_vertices_in), | ||
| 522 | ctx.Const(16u)); | ||
| 523 | default: | ||
| 524 | LOG_WARNING(Shader, "(STUBBED) called"); | ||
| 525 | return ctx.Const(0x00ff0000u); | ||
| 526 | } | ||
| 527 | } | ||
| 528 | |||
| 517 | Id EmitSampleId(EmitContext& ctx) { | 529 | Id EmitSampleId(EmitContext& ctx) { |
| 518 | return ctx.OpLoad(ctx.U32[1], ctx.sample_id); | 530 | return ctx.OpLoad(ctx.U32[1], ctx.sample_id); |
| 519 | } | 531 | } |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index 7070c8fda..e31cdc5e8 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h | |||
| @@ -72,6 +72,7 @@ void EmitSetOFlag(EmitContext& ctx); | |||
| 72 | Id EmitWorkgroupId(EmitContext& ctx); | 72 | Id EmitWorkgroupId(EmitContext& ctx); |
| 73 | Id EmitLocalInvocationId(EmitContext& ctx); | 73 | Id EmitLocalInvocationId(EmitContext& ctx); |
| 74 | Id EmitInvocationId(EmitContext& ctx); | 74 | Id EmitInvocationId(EmitContext& ctx); |
| 75 | Id EmitInvocationInfo(EmitContext& ctx); | ||
| 75 | Id EmitSampleId(EmitContext& ctx); | 76 | Id EmitSampleId(EmitContext& ctx); |
| 76 | Id EmitIsHelperInvocation(EmitContext& ctx); | 77 | Id EmitIsHelperInvocation(EmitContext& ctx); |
| 77 | Id EmitYDirection(EmitContext& ctx); | 78 | 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) { | |||
| 1325 | if (info.uses_invocation_id) { | 1325 | if (info.uses_invocation_id) { |
| 1326 | invocation_id = DefineInput(*this, U32[1], false, spv::BuiltIn::InvocationId); | 1326 | invocation_id = DefineInput(*this, U32[1], false, spv::BuiltIn::InvocationId); |
| 1327 | } | 1327 | } |
| 1328 | if (info.uses_invocation_info && | ||
| 1329 | (stage == Shader::Stage::TessellationControl || stage == Shader::Stage::TessellationEval)) { | ||
| 1330 | patch_vertices_in = DefineInput(*this, U32[1], false, spv::BuiltIn::PatchVertices); | ||
| 1331 | } | ||
| 1328 | if (info.uses_sample_id) { | 1332 | if (info.uses_sample_id) { |
| 1329 | sample_id = DefineInput(*this, U32[1], false, spv::BuiltIn::SampleId); | 1333 | sample_id = DefineInput(*this, U32[1], false, spv::BuiltIn::SampleId); |
| 1330 | } | 1334 | } |
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index c86e50911..dde45b4bc 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h | |||
| @@ -204,6 +204,7 @@ public: | |||
| 204 | Id workgroup_id{}; | 204 | Id workgroup_id{}; |
| 205 | Id local_invocation_id{}; | 205 | Id local_invocation_id{}; |
| 206 | Id invocation_id{}; | 206 | Id invocation_id{}; |
| 207 | Id patch_vertices_in{}; | ||
| 207 | Id sample_id{}; | 208 | Id sample_id{}; |
| 208 | Id is_helper_invocation{}; | 209 | Id is_helper_invocation{}; |
| 209 | Id subgroup_local_invocation_id{}; | 210 | Id subgroup_local_invocation_id{}; |
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp index d4425f06d..0cdac0eff 100644 --- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp +++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp | |||
| @@ -362,6 +362,10 @@ U32 IREmitter::InvocationId() { | |||
| 362 | return Inst<U32>(Opcode::InvocationId); | 362 | return Inst<U32>(Opcode::InvocationId); |
| 363 | } | 363 | } |
| 364 | 364 | ||
| 365 | U32 IREmitter::InvocationInfo() { | ||
| 366 | return Inst<U32>(Opcode::InvocationInfo); | ||
| 367 | } | ||
| 368 | |||
| 365 | U32 IREmitter::SampleId() { | 369 | U32 IREmitter::SampleId() { |
| 366 | return Inst<U32>(Opcode::SampleId); | 370 | return Inst<U32>(Opcode::SampleId); |
| 367 | } | 371 | } |
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h index f163c18d9..2df992feb 100644 --- a/src/shader_recompiler/frontend/ir/ir_emitter.h +++ b/src/shader_recompiler/frontend/ir/ir_emitter.h | |||
| @@ -97,6 +97,7 @@ public: | |||
| 97 | [[nodiscard]] U32 LocalInvocationIdZ(); | 97 | [[nodiscard]] U32 LocalInvocationIdZ(); |
| 98 | 98 | ||
| 99 | [[nodiscard]] U32 InvocationId(); | 99 | [[nodiscard]] U32 InvocationId(); |
| 100 | [[nodiscard]] U32 InvocationInfo(); | ||
| 100 | [[nodiscard]] U32 SampleId(); | 101 | [[nodiscard]] U32 SampleId(); |
| 101 | [[nodiscard]] U1 IsHelperInvocation(); | 102 | [[nodiscard]] U1 IsHelperInvocation(); |
| 102 | [[nodiscard]] F32 YDirection(); | 103 | [[nodiscard]] F32 YDirection(); |
diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc index 88aa077ee..1fe3749cc 100644 --- a/src/shader_recompiler/frontend/ir/opcodes.inc +++ b/src/shader_recompiler/frontend/ir/opcodes.inc | |||
| @@ -59,6 +59,7 @@ OPCODE(SetOFlag, Void, U1, | |||
| 59 | OPCODE(WorkgroupId, U32x3, ) | 59 | OPCODE(WorkgroupId, U32x3, ) |
| 60 | OPCODE(LocalInvocationId, U32x3, ) | 60 | OPCODE(LocalInvocationId, U32x3, ) |
| 61 | OPCODE(InvocationId, U32, ) | 61 | OPCODE(InvocationId, U32, ) |
| 62 | OPCODE(InvocationInfo, U32, ) | ||
| 62 | OPCODE(SampleId, U32, ) | 63 | OPCODE(SampleId, U32, ) |
| 63 | OPCODE(IsHelperInvocation, U1, ) | 64 | OPCODE(IsHelperInvocation, U1, ) |
| 64 | OPCODE(YDirection, F32, ) | 65 | 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 { | |||
| 14 | TessellationLodBottom, | 14 | TessellationLodBottom, |
| 15 | TessellationLodInteriorU, | 15 | TessellationLodInteriorU, |
| 16 | TessellationLodInteriorV, | 16 | TessellationLodInteriorV, |
| 17 | ComponentPadding0, | ||
| 18 | ComponentPadding1, | ||
| 19 | Component0, | 17 | Component0, |
| 20 | Component1, | 18 | Component1, |
| 21 | Component2, | 19 | Component2, |
| @@ -137,7 +135,7 @@ enum class Patch : u64 { | |||
| 137 | Component118, | 135 | Component118, |
| 138 | Component119, | 136 | Component119, |
| 139 | }; | 137 | }; |
| 140 | static_assert(static_cast<u64>(Patch::Component119) == 127); | 138 | static_assert(static_cast<u64>(Patch::Component119) == 125); |
| 141 | 139 | ||
| 142 | [[nodiscard]] bool IsGeneric(Patch patch) noexcept; | 140 | [[nodiscard]] bool IsGeneric(Patch patch) noexcept; |
| 143 | 141 | ||
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp index 52be12f9c..753c62098 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp | |||
| @@ -117,8 +117,7 @@ enum class SpecialRegister : u64 { | |||
| 117 | case SpecialRegister::SR_THREAD_KILL: | 117 | case SpecialRegister::SR_THREAD_KILL: |
| 118 | return IR::U32{ir.Select(ir.IsHelperInvocation(), ir.Imm32(-1), ir.Imm32(0))}; | 118 | return IR::U32{ir.Select(ir.IsHelperInvocation(), ir.Imm32(-1), ir.Imm32(0))}; |
| 119 | case SpecialRegister::SR_INVOCATION_INFO: | 119 | case SpecialRegister::SR_INVOCATION_INFO: |
| 120 | LOG_WARNING(Shader, "(STUBBED) SR_INVOCATION_INFO"); | 120 | return ir.InvocationInfo(); |
| 121 | return ir.Imm32(0x00ff'0000); | ||
| 122 | case SpecialRegister::SR_TID: { | 121 | case SpecialRegister::SR_TID: { |
| 123 | const IR::Value tid{ir.LocalInvocationId()}; | 122 | const IR::Value tid{ir.LocalInvocationId()}; |
| 124 | return ir.BitFieldInsert(ir.BitFieldInsert(IR::U32{ir.CompositeExtract(tid, 0)}, | 123 | return ir.BitFieldInsert(ir.BitFieldInsert(IR::U32{ir.CompositeExtract(tid, 0)}, |
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp index b7162f719..376aae0ea 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp | |||
| @@ -223,7 +223,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo | |||
| 223 | Optimization::PositionPass(env, program); | 223 | Optimization::PositionPass(env, program); |
| 224 | 224 | ||
| 225 | Optimization::GlobalMemoryToStorageBufferPass(program); | 225 | Optimization::GlobalMemoryToStorageBufferPass(program); |
| 226 | Optimization::TexturePass(env, program); | 226 | Optimization::TexturePass(env, program, host_info); |
| 227 | 227 | ||
| 228 | if (Settings::values.resolution_info.active) { | 228 | if (Settings::values.resolution_info.active) { |
| 229 | Optimization::RescalingPass(program); | 229 | Optimization::RescalingPass(program); |
diff --git a/src/shader_recompiler/host_translate_info.h b/src/shader_recompiler/host_translate_info.h index 881874310..cc1500690 100644 --- a/src/shader_recompiler/host_translate_info.h +++ b/src/shader_recompiler/host_translate_info.h | |||
| @@ -13,6 +13,7 @@ struct HostTranslateInfo { | |||
| 13 | bool support_float16{}; ///< True when the device supports 16-bit floats | 13 | bool support_float16{}; ///< True when the device supports 16-bit floats |
| 14 | bool support_int64{}; ///< True when the device supports 64-bit integers | 14 | bool support_int64{}; ///< True when the device supports 64-bit integers |
| 15 | bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered | 15 | bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered |
| 16 | bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers | ||
| 16 | }; | 17 | }; |
| 17 | 18 | ||
| 18 | } // namespace Shader | 19 | } // namespace Shader |
diff --git a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp index 7cff8ecdc..5a4195217 100644 --- a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp +++ b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp | |||
| @@ -468,6 +468,9 @@ void VisitUsages(Info& info, IR::Inst& inst) { | |||
| 468 | case IR::Opcode::InvocationId: | 468 | case IR::Opcode::InvocationId: |
| 469 | info.uses_invocation_id = true; | 469 | info.uses_invocation_id = true; |
| 470 | break; | 470 | break; |
| 471 | case IR::Opcode::InvocationInfo: | ||
| 472 | info.uses_invocation_info = true; | ||
| 473 | break; | ||
| 471 | case IR::Opcode::SampleId: | 474 | case IR::Opcode::SampleId: |
| 472 | info.uses_sample_id = true; | 475 | info.uses_sample_id = true; |
| 473 | break; | 476 | break; |
diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h index 24f609d69..586a0668f 100644 --- a/src/shader_recompiler/ir_opt/passes.h +++ b/src/shader_recompiler/ir_opt/passes.h | |||
| @@ -6,6 +6,10 @@ | |||
| 6 | #include "shader_recompiler/environment.h" | 6 | #include "shader_recompiler/environment.h" |
| 7 | #include "shader_recompiler/frontend/ir/program.h" | 7 | #include "shader_recompiler/frontend/ir/program.h" |
| 8 | 8 | ||
| 9 | namespace Shader { | ||
| 10 | struct HostTranslateInfo; | ||
| 11 | } | ||
| 12 | |||
| 9 | namespace Shader::Optimization { | 13 | namespace Shader::Optimization { |
| 10 | 14 | ||
| 11 | void CollectShaderInfoPass(Environment& env, IR::Program& program); | 15 | void CollectShaderInfoPass(Environment& env, IR::Program& program); |
| @@ -18,7 +22,7 @@ void LowerInt64ToInt32(IR::Program& program); | |||
| 18 | void RescalingPass(IR::Program& program); | 22 | void RescalingPass(IR::Program& program); |
| 19 | void SsaRewritePass(IR::Program& program); | 23 | void SsaRewritePass(IR::Program& program); |
| 20 | void PositionPass(Environment& env, IR::Program& program); | 24 | void PositionPass(Environment& env, IR::Program& program); |
| 21 | void TexturePass(Environment& env, IR::Program& program); | 25 | void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo& host_info); |
| 22 | void VerificationPass(const IR::Program& program); | 26 | void VerificationPass(const IR::Program& program); |
| 23 | 27 | ||
| 24 | // Dual Vertex | 28 | // Dual Vertex |
diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp index 9eff84a3d..f5c86fcb1 100644 --- a/src/shader_recompiler/ir_opt/texture_pass.cpp +++ b/src/shader_recompiler/ir_opt/texture_pass.cpp | |||
| @@ -7,11 +7,11 @@ | |||
| 7 | 7 | ||
| 8 | #include <boost/container/small_vector.hpp> | 8 | #include <boost/container/small_vector.hpp> |
| 9 | 9 | ||
| 10 | #include "common/settings.h" | ||
| 11 | #include "shader_recompiler/environment.h" | 10 | #include "shader_recompiler/environment.h" |
| 12 | #include "shader_recompiler/frontend/ir/basic_block.h" | 11 | #include "shader_recompiler/frontend/ir/basic_block.h" |
| 13 | #include "shader_recompiler/frontend/ir/breadth_first_search.h" | 12 | #include "shader_recompiler/frontend/ir/breadth_first_search.h" |
| 14 | #include "shader_recompiler/frontend/ir/ir_emitter.h" | 13 | #include "shader_recompiler/frontend/ir/ir_emitter.h" |
| 14 | #include "shader_recompiler/host_translate_info.h" | ||
| 15 | #include "shader_recompiler/ir_opt/passes.h" | 15 | #include "shader_recompiler/ir_opt/passes.h" |
| 16 | #include "shader_recompiler/shader_info.h" | 16 | #include "shader_recompiler/shader_info.h" |
| 17 | 17 | ||
| @@ -461,7 +461,7 @@ void PatchImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) { | |||
| 461 | ir.FPRecip(ir.ConvertUToF(32, 32, ir.CompositeExtract(texture_size, 1)))))); | 461 | ir.FPRecip(ir.ConvertUToF(32, 32, ir.CompositeExtract(texture_size, 1)))))); |
| 462 | } | 462 | } |
| 463 | 463 | ||
| 464 | void PathTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_format) { | 464 | void PatchTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_format) { |
| 465 | const auto it{IR::Block::InstructionList::s_iterator_to(inst)}; | 465 | const auto it{IR::Block::InstructionList::s_iterator_to(inst)}; |
| 466 | IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; | 466 | IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; |
| 467 | auto get_max_value = [pixel_format]() -> float { | 467 | auto get_max_value = [pixel_format]() -> float { |
| @@ -494,7 +494,7 @@ void PathTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_f | |||
| 494 | } | 494 | } |
| 495 | } // Anonymous namespace | 495 | } // Anonymous namespace |
| 496 | 496 | ||
| 497 | void TexturePass(Environment& env, IR::Program& program) { | 497 | void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo& host_info) { |
| 498 | TextureInstVector to_replace; | 498 | TextureInstVector to_replace; |
| 499 | for (IR::Block* const block : program.post_order_blocks) { | 499 | for (IR::Block* const block : program.post_order_blocks) { |
| 500 | for (IR::Inst& inst : block->Instructions()) { | 500 | for (IR::Inst& inst : block->Instructions()) { |
| @@ -639,11 +639,11 @@ void TexturePass(Environment& env, IR::Program& program) { | |||
| 639 | inst->SetArg(0, IR::Value{}); | 639 | inst->SetArg(0, IR::Value{}); |
| 640 | } | 640 | } |
| 641 | 641 | ||
| 642 | if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL && | 642 | if (!host_info.support_snorm_render_buffer && inst->GetOpcode() == IR::Opcode::ImageFetch && |
| 643 | inst->GetOpcode() == IR::Opcode::ImageFetch && flags.type == TextureType::Buffer) { | 643 | flags.type == TextureType::Buffer) { |
| 644 | const auto pixel_format = ReadTexturePixelFormat(env, cbuf); | 644 | const auto pixel_format = ReadTexturePixelFormat(env, cbuf); |
| 645 | if (pixel_format != TexturePixelFormat::OTHER) { | 645 | if (pixel_format != TexturePixelFormat::OTHER) { |
| 646 | PathTexelFetch(*texture_inst.block, *texture_inst.inst, pixel_format); | 646 | PatchTexelFetch(*texture_inst.block, *texture_inst.inst, pixel_format); |
| 647 | } | 647 | } |
| 648 | } | 648 | } |
| 649 | } | 649 | } |
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h index f31e1f821..ee6252bb5 100644 --- a/src/shader_recompiler/shader_info.h +++ b/src/shader_recompiler/shader_info.h | |||
| @@ -127,6 +127,7 @@ struct Info { | |||
| 127 | bool uses_workgroup_id{}; | 127 | bool uses_workgroup_id{}; |
| 128 | bool uses_local_invocation_id{}; | 128 | bool uses_local_invocation_id{}; |
| 129 | bool uses_invocation_id{}; | 129 | bool uses_invocation_id{}; |
| 130 | bool uses_invocation_info{}; | ||
| 130 | bool uses_sample_id{}; | 131 | bool uses_sample_id{}; |
| 131 | bool uses_is_helper_invocation{}; | 132 | bool uses_is_helper_invocation{}; |
| 132 | bool uses_subgroup_invocation_id{}; | 133 | bool uses_subgroup_invocation_id{}; |
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 106991969..d7f7d336c 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt | |||
| @@ -73,8 +73,6 @@ add_library(video_core STATIC | |||
| 73 | macro/macro_hle.h | 73 | macro/macro_hle.h |
| 74 | macro/macro_interpreter.cpp | 74 | macro/macro_interpreter.cpp |
| 75 | macro/macro_interpreter.h | 75 | macro/macro_interpreter.h |
| 76 | macro/macro_jit_x64.cpp | ||
| 77 | macro/macro_jit_x64.h | ||
| 78 | fence_manager.h | 76 | fence_manager.h |
| 79 | gpu.cpp | 77 | gpu.cpp |
| 80 | gpu.h | 78 | gpu.h |
| @@ -245,7 +243,7 @@ add_library(video_core STATIC | |||
| 245 | create_target_directory_groups(video_core) | 243 | create_target_directory_groups(video_core) |
| 246 | 244 | ||
| 247 | target_link_libraries(video_core PUBLIC common core) | 245 | target_link_libraries(video_core PUBLIC common core) |
| 248 | target_link_libraries(video_core PUBLIC glad shader_recompiler xbyak) | 246 | target_link_libraries(video_core PUBLIC glad shader_recompiler) |
| 249 | 247 | ||
| 250 | if (YUZU_USE_BUNDLED_FFMPEG AND NOT WIN32) | 248 | if (YUZU_USE_BUNDLED_FFMPEG AND NOT WIN32) |
| 251 | add_dependencies(video_core ffmpeg-build) | 249 | add_dependencies(video_core ffmpeg-build) |
| @@ -282,8 +280,19 @@ else() | |||
| 282 | 280 | ||
| 283 | -Wno-sign-conversion | 281 | -Wno-sign-conversion |
| 284 | ) | 282 | ) |
| 283 | |||
| 284 | # xbyak | ||
| 285 | set_source_files_properties(macro/macro_jit_x64.cpp PROPERTIES COMPILE_OPTIONS "-Wno-conversion;-Wno-shadow") | ||
| 285 | endif() | 286 | endif() |
| 286 | 287 | ||
| 287 | if (ARCHITECTURE_x86_64) | 288 | if (ARCHITECTURE_x86_64) |
| 289 | target_sources(video_core PRIVATE | ||
| 290 | macro/macro_jit_x64.cpp | ||
| 291 | macro/macro_jit_x64.h | ||
| 292 | ) | ||
| 293 | target_link_libraries(video_core PUBLIC xbyak) | ||
| 294 | endif() | ||
| 295 | |||
| 296 | if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) | ||
| 288 | target_link_libraries(video_core PRIVATE dynarmic) | 297 | target_link_libraries(video_core PRIVATE dynarmic) |
| 289 | endif() | 298 | 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 | |||
| 249 | return; | 249 | return; |
| 250 | case MAXWELL3D_REG_INDEX(fragment_barrier): | 250 | case MAXWELL3D_REG_INDEX(fragment_barrier): |
| 251 | return rasterizer->FragmentBarrier(); | 251 | return rasterizer->FragmentBarrier(); |
| 252 | case MAXWELL3D_REG_INDEX(invalidate_texture_data_cache): | ||
| 253 | rasterizer->InvalidateGPUCache(); | ||
| 254 | return rasterizer->WaitForIdle(); | ||
| 255 | case MAXWELL3D_REG_INDEX(tiled_cache_barrier): | ||
| 256 | return rasterizer->TiledCacheBarrier(); | ||
| 252 | } | 257 | } |
| 253 | } | 258 | } |
| 254 | 259 | ||
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index a948fcb14..34b085388 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h | |||
| @@ -707,7 +707,7 @@ public: | |||
| 707 | case Size::Size_A2_B10_G10_R10: | 707 | case Size::Size_A2_B10_G10_R10: |
| 708 | return "2_10_10_10"; | 708 | return "2_10_10_10"; |
| 709 | case Size::Size_B10_G11_R11: | 709 | case Size::Size_B10_G11_R11: |
| 710 | return "10_11_12"; | 710 | return "10_11_11"; |
| 711 | default: | 711 | default: |
| 712 | ASSERT(false); | 712 | ASSERT(false); |
| 713 | return {}; | 713 | return {}; |
| @@ -2639,7 +2639,7 @@ public: | |||
| 2639 | L2CacheControl l2_cache_control; ///< 0x0218 | 2639 | L2CacheControl l2_cache_control; ///< 0x0218 |
| 2640 | InvalidateShaderCache invalidate_shader_cache; ///< 0x021C | 2640 | InvalidateShaderCache invalidate_shader_cache; ///< 0x021C |
| 2641 | INSERT_PADDING_BYTES_NOINIT(0xA8); | 2641 | INSERT_PADDING_BYTES_NOINIT(0xA8); |
| 2642 | SyncInfo sync_info; ///< 0x02C8 | 2642 | SyncInfo sync_info; ///< 0x02C8 |
| 2643 | INSERT_PADDING_BYTES_NOINIT(0x4); | 2643 | INSERT_PADDING_BYTES_NOINIT(0x4); |
| 2644 | u32 prim_circular_buffer_throttle; ///< 0x02D0 | 2644 | u32 prim_circular_buffer_throttle; ///< 0x02D0 |
| 2645 | u32 flush_invalidate_rop_mini_cache; ///< 0x02D4 | 2645 | u32 flush_invalidate_rop_mini_cache; ///< 0x02D4 |
| @@ -2731,7 +2731,11 @@ public: | |||
| 2731 | s32 stencil_back_ref; ///< 0x0F54 | 2731 | s32 stencil_back_ref; ///< 0x0F54 |
| 2732 | u32 stencil_back_mask; ///< 0x0F58 | 2732 | u32 stencil_back_mask; ///< 0x0F58 |
| 2733 | u32 stencil_back_func_mask; ///< 0x0F5C | 2733 | u32 stencil_back_func_mask; ///< 0x0F5C |
| 2734 | INSERT_PADDING_BYTES_NOINIT(0x24); | 2734 | INSERT_PADDING_BYTES_NOINIT(0x14); |
| 2735 | u32 invalidate_texture_data_cache; ///< 0x0F74 Assumed - Not in official docs. | ||
| 2736 | INSERT_PADDING_BYTES_NOINIT(0x4); | ||
| 2737 | u32 tiled_cache_barrier; ///< 0x0F7C Assumed - Not in official docs. | ||
| 2738 | INSERT_PADDING_BYTES_NOINIT(0x4); | ||
| 2735 | VertexStreamSubstitute vertex_stream_substitute; ///< 0x0F84 | 2739 | VertexStreamSubstitute vertex_stream_substitute; ///< 0x0F84 |
| 2736 | u32 line_mode_clip_generated_edge_do_not_draw; ///< 0x0F8C | 2740 | u32 line_mode_clip_generated_edge_do_not_draw; ///< 0x0F8C |
| 2737 | u32 color_mask_common; ///< 0x0F90 | 2741 | u32 color_mask_common; ///< 0x0F90 |
| @@ -2791,7 +2795,8 @@ public: | |||
| 2791 | FillViaTriangleMode fill_via_triangle_mode; ///< 0x113C | 2795 | FillViaTriangleMode fill_via_triangle_mode; ///< 0x113C |
| 2792 | u32 blend_per_format_snorm8_unorm16_snorm16_enabled; ///< 0x1140 | 2796 | u32 blend_per_format_snorm8_unorm16_snorm16_enabled; ///< 0x1140 |
| 2793 | u32 flush_pending_writes_sm_gloal_store; ///< 0x1144 | 2797 | u32 flush_pending_writes_sm_gloal_store; ///< 0x1144 |
| 2794 | INSERT_PADDING_BYTES_NOINIT(0x18); | 2798 | u32 conservative_raster_enable; ///< 0x1148 Assumed - Not in official docs. |
| 2799 | INSERT_PADDING_BYTES_NOINIT(0x14); | ||
| 2795 | std::array<VertexAttribute, NumVertexAttributes> vertex_attrib_format; ///< 0x1160 | 2800 | std::array<VertexAttribute, NumVertexAttributes> vertex_attrib_format; ///< 0x1160 |
| 2796 | std::array<MsaaSampleLocation, 4> multisample_sample_locations; ///< 0x11E0 | 2801 | std::array<MsaaSampleLocation, 4> multisample_sample_locations; ///< 0x11E0 |
| 2797 | u32 offset_render_target_index_by_viewport_index; ///< 0x11F0 | 2802 | u32 offset_render_target_index_by_viewport_index; ///< 0x11F0 |
| @@ -2970,7 +2975,7 @@ public: | |||
| 2970 | CullFace gl_cull_face; ///< 0x1920 | 2975 | CullFace gl_cull_face; ///< 0x1920 |
| 2971 | Viewport::PixelCenter viewport_pixel_center; ///< 0x1924 | 2976 | Viewport::PixelCenter viewport_pixel_center; ///< 0x1924 |
| 2972 | INSERT_PADDING_BYTES_NOINIT(0x4); | 2977 | INSERT_PADDING_BYTES_NOINIT(0x4); |
| 2973 | u32 viewport_scale_offset_enbled; ///< 0x192C | 2978 | u32 viewport_scale_offset_enabled; ///< 0x192C |
| 2974 | INSERT_PADDING_BYTES_NOINIT(0xC); | 2979 | INSERT_PADDING_BYTES_NOINIT(0xC); |
| 2975 | ViewportClipControl viewport_clip_control; ///< 0x193C | 2980 | ViewportClipControl viewport_clip_control; ///< 0x193C |
| 2976 | UserClip::Op user_clip_op; ///< 0x1940 | 2981 | UserClip::Op user_clip_op; ///< 0x1940 |
| @@ -3287,6 +3292,8 @@ ASSERT_REG_POSITION(const_color_rendering, 0x0F40); | |||
| 3287 | ASSERT_REG_POSITION(stencil_back_ref, 0x0F54); | 3292 | ASSERT_REG_POSITION(stencil_back_ref, 0x0F54); |
| 3288 | ASSERT_REG_POSITION(stencil_back_mask, 0x0F58); | 3293 | ASSERT_REG_POSITION(stencil_back_mask, 0x0F58); |
| 3289 | ASSERT_REG_POSITION(stencil_back_func_mask, 0x0F5C); | 3294 | ASSERT_REG_POSITION(stencil_back_func_mask, 0x0F5C); |
| 3295 | ASSERT_REG_POSITION(invalidate_texture_data_cache, 0x0F74); | ||
| 3296 | ASSERT_REG_POSITION(tiled_cache_barrier, 0x0F7C); | ||
| 3290 | ASSERT_REG_POSITION(vertex_stream_substitute, 0x0F84); | 3297 | ASSERT_REG_POSITION(vertex_stream_substitute, 0x0F84); |
| 3291 | ASSERT_REG_POSITION(line_mode_clip_generated_edge_do_not_draw, 0x0F8C); | 3298 | ASSERT_REG_POSITION(line_mode_clip_generated_edge_do_not_draw, 0x0F8C); |
| 3292 | ASSERT_REG_POSITION(color_mask_common, 0x0F90); | 3299 | ASSERT_REG_POSITION(color_mask_common, 0x0F90); |
| @@ -3343,6 +3350,7 @@ ASSERT_REG_POSITION(post_ps_use_pre_ps_coverage, 0x1138); | |||
| 3343 | ASSERT_REG_POSITION(fill_via_triangle_mode, 0x113C); | 3350 | ASSERT_REG_POSITION(fill_via_triangle_mode, 0x113C); |
| 3344 | ASSERT_REG_POSITION(blend_per_format_snorm8_unorm16_snorm16_enabled, 0x1140); | 3351 | ASSERT_REG_POSITION(blend_per_format_snorm8_unorm16_snorm16_enabled, 0x1140); |
| 3345 | ASSERT_REG_POSITION(flush_pending_writes_sm_gloal_store, 0x1144); | 3352 | ASSERT_REG_POSITION(flush_pending_writes_sm_gloal_store, 0x1144); |
| 3353 | ASSERT_REG_POSITION(conservative_raster_enable, 0x1148); | ||
| 3346 | ASSERT_REG_POSITION(vertex_attrib_format, 0x1160); | 3354 | ASSERT_REG_POSITION(vertex_attrib_format, 0x1160); |
| 3347 | ASSERT_REG_POSITION(multisample_sample_locations, 0x11E0); | 3355 | ASSERT_REG_POSITION(multisample_sample_locations, 0x11E0); |
| 3348 | ASSERT_REG_POSITION(offset_render_target_index_by_viewport_index, 0x11F0); | 3356 | ASSERT_REG_POSITION(offset_render_target_index_by_viewport_index, 0x11F0); |
| @@ -3482,7 +3490,7 @@ ASSERT_REG_POSITION(gl_cull_test_enabled, 0x1918); | |||
| 3482 | ASSERT_REG_POSITION(gl_front_face, 0x191C); | 3490 | ASSERT_REG_POSITION(gl_front_face, 0x191C); |
| 3483 | ASSERT_REG_POSITION(gl_cull_face, 0x1920); | 3491 | ASSERT_REG_POSITION(gl_cull_face, 0x1920); |
| 3484 | ASSERT_REG_POSITION(viewport_pixel_center, 0x1924); | 3492 | ASSERT_REG_POSITION(viewport_pixel_center, 0x1924); |
| 3485 | ASSERT_REG_POSITION(viewport_scale_offset_enbled, 0x192C); | 3493 | ASSERT_REG_POSITION(viewport_scale_offset_enabled, 0x192C); |
| 3486 | ASSERT_REG_POSITION(viewport_clip_control, 0x193C); | 3494 | ASSERT_REG_POSITION(viewport_clip_control, 0x193C); |
| 3487 | ASSERT_REG_POSITION(user_clip_op, 0x1940); | 3495 | ASSERT_REG_POSITION(user_clip_op, 0x1940); |
| 3488 | ASSERT_REG_POSITION(render_enable_override, 0x1944); | 3496 | 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() { | |||
| 102 | const bool is_src_pitch = IsPitchKind(static_cast<PTEKind>(src_kind)); | 102 | const bool is_src_pitch = IsPitchKind(static_cast<PTEKind>(src_kind)); |
| 103 | const bool is_dst_pitch = IsPitchKind(static_cast<PTEKind>(dst_kind)); | 103 | const bool is_dst_pitch = IsPitchKind(static_cast<PTEKind>(dst_kind)); |
| 104 | if (!is_src_pitch && is_dst_pitch) { | 104 | if (!is_src_pitch && is_dst_pitch) { |
| 105 | std::vector<u8> tmp_buffer(regs.line_length_in); | 105 | UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0); |
| 106 | std::vector<u8> dst_buffer(regs.line_length_in); | 106 | UNIMPLEMENTED_IF(regs.offset_in % 16 != 0); |
| 107 | memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(), | 107 | UNIMPLEMENTED_IF(regs.offset_out % 16 != 0); |
| 108 | regs.line_length_in); | 108 | std::vector<u8> tmp_buffer(16); |
| 109 | for (u32 offset = 0; offset < regs.line_length_in; ++offset) { | 109 | for (u32 offset = 0; offset < regs.line_length_in; offset += 16) { |
| 110 | dst_buffer[offset] = | 110 | memory_manager.ReadBlockUnsafe( |
| 111 | tmp_buffer[convert_linear_2_blocklinear_addr(regs.offset_in + offset) - | 111 | convert_linear_2_blocklinear_addr(regs.offset_in + offset), |
| 112 | regs.offset_in]; | 112 | tmp_buffer.data(), tmp_buffer.size()); |
| 113 | memory_manager.WriteBlock(regs.offset_out + offset, tmp_buffer.data(), | ||
| 114 | tmp_buffer.size()); | ||
| 113 | } | 115 | } |
| 114 | memory_manager.WriteBlock(regs.offset_out, dst_buffer.data(), regs.line_length_in); | ||
| 115 | } else if (is_src_pitch && !is_dst_pitch) { | 116 | } else if (is_src_pitch && !is_dst_pitch) { |
| 116 | std::vector<u8> tmp_buffer(regs.line_length_in); | 117 | UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0); |
| 117 | std::vector<u8> dst_buffer(regs.line_length_in); | 118 | UNIMPLEMENTED_IF(regs.offset_in % 16 != 0); |
| 118 | memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(), | 119 | UNIMPLEMENTED_IF(regs.offset_out % 16 != 0); |
| 119 | regs.line_length_in); | 120 | std::vector<u8> tmp_buffer(16); |
| 120 | for (u32 offset = 0; offset < regs.line_length_in; ++offset) { | 121 | for (u32 offset = 0; offset < regs.line_length_in; offset += 16) { |
| 121 | dst_buffer[convert_linear_2_blocklinear_addr(regs.offset_out + offset) - | 122 | memory_manager.ReadBlockUnsafe(regs.offset_in + offset, tmp_buffer.data(), |
| 122 | regs.offset_out] = tmp_buffer[offset]; | 123 | tmp_buffer.size()); |
| 124 | memory_manager.WriteBlock( | ||
| 125 | convert_linear_2_blocklinear_addr(regs.offset_out + offset), | ||
| 126 | tmp_buffer.data(), tmp_buffer.size()); | ||
| 123 | } | 127 | } |
| 124 | memory_manager.WriteBlock(regs.offset_out, dst_buffer.data(), regs.line_length_in); | ||
| 125 | } else { | 128 | } else { |
| 126 | if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) { | 129 | if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) { |
| 127 | std::vector<u8> tmp_buffer(regs.line_length_in); | 130 | std::vector<u8> tmp_buffer(regs.line_length_in); |
| @@ -311,6 +314,7 @@ void MaxwellDMA::ReleaseSemaphore() { | |||
| 311 | } | 314 | } |
| 312 | default: | 315 | default: |
| 313 | ASSERT_MSG(false, "Unknown semaphore type: {}", static_cast<u32>(type.Value())); | 316 | ASSERT_MSG(false, "Unknown semaphore type: {}", static_cast<u32>(type.Value())); |
| 317 | break; | ||
| 314 | } | 318 | } |
| 315 | } | 319 | } |
| 316 | 320 | ||
diff --git a/src/video_core/engines/puller.cpp b/src/video_core/engines/puller.cpp index 3977bb0fb..4d2278811 100644 --- a/src/video_core/engines/puller.cpp +++ b/src/video_core/engines/puller.cpp | |||
| @@ -50,6 +50,7 @@ void Puller::ProcessBindMethod(const MethodCall& method_call) { | |||
| 50 | break; | 50 | break; |
| 51 | default: | 51 | default: |
| 52 | UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id); | 52 | UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id); |
| 53 | break; | ||
| 53 | } | 54 | } |
| 54 | } | 55 | } |
| 55 | 56 | ||
| @@ -65,6 +66,7 @@ void Puller::ProcessFenceActionMethod() { | |||
| 65 | break; | 66 | break; |
| 66 | default: | 67 | default: |
| 67 | UNIMPLEMENTED_MSG("Unimplemented operation {}", regs.fence_action.op.Value()); | 68 | UNIMPLEMENTED_MSG("Unimplemented operation {}", regs.fence_action.op.Value()); |
| 69 | break; | ||
| 68 | } | 70 | } |
| 69 | } | 71 | } |
| 70 | 72 | ||
| @@ -228,6 +230,7 @@ void Puller::CallEngineMethod(const MethodCall& method_call) { | |||
| 228 | break; | 230 | break; |
| 229 | default: | 231 | default: |
| 230 | UNIMPLEMENTED_MSG("Unimplemented engine"); | 232 | UNIMPLEMENTED_MSG("Unimplemented engine"); |
| 233 | break; | ||
| 231 | } | 234 | } |
| 232 | } | 235 | } |
| 233 | 236 | ||
| @@ -254,6 +257,7 @@ void Puller::CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_s | |||
| 254 | break; | 257 | break; |
| 255 | default: | 258 | default: |
| 256 | UNIMPLEMENTED_MSG("Unimplemented engine"); | 259 | UNIMPLEMENTED_MSG("Unimplemented engine"); |
| 260 | break; | ||
| 257 | } | 261 | } |
| 258 | } | 262 | } |
| 259 | 263 | ||
diff --git a/src/video_core/macro/macro.cpp b/src/video_core/macro/macro.cpp index f61d5998e..505d81c1e 100644 --- a/src/video_core/macro/macro.cpp +++ b/src/video_core/macro/macro.cpp | |||
| @@ -16,7 +16,10 @@ | |||
| 16 | #include "video_core/macro/macro.h" | 16 | #include "video_core/macro/macro.h" |
| 17 | #include "video_core/macro/macro_hle.h" | 17 | #include "video_core/macro/macro_hle.h" |
| 18 | #include "video_core/macro/macro_interpreter.h" | 18 | #include "video_core/macro/macro_interpreter.h" |
| 19 | |||
| 20 | #ifdef ARCHITECTURE_x86_64 | ||
| 19 | #include "video_core/macro/macro_jit_x64.h" | 21 | #include "video_core/macro/macro_jit_x64.h" |
| 22 | #endif | ||
| 20 | 23 | ||
| 21 | namespace Tegra { | 24 | namespace Tegra { |
| 22 | 25 | ||
diff --git a/src/video_core/macro/macro_interpreter.cpp b/src/video_core/macro/macro_interpreter.cpp index c0d32c112..0d63495a9 100644 --- a/src/video_core/macro/macro_interpreter.cpp +++ b/src/video_core/macro/macro_interpreter.cpp | |||
| @@ -201,6 +201,7 @@ bool MacroInterpreterImpl::Step(bool is_delay_slot) { | |||
| 201 | } | 201 | } |
| 202 | default: | 202 | default: |
| 203 | UNIMPLEMENTED_MSG("Unimplemented macro operation {}", opcode.operation.Value()); | 203 | UNIMPLEMENTED_MSG("Unimplemented macro operation {}", opcode.operation.Value()); |
| 204 | break; | ||
| 204 | } | 205 | } |
| 205 | 206 | ||
| 206 | // An instruction with the Exit flag will not actually | 207 | // An instruction with the Exit flag will not actually |
| @@ -297,6 +298,7 @@ void MacroInterpreterImpl::ProcessResult(Macro::ResultOperation operation, u32 r | |||
| 297 | break; | 298 | break; |
| 298 | default: | 299 | default: |
| 299 | UNIMPLEMENTED_MSG("Unimplemented result operation {}", operation); | 300 | UNIMPLEMENTED_MSG("Unimplemented result operation {}", operation); |
| 301 | break; | ||
| 300 | } | 302 | } |
| 301 | } | 303 | } |
| 302 | 304 | ||
diff --git a/src/video_core/macro/macro_jit_x64.cpp b/src/video_core/macro/macro_jit_x64.cpp index 25c1ce798..7347cbd88 100644 --- a/src/video_core/macro/macro_jit_x64.cpp +++ b/src/video_core/macro/macro_jit_x64.cpp | |||
| @@ -652,6 +652,7 @@ void MacroJITx64Impl::Compile_ProcessResult(Macro::ResultOperation operation, u3 | |||
| 652 | break; | 652 | break; |
| 653 | default: | 653 | default: |
| 654 | UNIMPLEMENTED_MSG("Unimplemented macro operation {}", operation); | 654 | UNIMPLEMENTED_MSG("Unimplemented macro operation {}", operation); |
| 655 | break; | ||
| 655 | } | 656 | } |
| 656 | } | 657 | } |
| 657 | 658 | ||
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 72e314d39..d05a5f60b 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -618,7 +618,7 @@ void RasterizerOpenGL::SyncViewport() { | |||
| 618 | } | 618 | } |
| 619 | flags[Dirty::Viewport0 + index] = false; | 619 | flags[Dirty::Viewport0 + index] = false; |
| 620 | 620 | ||
| 621 | if (!regs.viewport_scale_offset_enbled) { | 621 | if (!regs.viewport_scale_offset_enabled) { |
| 622 | const auto x = static_cast<GLfloat>(regs.surface_clip.x); | 622 | const auto x = static_cast<GLfloat>(regs.surface_clip.x); |
| 623 | const auto y = static_cast<GLfloat>(regs.surface_clip.y); | 623 | const auto y = static_cast<GLfloat>(regs.surface_clip.y); |
| 624 | const auto width = static_cast<GLfloat>(regs.surface_clip.width); | 624 | const auto width = static_cast<GLfloat>(regs.surface_clip.width); |
| @@ -770,7 +770,7 @@ void RasterizerOpenGL::SyncStencilTestState() { | |||
| 770 | 770 | ||
| 771 | if (regs.stencil_two_side_enable) { | 771 | if (regs.stencil_two_side_enable) { |
| 772 | glStencilFuncSeparate(GL_BACK, MaxwellToGL::ComparisonOp(regs.stencil_back_op.func), | 772 | glStencilFuncSeparate(GL_BACK, MaxwellToGL::ComparisonOp(regs.stencil_back_op.func), |
| 773 | regs.stencil_back_ref, regs.stencil_back_mask); | 773 | regs.stencil_back_ref, regs.stencil_back_func_mask); |
| 774 | glStencilOpSeparate(GL_BACK, MaxwellToGL::StencilOp(regs.stencil_back_op.fail), | 774 | glStencilOpSeparate(GL_BACK, MaxwellToGL::StencilOp(regs.stencil_back_op.fail), |
| 775 | MaxwellToGL::StencilOp(regs.stencil_back_op.zfail), | 775 | MaxwellToGL::StencilOp(regs.stencil_back_op.zfail), |
| 776 | MaxwellToGL::StencilOp(regs.stencil_back_op.zpass)); | 776 | MaxwellToGL::StencilOp(regs.stencil_back_op.zpass)); |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 977709518..3fe04a115 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp | |||
| @@ -76,7 +76,8 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key, | |||
| 76 | } | 76 | } |
| 77 | break; | 77 | break; |
| 78 | case Shader::Stage::TessellationEval: | 78 | case Shader::Stage::TessellationEval: |
| 79 | info.tess_clockwise = key.tessellation_clockwise != 0; | 79 | // Flip the face, as OpenGL's drawing is flipped. |
| 80 | info.tess_clockwise = key.tessellation_clockwise == 0; | ||
| 80 | info.tess_primitive = [&key] { | 81 | info.tess_primitive = [&key] { |
| 81 | switch (key.tessellation_primitive) { | 82 | switch (key.tessellation_primitive) { |
| 82 | case Maxwell::Tessellation::DomainType::Isolines: | 83 | case Maxwell::Tessellation::DomainType::Isolines: |
| @@ -218,6 +219,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo | |||
| 218 | .support_float16 = false, | 219 | .support_float16 = false, |
| 219 | .support_int64 = device.HasShaderInt64(), | 220 | .support_int64 = device.HasShaderInt64(), |
| 220 | .needs_demote_reorder = device.IsAmd(), | 221 | .needs_demote_reorder = device.IsAmd(), |
| 222 | .support_snorm_render_buffer = false, | ||
| 221 | } { | 223 | } { |
| 222 | if (use_asynchronous_shaders) { | 224 | if (use_asynchronous_shaders) { |
| 223 | workers = CreateWorkers(); | 225 | workers = CreateWorkers(); |
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp index a359f96f1..d53b422ca 100644 --- a/src/video_core/renderer_opengl/gl_state_tracker.cpp +++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp | |||
| @@ -70,8 +70,8 @@ void SetupDirtyViewports(Tables& tables) { | |||
| 70 | FillBlock(tables[1], OFF(viewport_transform), NUM(viewport_transform), Viewports); | 70 | FillBlock(tables[1], OFF(viewport_transform), NUM(viewport_transform), Viewports); |
| 71 | FillBlock(tables[1], OFF(viewports), NUM(viewports), Viewports); | 71 | FillBlock(tables[1], OFF(viewports), NUM(viewports), Viewports); |
| 72 | 72 | ||
| 73 | tables[0][OFF(viewport_scale_offset_enbled)] = ViewportTransform; | 73 | tables[0][OFF(viewport_scale_offset_enabled)] = ViewportTransform; |
| 74 | tables[1][OFF(viewport_scale_offset_enbled)] = Viewports; | 74 | tables[1][OFF(viewport_scale_offset_enabled)] = Viewports; |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | void SetupDirtyScissors(Tables& tables) { | 77 | 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 | |||
| 891 | break; | 891 | break; |
| 892 | default: | 892 | default: |
| 893 | ASSERT(false); | 893 | ASSERT(false); |
| 894 | break; | ||
| 894 | } | 895 | } |
| 895 | } | 896 | } |
| 896 | 897 | ||
| @@ -927,6 +928,7 @@ void Image::CopyImageToBuffer(const VideoCommon::BufferImageCopy& copy, size_t b | |||
| 927 | break; | 928 | break; |
| 928 | default: | 929 | default: |
| 929 | ASSERT(false); | 930 | ASSERT(false); |
| 931 | break; | ||
| 930 | } | 932 | } |
| 931 | // Compressed formats don't have a pixel format or type | 933 | // Compressed formats don't have a pixel format or type |
| 932 | const bool is_compressed = gl_format == GL_NONE; | 934 | const bool is_compressed = gl_format == GL_NONE; |
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 8bd5eba7e..f29462f7c 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp | |||
| @@ -340,6 +340,7 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, | |||
| 340 | texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; | 340 | texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; |
| 341 | // UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", | 341 | // UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", |
| 342 | // static_cast<u32>(framebuffer.pixel_format)); | 342 | // static_cast<u32>(framebuffer.pixel_format)); |
| 343 | break; | ||
| 343 | } | 344 | } |
| 344 | 345 | ||
| 345 | texture.resource.Release(); | 346 | texture.resource.Release(); |
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index f85ed8e5b..98cc26679 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp | |||
| @@ -90,6 +90,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, | |||
| 90 | depth_format.Assign(static_cast<u32>(regs.zeta.format)); | 90 | depth_format.Assign(static_cast<u32>(regs.zeta.format)); |
| 91 | y_negate.Assign(regs.window_origin.mode != Maxwell::WindowOrigin::Mode::UpperLeft ? 1 : 0); | 91 | y_negate.Assign(regs.window_origin.mode != Maxwell::WindowOrigin::Mode::UpperLeft ? 1 : 0); |
| 92 | provoking_vertex_last.Assign(regs.provoking_vertex == Maxwell::ProvokingVertex::Last ? 1 : 0); | 92 | provoking_vertex_last.Assign(regs.provoking_vertex == Maxwell::ProvokingVertex::Last ? 1 : 0); |
| 93 | conservative_raster_enable.Assign(regs.conservative_raster_enable != 0 ? 1 : 0); | ||
| 93 | smooth_lines.Assign(regs.line_anti_alias_enable != 0 ? 1 : 0); | 94 | smooth_lines.Assign(regs.line_anti_alias_enable != 0 ? 1 : 0); |
| 94 | 95 | ||
| 95 | for (size_t i = 0; i < regs.rt.size(); ++i) { | 96 | for (size_t i = 0; i < regs.rt.size(); ++i) { |
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index 43441209c..1afdef329 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h | |||
| @@ -193,6 +193,7 @@ struct FixedPipelineState { | |||
| 193 | BitField<6, 5, u32> depth_format; | 193 | BitField<6, 5, u32> depth_format; |
| 194 | BitField<11, 1, u32> y_negate; | 194 | BitField<11, 1, u32> y_negate; |
| 195 | BitField<12, 1, u32> provoking_vertex_last; | 195 | BitField<12, 1, u32> provoking_vertex_last; |
| 196 | BitField<13, 1, u32> conservative_raster_enable; | ||
| 196 | BitField<14, 1, u32> smooth_lines; | 197 | BitField<14, 1, u32> smooth_lines; |
| 197 | }; | 198 | }; |
| 198 | std::array<u8, Maxwell::NumRenderTargets> color_formats; | 199 | std::array<u8, Maxwell::NumRenderTargets> color_formats; |
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 1aa116cea..ef75c126c 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp | |||
| @@ -680,6 +680,15 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { | |||
| 680 | .lineStippleFactor = 0, | 680 | .lineStippleFactor = 0, |
| 681 | .lineStipplePattern = 0, | 681 | .lineStipplePattern = 0, |
| 682 | }; | 682 | }; |
| 683 | VkPipelineRasterizationConservativeStateCreateInfoEXT conservative_raster{ | ||
| 684 | .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_CONSERVATIVE_STATE_CREATE_INFO_EXT, | ||
| 685 | .pNext = nullptr, | ||
| 686 | .flags = 0, | ||
| 687 | .conservativeRasterizationMode = key.state.conservative_raster_enable != 0 | ||
| 688 | ? VK_CONSERVATIVE_RASTERIZATION_MODE_OVERESTIMATE_EXT | ||
| 689 | : VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT, | ||
| 690 | .extraPrimitiveOverestimationSize = 0.0f, | ||
| 691 | }; | ||
| 683 | VkPipelineRasterizationProvokingVertexStateCreateInfoEXT provoking_vertex{ | 692 | VkPipelineRasterizationProvokingVertexStateCreateInfoEXT provoking_vertex{ |
| 684 | .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT, | 693 | .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT, |
| 685 | .pNext = nullptr, | 694 | .pNext = nullptr, |
| @@ -690,6 +699,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { | |||
| 690 | if (IsLine(input_assembly_topology) && device.IsExtLineRasterizationSupported()) { | 699 | if (IsLine(input_assembly_topology) && device.IsExtLineRasterizationSupported()) { |
| 691 | line_state.pNext = std::exchange(rasterization_ci.pNext, &line_state); | 700 | line_state.pNext = std::exchange(rasterization_ci.pNext, &line_state); |
| 692 | } | 701 | } |
| 702 | if (device.IsExtConservativeRasterizationSupported()) { | ||
| 703 | conservative_raster.pNext = std::exchange(rasterization_ci.pNext, &conservative_raster); | ||
| 704 | } | ||
| 693 | if (device.IsExtProvokingVertexSupported()) { | 705 | if (device.IsExtProvokingVertexSupported()) { |
| 694 | provoking_vertex.pNext = std::exchange(rasterization_ci.pNext, &provoking_vertex); | 706 | provoking_vertex.pNext = std::exchange(rasterization_ci.pNext, &provoking_vertex); |
| 695 | } | 707 | } |
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index b42e5be1e..d4b0a542a 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | |||
| @@ -166,6 +166,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program | |||
| 166 | } | 166 | } |
| 167 | break; | 167 | break; |
| 168 | case Shader::Stage::TessellationEval: | 168 | case Shader::Stage::TessellationEval: |
| 169 | info.tess_clockwise = key.state.tessellation_clockwise != 0; | ||
| 169 | info.tess_primitive = [&key] { | 170 | info.tess_primitive = [&key] { |
| 170 | const u32 raw{key.state.tessellation_primitive.Value()}; | 171 | const u32 raw{key.state.tessellation_primitive.Value()}; |
| 171 | switch (static_cast<Maxwell::Tessellation::DomainType>(raw)) { | 172 | switch (static_cast<Maxwell::Tessellation::DomainType>(raw)) { |
| @@ -325,6 +326,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device | |||
| 325 | .support_int64 = device.IsShaderInt64Supported(), | 326 | .support_int64 = device.IsShaderInt64Supported(), |
| 326 | .needs_demote_reorder = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY_KHR || | 327 | .needs_demote_reorder = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY_KHR || |
| 327 | driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR, | 328 | driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR, |
| 329 | .support_snorm_render_buffer = true, | ||
| 328 | }; | 330 | }; |
| 329 | } | 331 | } |
| 330 | 332 | ||
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index f79fa8313..f69c0c50f 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp | |||
| @@ -683,7 +683,7 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg | |||
| 683 | if (!state_tracker.TouchViewports()) { | 683 | if (!state_tracker.TouchViewports()) { |
| 684 | return; | 684 | return; |
| 685 | } | 685 | } |
| 686 | if (!regs.viewport_scale_offset_enbled) { | 686 | if (!regs.viewport_scale_offset_enabled) { |
| 687 | const auto x = static_cast<float>(regs.surface_clip.x); | 687 | const auto x = static_cast<float>(regs.surface_clip.x); |
| 688 | const auto y = static_cast<float>(regs.surface_clip.y); | 688 | const auto y = static_cast<float>(regs.surface_clip.y); |
| 689 | const auto width = static_cast<float>(regs.surface_clip.width); | 689 | const auto width = static_cast<float>(regs.surface_clip.width); |
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 7934f2a51..4a7b633b7 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp | |||
| @@ -221,6 +221,7 @@ void Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_s | |||
| 221 | [[fallthrough]]; | 221 | [[fallthrough]]; |
| 222 | default: | 222 | default: |
| 223 | vk::Check(result); | 223 | vk::Check(result); |
| 224 | break; | ||
| 224 | } | 225 | } |
| 225 | }); | 226 | }); |
| 226 | chunk->MarkSubmit(); | 227 | chunk->MarkSubmit(); |
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp index b87c3be66..edb41b171 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp +++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp | |||
| @@ -51,7 +51,7 @@ Flags MakeInvalidationFlags() { | |||
| 51 | void SetupDirtyViewports(Tables& tables) { | 51 | void SetupDirtyViewports(Tables& tables) { |
| 52 | FillBlock(tables[0], OFF(viewport_transform), NUM(viewport_transform), Viewports); | 52 | FillBlock(tables[0], OFF(viewport_transform), NUM(viewport_transform), Viewports); |
| 53 | FillBlock(tables[0], OFF(viewports), NUM(viewports), Viewports); | 53 | FillBlock(tables[0], OFF(viewports), NUM(viewports), Viewports); |
| 54 | tables[0][OFF(viewport_scale_offset_enbled)] = Viewports; | 54 | tables[0][OFF(viewport_scale_offset_enabled)] = Viewports; |
| 55 | tables[1][OFF(window_origin)] = Viewports; | 55 | tables[1][OFF(window_origin)] = Viewports; |
| 56 | } | 56 | } |
| 57 | 57 | ||
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 853b80d8a..a65bbeb1c 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp | |||
| @@ -108,6 +108,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) { | |||
| 108 | break; | 108 | break; |
| 109 | default: | 109 | default: |
| 110 | ASSERT_MSG(false, "Invalid surface type"); | 110 | ASSERT_MSG(false, "Invalid surface type"); |
| 111 | break; | ||
| 111 | } | 112 | } |
| 112 | } | 113 | } |
| 113 | if (info.storage) { | 114 | if (info.storage) { |
diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp index 37bb76b72..f24f320b6 100644 --- a/src/video_core/shader_environment.cpp +++ b/src/video_core/shader_environment.cpp | |||
| @@ -352,7 +352,7 @@ Shader::TexturePixelFormat GraphicsEnvironment::ReadTexturePixelFormat(u32 handl | |||
| 352 | 352 | ||
| 353 | u32 GraphicsEnvironment::ReadViewportTransformState() { | 353 | u32 GraphicsEnvironment::ReadViewportTransformState() { |
| 354 | const auto& regs{maxwell3d->regs}; | 354 | const auto& regs{maxwell3d->regs}; |
| 355 | viewport_transform_state = regs.viewport_scale_offset_enbled; | 355 | viewport_transform_state = regs.viewport_scale_offset_enabled; |
| 356 | return viewport_transform_state; | 356 | return viewport_transform_state; |
| 357 | } | 357 | } |
| 358 | 358 | ||
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index fd1a4b987..59120cd09 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp | |||
| @@ -170,6 +170,7 @@ void Swizzle(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixe | |||
| 170 | #undef BPP_CASE | 170 | #undef BPP_CASE |
| 171 | default: | 171 | default: |
| 172 | ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel); | 172 | ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel); |
| 173 | break; | ||
| 173 | } | 174 | } |
| 174 | } | 175 | } |
| 175 | 176 | ||
| @@ -217,6 +218,7 @@ void SwizzleSubrect(std::span<u8> output, std::span<const u8> input, u32 bytes_p | |||
| 217 | #undef BPP_CASE | 218 | #undef BPP_CASE |
| 218 | default: | 219 | default: |
| 219 | ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel); | 220 | ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel); |
| 221 | break; | ||
| 220 | } | 222 | } |
| 221 | } | 223 | } |
| 222 | 224 | ||
| @@ -240,6 +242,7 @@ void UnswizzleSubrect(std::span<u8> output, std::span<const u8> input, u32 bytes | |||
| 240 | #undef BPP_CASE | 242 | #undef BPP_CASE |
| 241 | default: | 243 | default: |
| 242 | ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel); | 244 | ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel); |
| 245 | break; | ||
| 243 | } | 246 | } |
| 244 | } | 247 | } |
| 245 | 248 | ||
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 239f12382..5cc1fbf32 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -385,6 +385,6 @@ if (NOT APPLE) | |||
| 385 | target_compile_definitions(yuzu PRIVATE HAS_OPENGL) | 385 | target_compile_definitions(yuzu PRIVATE HAS_OPENGL) |
| 386 | endif() | 386 | endif() |
| 387 | 387 | ||
| 388 | if (ARCHITECTURE_x86_64) | 388 | if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) |
| 389 | target_link_libraries(yuzu PRIVATE dynarmic) | 389 | target_link_libraries(yuzu PRIVATE dynarmic) |
| 390 | endif() | 390 | 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) { | |||
| 401 | } | 401 | } |
| 402 | 402 | ||
| 403 | int GRenderWindow::QtKeyToSwitchKey(Qt::Key qt_key) { | 403 | int GRenderWindow::QtKeyToSwitchKey(Qt::Key qt_key) { |
| 404 | switch (qt_key) { | 404 | static constexpr std::array<std::pair<Qt::Key, Settings::NativeKeyboard::Keys>, 106> key_map = { |
| 405 | case Qt::Key_A: | 405 | std::pair<Qt::Key, Settings::NativeKeyboard::Keys>{Qt::Key_A, Settings::NativeKeyboard::A}, |
| 406 | return Settings::NativeKeyboard::A; | 406 | {Qt::Key_A, Settings::NativeKeyboard::A}, |
| 407 | case Qt::Key_B: | 407 | {Qt::Key_B, Settings::NativeKeyboard::B}, |
| 408 | return Settings::NativeKeyboard::B; | 408 | {Qt::Key_C, Settings::NativeKeyboard::C}, |
| 409 | case Qt::Key_C: | 409 | {Qt::Key_D, Settings::NativeKeyboard::D}, |
| 410 | return Settings::NativeKeyboard::C; | 410 | {Qt::Key_E, Settings::NativeKeyboard::E}, |
| 411 | case Qt::Key_D: | 411 | {Qt::Key_F, Settings::NativeKeyboard::F}, |
| 412 | return Settings::NativeKeyboard::D; | 412 | {Qt::Key_G, Settings::NativeKeyboard::G}, |
| 413 | case Qt::Key_E: | 413 | {Qt::Key_H, Settings::NativeKeyboard::H}, |
| 414 | return Settings::NativeKeyboard::E; | 414 | {Qt::Key_I, Settings::NativeKeyboard::I}, |
| 415 | case Qt::Key_F: | 415 | {Qt::Key_J, Settings::NativeKeyboard::J}, |
| 416 | return Settings::NativeKeyboard::F; | 416 | {Qt::Key_K, Settings::NativeKeyboard::K}, |
| 417 | case Qt::Key_G: | 417 | {Qt::Key_L, Settings::NativeKeyboard::L}, |
| 418 | return Settings::NativeKeyboard::G; | 418 | {Qt::Key_M, Settings::NativeKeyboard::M}, |
| 419 | case Qt::Key_H: | 419 | {Qt::Key_N, Settings::NativeKeyboard::N}, |
| 420 | return Settings::NativeKeyboard::H; | 420 | {Qt::Key_O, Settings::NativeKeyboard::O}, |
| 421 | case Qt::Key_I: | 421 | {Qt::Key_P, Settings::NativeKeyboard::P}, |
| 422 | return Settings::NativeKeyboard::I; | 422 | {Qt::Key_Q, Settings::NativeKeyboard::Q}, |
| 423 | case Qt::Key_J: | 423 | {Qt::Key_R, Settings::NativeKeyboard::R}, |
| 424 | return Settings::NativeKeyboard::J; | 424 | {Qt::Key_S, Settings::NativeKeyboard::S}, |
| 425 | case Qt::Key_K: | 425 | {Qt::Key_T, Settings::NativeKeyboard::T}, |
| 426 | return Settings::NativeKeyboard::K; | 426 | {Qt::Key_U, Settings::NativeKeyboard::U}, |
| 427 | case Qt::Key_L: | 427 | {Qt::Key_V, Settings::NativeKeyboard::V}, |
| 428 | return Settings::NativeKeyboard::L; | 428 | {Qt::Key_W, Settings::NativeKeyboard::W}, |
| 429 | case Qt::Key_M: | 429 | {Qt::Key_X, Settings::NativeKeyboard::X}, |
| 430 | return Settings::NativeKeyboard::M; | 430 | {Qt::Key_Y, Settings::NativeKeyboard::Y}, |
| 431 | case Qt::Key_N: | 431 | {Qt::Key_Z, Settings::NativeKeyboard::Z}, |
| 432 | return Settings::NativeKeyboard::N; | 432 | {Qt::Key_1, Settings::NativeKeyboard::N1}, |
| 433 | case Qt::Key_O: | 433 | {Qt::Key_2, Settings::NativeKeyboard::N2}, |
| 434 | return Settings::NativeKeyboard::O; | 434 | {Qt::Key_3, Settings::NativeKeyboard::N3}, |
| 435 | case Qt::Key_P: | 435 | {Qt::Key_4, Settings::NativeKeyboard::N4}, |
| 436 | return Settings::NativeKeyboard::P; | 436 | {Qt::Key_5, Settings::NativeKeyboard::N5}, |
| 437 | case Qt::Key_Q: | 437 | {Qt::Key_6, Settings::NativeKeyboard::N6}, |
| 438 | return Settings::NativeKeyboard::Q; | 438 | {Qt::Key_7, Settings::NativeKeyboard::N7}, |
| 439 | case Qt::Key_R: | 439 | {Qt::Key_8, Settings::NativeKeyboard::N8}, |
| 440 | return Settings::NativeKeyboard::R; | 440 | {Qt::Key_9, Settings::NativeKeyboard::N9}, |
| 441 | case Qt::Key_S: | 441 | {Qt::Key_0, Settings::NativeKeyboard::N0}, |
| 442 | return Settings::NativeKeyboard::S; | 442 | {Qt::Key_Return, Settings::NativeKeyboard::Return}, |
| 443 | case Qt::Key_T: | 443 | {Qt::Key_Escape, Settings::NativeKeyboard::Escape}, |
| 444 | return Settings::NativeKeyboard::T; | 444 | {Qt::Key_Backspace, Settings::NativeKeyboard::Backspace}, |
| 445 | case Qt::Key_U: | 445 | {Qt::Key_Tab, Settings::NativeKeyboard::Tab}, |
| 446 | return Settings::NativeKeyboard::U; | 446 | {Qt::Key_Space, Settings::NativeKeyboard::Space}, |
| 447 | case Qt::Key_V: | 447 | {Qt::Key_Minus, Settings::NativeKeyboard::Minus}, |
| 448 | return Settings::NativeKeyboard::V; | 448 | {Qt::Key_Plus, Settings::NativeKeyboard::Plus}, |
| 449 | case Qt::Key_W: | 449 | {Qt::Key_questiondown, Settings::NativeKeyboard::Plus}, |
| 450 | return Settings::NativeKeyboard::W; | 450 | {Qt::Key_BracketLeft, Settings::NativeKeyboard::OpenBracket}, |
| 451 | case Qt::Key_X: | 451 | {Qt::Key_BraceLeft, Settings::NativeKeyboard::OpenBracket}, |
| 452 | return Settings::NativeKeyboard::X; | 452 | {Qt::Key_BracketRight, Settings::NativeKeyboard::CloseBracket}, |
| 453 | case Qt::Key_Y: | 453 | {Qt::Key_BraceRight, Settings::NativeKeyboard::CloseBracket}, |
| 454 | return Settings::NativeKeyboard::Y; | 454 | {Qt::Key_Bar, Settings::NativeKeyboard::Pipe}, |
| 455 | case Qt::Key_Z: | 455 | {Qt::Key_Dead_Tilde, Settings::NativeKeyboard::Tilde}, |
| 456 | return Settings::NativeKeyboard::Z; | 456 | {Qt::Key_Ntilde, Settings::NativeKeyboard::Semicolon}, |
| 457 | case Qt::Key_1: | 457 | {Qt::Key_Semicolon, Settings::NativeKeyboard::Semicolon}, |
| 458 | return Settings::NativeKeyboard::N1; | 458 | {Qt::Key_Apostrophe, Settings::NativeKeyboard::Quote}, |
| 459 | case Qt::Key_2: | 459 | {Qt::Key_Dead_Grave, Settings::NativeKeyboard::Backquote}, |
| 460 | return Settings::NativeKeyboard::N2; | 460 | {Qt::Key_Comma, Settings::NativeKeyboard::Comma}, |
| 461 | case Qt::Key_3: | 461 | {Qt::Key_Period, Settings::NativeKeyboard::Period}, |
| 462 | return Settings::NativeKeyboard::N3; | 462 | {Qt::Key_Slash, Settings::NativeKeyboard::Slash}, |
| 463 | case Qt::Key_4: | 463 | {Qt::Key_CapsLock, Settings::NativeKeyboard::CapsLockKey}, |
| 464 | return Settings::NativeKeyboard::N4; | 464 | {Qt::Key_F1, Settings::NativeKeyboard::F1}, |
| 465 | case Qt::Key_5: | 465 | {Qt::Key_F2, Settings::NativeKeyboard::F2}, |
| 466 | return Settings::NativeKeyboard::N5; | 466 | {Qt::Key_F3, Settings::NativeKeyboard::F3}, |
| 467 | case Qt::Key_6: | 467 | {Qt::Key_F4, Settings::NativeKeyboard::F4}, |
| 468 | return Settings::NativeKeyboard::N6; | 468 | {Qt::Key_F5, Settings::NativeKeyboard::F5}, |
| 469 | case Qt::Key_7: | 469 | {Qt::Key_F6, Settings::NativeKeyboard::F6}, |
| 470 | return Settings::NativeKeyboard::N7; | 470 | {Qt::Key_F7, Settings::NativeKeyboard::F7}, |
| 471 | case Qt::Key_8: | 471 | {Qt::Key_F8, Settings::NativeKeyboard::F8}, |
| 472 | return Settings::NativeKeyboard::N8; | 472 | {Qt::Key_F9, Settings::NativeKeyboard::F9}, |
| 473 | case Qt::Key_9: | 473 | {Qt::Key_F10, Settings::NativeKeyboard::F10}, |
| 474 | return Settings::NativeKeyboard::N9; | 474 | {Qt::Key_F11, Settings::NativeKeyboard::F11}, |
| 475 | case Qt::Key_0: | 475 | {Qt::Key_F12, Settings::NativeKeyboard::F12}, |
| 476 | return Settings::NativeKeyboard::N0; | 476 | {Qt::Key_Print, Settings::NativeKeyboard::PrintScreen}, |
| 477 | case Qt::Key_Return: | 477 | {Qt::Key_ScrollLock, Settings::NativeKeyboard::ScrollLockKey}, |
| 478 | return Settings::NativeKeyboard::Return; | 478 | {Qt::Key_Pause, Settings::NativeKeyboard::Pause}, |
| 479 | case Qt::Key_Escape: | 479 | {Qt::Key_Insert, Settings::NativeKeyboard::Insert}, |
| 480 | return Settings::NativeKeyboard::Escape; | 480 | {Qt::Key_Home, Settings::NativeKeyboard::Home}, |
| 481 | case Qt::Key_Backspace: | 481 | {Qt::Key_PageUp, Settings::NativeKeyboard::PageUp}, |
| 482 | return Settings::NativeKeyboard::Backspace; | 482 | {Qt::Key_Delete, Settings::NativeKeyboard::Delete}, |
| 483 | case Qt::Key_Tab: | 483 | {Qt::Key_End, Settings::NativeKeyboard::End}, |
| 484 | return Settings::NativeKeyboard::Tab; | 484 | {Qt::Key_PageDown, Settings::NativeKeyboard::PageDown}, |
| 485 | case Qt::Key_Space: | 485 | {Qt::Key_Right, Settings::NativeKeyboard::Right}, |
| 486 | return Settings::NativeKeyboard::Space; | 486 | {Qt::Key_Left, Settings::NativeKeyboard::Left}, |
| 487 | case Qt::Key_Minus: | 487 | {Qt::Key_Down, Settings::NativeKeyboard::Down}, |
| 488 | return Settings::NativeKeyboard::Minus; | 488 | {Qt::Key_Up, Settings::NativeKeyboard::Up}, |
| 489 | case Qt::Key_Plus: | 489 | {Qt::Key_NumLock, Settings::NativeKeyboard::NumLockKey}, |
| 490 | case Qt::Key_questiondown: | 490 | // Numpad keys are missing here |
| 491 | return Settings::NativeKeyboard::Plus; | 491 | {Qt::Key_F13, Settings::NativeKeyboard::F13}, |
| 492 | case Qt::Key_BracketLeft: | 492 | {Qt::Key_F14, Settings::NativeKeyboard::F14}, |
| 493 | case Qt::Key_BraceLeft: | 493 | {Qt::Key_F15, Settings::NativeKeyboard::F15}, |
| 494 | return Settings::NativeKeyboard::OpenBracket; | 494 | {Qt::Key_F16, Settings::NativeKeyboard::F16}, |
| 495 | case Qt::Key_BracketRight: | 495 | {Qt::Key_F17, Settings::NativeKeyboard::F17}, |
| 496 | case Qt::Key_BraceRight: | 496 | {Qt::Key_F18, Settings::NativeKeyboard::F18}, |
| 497 | return Settings::NativeKeyboard::CloseBracket; | 497 | {Qt::Key_F19, Settings::NativeKeyboard::F19}, |
| 498 | case Qt::Key_Bar: | 498 | {Qt::Key_F20, Settings::NativeKeyboard::F20}, |
| 499 | return Settings::NativeKeyboard::Pipe; | 499 | {Qt::Key_F21, Settings::NativeKeyboard::F21}, |
| 500 | case Qt::Key_Dead_Tilde: | 500 | {Qt::Key_F22, Settings::NativeKeyboard::F22}, |
| 501 | return Settings::NativeKeyboard::Tilde; | 501 | {Qt::Key_F23, Settings::NativeKeyboard::F23}, |
| 502 | case Qt::Key_Ntilde: | 502 | {Qt::Key_F24, Settings::NativeKeyboard::F24}, |
| 503 | case Qt::Key_Semicolon: | 503 | // {Qt::..., Settings::NativeKeyboard::KPComma}, |
| 504 | return Settings::NativeKeyboard::Semicolon; | 504 | // {Qt::..., Settings::NativeKeyboard::Ro}, |
| 505 | case Qt::Key_Apostrophe: | 505 | {Qt::Key_Hiragana_Katakana, Settings::NativeKeyboard::KatakanaHiragana}, |
| 506 | return Settings::NativeKeyboard::Quote; | 506 | {Qt::Key_yen, Settings::NativeKeyboard::Yen}, |
| 507 | case Qt::Key_Dead_Grave: | 507 | {Qt::Key_Henkan, Settings::NativeKeyboard::Henkan}, |
| 508 | return Settings::NativeKeyboard::Backquote; | 508 | {Qt::Key_Muhenkan, Settings::NativeKeyboard::Muhenkan}, |
| 509 | case Qt::Key_Comma: | 509 | // {Qt::..., Settings::NativeKeyboard::NumPadCommaPc98}, |
| 510 | return Settings::NativeKeyboard::Comma; | 510 | {Qt::Key_Hangul, Settings::NativeKeyboard::HangulEnglish}, |
| 511 | case Qt::Key_Period: | 511 | {Qt::Key_Hangul_Hanja, Settings::NativeKeyboard::Hanja}, |
| 512 | return Settings::NativeKeyboard::Period; | 512 | {Qt::Key_Katakana, Settings::NativeKeyboard::KatakanaKey}, |
| 513 | case Qt::Key_Slash: | 513 | {Qt::Key_Hiragana, Settings::NativeKeyboard::HiraganaKey}, |
| 514 | return Settings::NativeKeyboard::Slash; | 514 | {Qt::Key_Zenkaku_Hankaku, Settings::NativeKeyboard::ZenkakuHankaku}, |
| 515 | case Qt::Key_CapsLock: | 515 | // Modifier keys are handled by the modifier property |
| 516 | return Settings::NativeKeyboard::CapsLock; | 516 | }; |
| 517 | case Qt::Key_F1: | 517 | |
| 518 | return Settings::NativeKeyboard::F1; | 518 | for (const auto& [qkey, nkey] : key_map) { |
| 519 | case Qt::Key_F2: | 519 | if (qt_key == qkey) { |
| 520 | return Settings::NativeKeyboard::F2; | 520 | return nkey; |
| 521 | case Qt::Key_F3: | 521 | } |
| 522 | return Settings::NativeKeyboard::F3; | ||
| 523 | case Qt::Key_F4: | ||
| 524 | return Settings::NativeKeyboard::F4; | ||
| 525 | case Qt::Key_F5: | ||
| 526 | return Settings::NativeKeyboard::F5; | ||
| 527 | case Qt::Key_F6: | ||
| 528 | return Settings::NativeKeyboard::F6; | ||
| 529 | case Qt::Key_F7: | ||
| 530 | return Settings::NativeKeyboard::F7; | ||
| 531 | case Qt::Key_F8: | ||
| 532 | return Settings::NativeKeyboard::F8; | ||
| 533 | case Qt::Key_F9: | ||
| 534 | return Settings::NativeKeyboard::F9; | ||
| 535 | case Qt::Key_F10: | ||
| 536 | return Settings::NativeKeyboard::F10; | ||
| 537 | case Qt::Key_F11: | ||
| 538 | return Settings::NativeKeyboard::F11; | ||
| 539 | case Qt::Key_F12: | ||
| 540 | return Settings::NativeKeyboard::F12; | ||
| 541 | case Qt::Key_Print: | ||
| 542 | return Settings::NativeKeyboard::PrintScreen; | ||
| 543 | case Qt::Key_ScrollLock: | ||
| 544 | return Settings::NativeKeyboard::ScrollLock; | ||
| 545 | case Qt::Key_Pause: | ||
| 546 | return Settings::NativeKeyboard::Pause; | ||
| 547 | case Qt::Key_Insert: | ||
| 548 | return Settings::NativeKeyboard::Insert; | ||
| 549 | case Qt::Key_Home: | ||
| 550 | return Settings::NativeKeyboard::Home; | ||
| 551 | case Qt::Key_PageUp: | ||
| 552 | return Settings::NativeKeyboard::PageUp; | ||
| 553 | case Qt::Key_Delete: | ||
| 554 | return Settings::NativeKeyboard::Delete; | ||
| 555 | case Qt::Key_End: | ||
| 556 | return Settings::NativeKeyboard::End; | ||
| 557 | case Qt::Key_PageDown: | ||
| 558 | return Settings::NativeKeyboard::PageDown; | ||
| 559 | case Qt::Key_Right: | ||
| 560 | return Settings::NativeKeyboard::Right; | ||
| 561 | case Qt::Key_Left: | ||
| 562 | return Settings::NativeKeyboard::Left; | ||
| 563 | case Qt::Key_Down: | ||
| 564 | return Settings::NativeKeyboard::Down; | ||
| 565 | case Qt::Key_Up: | ||
| 566 | return Settings::NativeKeyboard::Up; | ||
| 567 | case Qt::Key_NumLock: | ||
| 568 | return Settings::NativeKeyboard::NumLock; | ||
| 569 | // Numpad keys are missing here | ||
| 570 | case Qt::Key_F13: | ||
| 571 | return Settings::NativeKeyboard::F13; | ||
| 572 | case Qt::Key_F14: | ||
| 573 | return Settings::NativeKeyboard::F14; | ||
| 574 | case Qt::Key_F15: | ||
| 575 | return Settings::NativeKeyboard::F15; | ||
| 576 | case Qt::Key_F16: | ||
| 577 | return Settings::NativeKeyboard::F16; | ||
| 578 | case Qt::Key_F17: | ||
| 579 | return Settings::NativeKeyboard::F17; | ||
| 580 | case Qt::Key_F18: | ||
| 581 | return Settings::NativeKeyboard::F18; | ||
| 582 | case Qt::Key_F19: | ||
| 583 | return Settings::NativeKeyboard::F19; | ||
| 584 | case Qt::Key_F20: | ||
| 585 | return Settings::NativeKeyboard::F20; | ||
| 586 | case Qt::Key_F21: | ||
| 587 | return Settings::NativeKeyboard::F21; | ||
| 588 | case Qt::Key_F22: | ||
| 589 | return Settings::NativeKeyboard::F22; | ||
| 590 | case Qt::Key_F23: | ||
| 591 | return Settings::NativeKeyboard::F23; | ||
| 592 | case Qt::Key_F24: | ||
| 593 | return Settings::NativeKeyboard::F24; | ||
| 594 | // case Qt::: | ||
| 595 | // return Settings::NativeKeyboard::KPComma; | ||
| 596 | // case Qt::: | ||
| 597 | // return Settings::NativeKeyboard::Ro; | ||
| 598 | case Qt::Key_Hiragana_Katakana: | ||
| 599 | return Settings::NativeKeyboard::KatakanaHiragana; | ||
| 600 | case Qt::Key_yen: | ||
| 601 | return Settings::NativeKeyboard::Yen; | ||
| 602 | case Qt::Key_Henkan: | ||
| 603 | return Settings::NativeKeyboard::Henkan; | ||
| 604 | case Qt::Key_Muhenkan: | ||
| 605 | return Settings::NativeKeyboard::Muhenkan; | ||
| 606 | // case Qt::: | ||
| 607 | // return Settings::NativeKeyboard::NumPadCommaPc98; | ||
| 608 | case Qt::Key_Hangul: | ||
| 609 | return Settings::NativeKeyboard::HangulEnglish; | ||
| 610 | case Qt::Key_Hangul_Hanja: | ||
| 611 | return Settings::NativeKeyboard::Hanja; | ||
| 612 | case Qt::Key_Katakana: | ||
| 613 | return Settings::NativeKeyboard::KatakanaKey; | ||
| 614 | case Qt::Key_Hiragana: | ||
| 615 | return Settings::NativeKeyboard::HiraganaKey; | ||
| 616 | case Qt::Key_Zenkaku_Hankaku: | ||
| 617 | return Settings::NativeKeyboard::ZenkakuHankaku; | ||
| 618 | // Modifier keys are handled by the modifier property | ||
| 619 | default: | ||
| 620 | return Settings::NativeKeyboard::None; | ||
| 621 | } | 522 | } |
| 523 | |||
| 524 | return Settings::NativeKeyboard::None; | ||
| 622 | } | 525 | } |
| 623 | 526 | ||
| 624 | int GRenderWindow::QtModifierToSwitchModifier(Qt::KeyboardModifiers qt_modifiers) { | 527 | 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) | |||
| 15 | : QWizard(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), | 15 | : QWizard(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), |
| 16 | ui{std::make_unique<Ui::CompatDB>()}, telemetry_session{telemetry_session_} { | 16 | ui{std::make_unique<Ui::CompatDB>()}, telemetry_session{telemetry_session_} { |
| 17 | ui->setupUi(this); | 17 | ui->setupUi(this); |
| 18 | connect(ui->radioButton_Perfect, &QRadioButton::clicked, this, &CompatDB::EnableNext); | 18 | |
| 19 | connect(ui->radioButton_Great, &QRadioButton::clicked, this, &CompatDB::EnableNext); | 19 | connect(ui->radioButton_GameBoot_Yes, &QRadioButton::clicked, this, &CompatDB::EnableNext); |
| 20 | connect(ui->radioButton_Okay, &QRadioButton::clicked, this, &CompatDB::EnableNext); | 20 | connect(ui->radioButton_GameBoot_No, &QRadioButton::clicked, this, &CompatDB::EnableNext); |
| 21 | connect(ui->radioButton_Bad, &QRadioButton::clicked, this, &CompatDB::EnableNext); | 21 | connect(ui->radioButton_Gameplay_Yes, &QRadioButton::clicked, this, &CompatDB::EnableNext); |
| 22 | connect(ui->radioButton_IntroMenu, &QRadioButton::clicked, this, &CompatDB::EnableNext); | 22 | connect(ui->radioButton_Gameplay_No, &QRadioButton::clicked, this, &CompatDB::EnableNext); |
| 23 | connect(ui->radioButton_WontBoot, &QRadioButton::clicked, this, &CompatDB::EnableNext); | 23 | connect(ui->radioButton_NoFreeze_Yes, &QRadioButton::clicked, this, &CompatDB::EnableNext); |
| 24 | connect(ui->radioButton_NoFreeze_No, &QRadioButton::clicked, this, &CompatDB::EnableNext); | ||
| 25 | connect(ui->radioButton_Complete_Yes, &QRadioButton::clicked, this, &CompatDB::EnableNext); | ||
| 26 | connect(ui->radioButton_Complete_No, &QRadioButton::clicked, this, &CompatDB::EnableNext); | ||
| 27 | connect(ui->radioButton_Graphical_Major, &QRadioButton::clicked, this, &CompatDB::EnableNext); | ||
| 28 | connect(ui->radioButton_Graphical_Minor, &QRadioButton::clicked, this, &CompatDB::EnableNext); | ||
| 29 | connect(ui->radioButton_Graphical_No, &QRadioButton::clicked, this, &CompatDB::EnableNext); | ||
| 30 | connect(ui->radioButton_Audio_Major, &QRadioButton::clicked, this, &CompatDB::EnableNext); | ||
| 31 | connect(ui->radioButton_Audio_Minor, &QRadioButton::clicked, this, &CompatDB::EnableNext); | ||
| 32 | connect(ui->radioButton_Audio_No, &QRadioButton::clicked, this, &CompatDB::EnableNext); | ||
| 33 | |||
| 24 | connect(button(NextButton), &QPushButton::clicked, this, &CompatDB::Submit); | 34 | connect(button(NextButton), &QPushButton::clicked, this, &CompatDB::Submit); |
| 25 | connect(&testcase_watcher, &QFutureWatcher<bool>::finished, this, | 35 | connect(&testcase_watcher, &QFutureWatcher<bool>::finished, this, |
| 26 | &CompatDB::OnTestcaseSubmitted); | 36 | &CompatDB::OnTestcaseSubmitted); |
| @@ -30,29 +40,82 @@ CompatDB::~CompatDB() = default; | |||
| 30 | 40 | ||
| 31 | enum class CompatDBPage { | 41 | enum class CompatDBPage { |
| 32 | Intro = 0, | 42 | Intro = 0, |
| 33 | Selection = 1, | 43 | GameBoot = 1, |
| 34 | Final = 2, | 44 | GamePlay = 2, |
| 45 | Freeze = 3, | ||
| 46 | Completion = 4, | ||
| 47 | Graphical = 5, | ||
| 48 | Audio = 6, | ||
| 49 | Final = 7, | ||
| 35 | }; | 50 | }; |
| 36 | 51 | ||
| 37 | void CompatDB::Submit() { | 52 | void CompatDB::Submit() { |
| 38 | QButtonGroup* compatibility = new QButtonGroup(this); | 53 | QButtonGroup* compatibility_GameBoot = new QButtonGroup(this); |
| 39 | compatibility->addButton(ui->radioButton_Perfect, 0); | 54 | compatibility_GameBoot->addButton(ui->radioButton_GameBoot_Yes, 0); |
| 40 | compatibility->addButton(ui->radioButton_Great, 1); | 55 | compatibility_GameBoot->addButton(ui->radioButton_GameBoot_No, 1); |
| 41 | compatibility->addButton(ui->radioButton_Okay, 2); | 56 | |
| 42 | compatibility->addButton(ui->radioButton_Bad, 3); | 57 | QButtonGroup* compatibility_Gameplay = new QButtonGroup(this); |
| 43 | compatibility->addButton(ui->radioButton_IntroMenu, 4); | 58 | compatibility_Gameplay->addButton(ui->radioButton_Gameplay_Yes, 0); |
| 44 | compatibility->addButton(ui->radioButton_WontBoot, 5); | 59 | compatibility_Gameplay->addButton(ui->radioButton_Gameplay_No, 1); |
| 60 | |||
| 61 | QButtonGroup* compatibility_NoFreeze = new QButtonGroup(this); | ||
| 62 | compatibility_NoFreeze->addButton(ui->radioButton_NoFreeze_Yes, 0); | ||
| 63 | compatibility_NoFreeze->addButton(ui->radioButton_NoFreeze_No, 1); | ||
| 64 | |||
| 65 | QButtonGroup* compatibility_Complete = new QButtonGroup(this); | ||
| 66 | compatibility_Complete->addButton(ui->radioButton_Complete_Yes, 0); | ||
| 67 | compatibility_Complete->addButton(ui->radioButton_Complete_No, 1); | ||
| 68 | |||
| 69 | QButtonGroup* compatibility_Graphical = new QButtonGroup(this); | ||
| 70 | compatibility_Graphical->addButton(ui->radioButton_Graphical_Major, 0); | ||
| 71 | compatibility_Graphical->addButton(ui->radioButton_Graphical_Minor, 1); | ||
| 72 | compatibility_Graphical->addButton(ui->radioButton_Graphical_No, 2); | ||
| 73 | |||
| 74 | QButtonGroup* compatibility_Audio = new QButtonGroup(this); | ||
| 75 | compatibility_Audio->addButton(ui->radioButton_Audio_Major, 0); | ||
| 76 | compatibility_Graphical->addButton(ui->radioButton_Audio_Minor, 1); | ||
| 77 | compatibility_Audio->addButton(ui->radioButton_Audio_No, 2); | ||
| 78 | |||
| 79 | const int compatiblity = static_cast<int>(CalculateCompatibility()); | ||
| 80 | |||
| 45 | switch ((static_cast<CompatDBPage>(currentId()))) { | 81 | switch ((static_cast<CompatDBPage>(currentId()))) { |
| 46 | case CompatDBPage::Selection: | 82 | case CompatDBPage::Intro: |
| 47 | if (compatibility->checkedId() == -1) { | 83 | break; |
| 84 | case CompatDBPage::GameBoot: | ||
| 85 | if (compatibility_GameBoot->checkedId() == -1) { | ||
| 86 | button(NextButton)->setEnabled(false); | ||
| 87 | } | ||
| 88 | break; | ||
| 89 | case CompatDBPage::GamePlay: | ||
| 90 | if (compatibility_Gameplay->checkedId() == -1) { | ||
| 91 | button(NextButton)->setEnabled(false); | ||
| 92 | } | ||
| 93 | break; | ||
| 94 | case CompatDBPage::Freeze: | ||
| 95 | if (compatibility_NoFreeze->checkedId() == -1) { | ||
| 96 | button(NextButton)->setEnabled(false); | ||
| 97 | } | ||
| 98 | break; | ||
| 99 | case CompatDBPage::Completion: | ||
| 100 | if (compatibility_Complete->checkedId() == -1) { | ||
| 101 | button(NextButton)->setEnabled(false); | ||
| 102 | } | ||
| 103 | break; | ||
| 104 | case CompatDBPage::Graphical: | ||
| 105 | if (compatibility_Graphical->checkedId() == -1) { | ||
| 106 | button(NextButton)->setEnabled(false); | ||
| 107 | } | ||
| 108 | break; | ||
| 109 | case CompatDBPage::Audio: | ||
| 110 | if (compatibility_Audio->checkedId() == -1) { | ||
| 48 | button(NextButton)->setEnabled(false); | 111 | button(NextButton)->setEnabled(false); |
| 49 | } | 112 | } |
| 50 | break; | 113 | break; |
| 51 | case CompatDBPage::Final: | 114 | case CompatDBPage::Final: |
| 52 | back(); | 115 | back(); |
| 53 | LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId()); | 116 | LOG_INFO(Frontend, "Compatibility Rating: {}", compatiblity); |
| 54 | telemetry_session.AddField(Common::Telemetry::FieldType::UserFeedback, "Compatibility", | 117 | telemetry_session.AddField(Common::Telemetry::FieldType::UserFeedback, "Compatibility", |
| 55 | compatibility->checkedId()); | 118 | compatiblity); |
| 56 | 119 | ||
| 57 | button(NextButton)->setEnabled(false); | 120 | button(NextButton)->setEnabled(false); |
| 58 | button(NextButton)->setText(tr("Submitting")); | 121 | button(NextButton)->setText(tr("Submitting")); |
| @@ -63,9 +126,70 @@ void CompatDB::Submit() { | |||
| 63 | break; | 126 | break; |
| 64 | default: | 127 | default: |
| 65 | LOG_ERROR(Frontend, "Unexpected page: {}", currentId()); | 128 | LOG_ERROR(Frontend, "Unexpected page: {}", currentId()); |
| 129 | break; | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | int CompatDB::nextId() const { | ||
| 134 | switch ((static_cast<CompatDBPage>(currentId()))) { | ||
| 135 | case CompatDBPage::Intro: | ||
| 136 | return static_cast<int>(CompatDBPage::GameBoot); | ||
| 137 | case CompatDBPage::GameBoot: | ||
| 138 | if (ui->radioButton_GameBoot_No->isChecked()) { | ||
| 139 | return static_cast<int>(CompatDBPage::Final); | ||
| 140 | } | ||
| 141 | return static_cast<int>(CompatDBPage::GamePlay); | ||
| 142 | case CompatDBPage::GamePlay: | ||
| 143 | if (ui->radioButton_Gameplay_No->isChecked()) { | ||
| 144 | return static_cast<int>(CompatDBPage::Final); | ||
| 145 | } | ||
| 146 | return static_cast<int>(CompatDBPage::Freeze); | ||
| 147 | case CompatDBPage::Freeze: | ||
| 148 | if (ui->radioButton_NoFreeze_No->isChecked()) { | ||
| 149 | return static_cast<int>(CompatDBPage::Final); | ||
| 150 | } | ||
| 151 | return static_cast<int>(CompatDBPage::Completion); | ||
| 152 | case CompatDBPage::Completion: | ||
| 153 | if (ui->radioButton_Complete_No->isChecked()) { | ||
| 154 | return static_cast<int>(CompatDBPage::Final); | ||
| 155 | } | ||
| 156 | return static_cast<int>(CompatDBPage::Graphical); | ||
| 157 | case CompatDBPage::Graphical: | ||
| 158 | return static_cast<int>(CompatDBPage::Audio); | ||
| 159 | case CompatDBPage::Audio: | ||
| 160 | return static_cast<int>(CompatDBPage::Final); | ||
| 161 | case CompatDBPage::Final: | ||
| 162 | return -1; | ||
| 163 | default: | ||
| 164 | LOG_ERROR(Frontend, "Unexpected page: {}", currentId()); | ||
| 165 | return static_cast<int>(CompatDBPage::Intro); | ||
| 66 | } | 166 | } |
| 67 | } | 167 | } |
| 68 | 168 | ||
| 169 | CompatibilityStatus CompatDB::CalculateCompatibility() const { | ||
| 170 | if (ui->radioButton_GameBoot_No->isChecked()) { | ||
| 171 | return CompatibilityStatus::WontBoot; | ||
| 172 | } | ||
| 173 | |||
| 174 | if (ui->radioButton_Gameplay_No->isChecked()) { | ||
| 175 | return CompatibilityStatus::IntroMenu; | ||
| 176 | } | ||
| 177 | |||
| 178 | if (ui->radioButton_NoFreeze_No->isChecked() || ui->radioButton_Complete_No->isChecked()) { | ||
| 179 | return CompatibilityStatus::Ingame; | ||
| 180 | } | ||
| 181 | |||
| 182 | if (ui->radioButton_Graphical_Major->isChecked() || ui->radioButton_Audio_Major->isChecked()) { | ||
| 183 | return CompatibilityStatus::Ingame; | ||
| 184 | } | ||
| 185 | |||
| 186 | if (ui->radioButton_Graphical_Minor->isChecked() || ui->radioButton_Audio_Minor->isChecked()) { | ||
| 187 | return CompatibilityStatus::Playable; | ||
| 188 | } | ||
| 189 | |||
| 190 | return CompatibilityStatus::Perfect; | ||
| 191 | } | ||
| 192 | |||
| 69 | void CompatDB::OnTestcaseSubmitted() { | 193 | void CompatDB::OnTestcaseSubmitted() { |
| 70 | if (!testcase_watcher.result()) { | 194 | if (!testcase_watcher.result()) { |
| 71 | QMessageBox::critical(this, tr("Communication error"), | 195 | QMessageBox::critical(this, tr("Communication error"), |
diff --git a/src/yuzu/compatdb.h b/src/yuzu/compatdb.h index 3252fc47a..37e11278b 100644 --- a/src/yuzu/compatdb.h +++ b/src/yuzu/compatdb.h | |||
| @@ -12,12 +12,22 @@ namespace Ui { | |||
| 12 | class CompatDB; | 12 | class CompatDB; |
| 13 | } | 13 | } |
| 14 | 14 | ||
| 15 | enum class CompatibilityStatus { | ||
| 16 | Perfect = 0, | ||
| 17 | Playable = 1, | ||
| 18 | // Unused: Okay = 2, | ||
| 19 | Ingame = 3, | ||
| 20 | IntroMenu = 4, | ||
| 21 | WontBoot = 5, | ||
| 22 | }; | ||
| 23 | |||
| 15 | class CompatDB : public QWizard { | 24 | class CompatDB : public QWizard { |
| 16 | Q_OBJECT | 25 | Q_OBJECT |
| 17 | 26 | ||
| 18 | public: | 27 | public: |
| 19 | explicit CompatDB(Core::TelemetrySession& telemetry_session_, QWidget* parent = nullptr); | 28 | explicit CompatDB(Core::TelemetrySession& telemetry_session_, QWidget* parent = nullptr); |
| 20 | ~CompatDB(); | 29 | ~CompatDB(); |
| 30 | int nextId() const override; | ||
| 21 | 31 | ||
| 22 | private: | 32 | private: |
| 23 | QFutureWatcher<bool> testcase_watcher; | 33 | QFutureWatcher<bool> testcase_watcher; |
| @@ -25,6 +35,7 @@ private: | |||
| 25 | std::unique_ptr<Ui::CompatDB> ui; | 35 | std::unique_ptr<Ui::CompatDB> ui; |
| 26 | 36 | ||
| 27 | void Submit(); | 37 | void Submit(); |
| 38 | CompatibilityStatus CalculateCompatibility() const; | ||
| 28 | void OnTestcaseSubmitted(); | 39 | void OnTestcaseSubmitted(); |
| 29 | void EnableNext(); | 40 | void EnableNext(); |
| 30 | 41 | ||
diff --git a/src/yuzu/compatdb.ui b/src/yuzu/compatdb.ui index 3ca55eda6..d11669df2 100644 --- a/src/yuzu/compatdb.ui +++ b/src/yuzu/compatdb.ui | |||
| @@ -58,128 +58,311 @@ | |||
| 58 | </item> | 58 | </item> |
| 59 | </layout> | 59 | </layout> |
| 60 | </widget> | 60 | </widget> |
| 61 | <widget class="QWizardPage" name="wizard_Report"> | 61 | <widget class="QWizardPage" name="wizard_GameBoot"> |
| 62 | <property name="title"> | 62 | <property name="title"> |
| 63 | <string>Report Game Compatibility</string> | 63 | <string>Report Game Compatibility</string> |
| 64 | </property> | 64 | </property> |
| 65 | <attribute name="pageId"> | 65 | <attribute name="pageId"> |
| 66 | <string notr="true">1</string> | 66 | <string notr="true">1</string> |
| 67 | </attribute> | 67 | </attribute> |
| 68 | <layout class="QFormLayout" name="formLayout"> | 68 | <layout class="QFormLayout" name="formLayout1"> |
| 69 | <item row="0" column="0" colspan="2"> | ||
| 70 | <widget class="QLabel" name="lbl_Independent1"> | ||
| 71 | <property name="font"> | ||
| 72 | <font> | ||
| 73 | <pointsize>10</pointsize> | ||
| 74 | </font> | ||
| 75 | </property> | ||
| 76 | <property name="text"> | ||
| 77 | <string><html><head/><body><p>Does the game boot?</p></body></html></string> | ||
| 78 | </property> | ||
| 79 | <property name="wordWrap"> | ||
| 80 | <bool>true</bool> | ||
| 81 | </property> | ||
| 82 | </widget> | ||
| 83 | </item> | ||
| 84 | <item row="1" column="0" colspan="2"> | ||
| 85 | <spacer name="verticalSpacer1"> | ||
| 86 | <property name="orientation"> | ||
| 87 | <enum>Qt::Vertical</enum> | ||
| 88 | </property> | ||
| 89 | <property name="sizeHint" stdset="0"> | ||
| 90 | <size> | ||
| 91 | <width>20</width> | ||
| 92 | <height>0</height> | ||
| 93 | </size> | ||
| 94 | </property> | ||
| 95 | </spacer> | ||
| 96 | </item> | ||
| 69 | <item row="2" column="0"> | 97 | <item row="2" column="0"> |
| 70 | <widget class="QRadioButton" name="radioButton_Perfect"> | 98 | <widget class="QRadioButton" name="radioButton_GameBoot_Yes"> |
| 71 | <property name="text"> | 99 | <property name="text"> |
| 72 | <string>Perfect</string> | 100 | <string>Yes The game starts to output video or audio</string> |
| 73 | </property> | 101 | </property> |
| 74 | </widget> | 102 | </widget> |
| 75 | </item> | 103 | </item> |
| 76 | <item row="2" column="1"> | 104 | <item row="4" column="0"> |
| 77 | <widget class="QLabel" name="lbl_Perfect"> | 105 | <widget class="QRadioButton" name="radioButton_GameBoot_No"> |
| 78 | <property name="text"> | 106 | <property name="text"> |
| 79 | <string><html><head/><body><p>Game functions flawlessly with no audio or graphical glitches.</p></body></html></string> | 107 | <string>No The game doesn't get past the "Launching..." screen</string> |
| 80 | </property> | 108 | </property> |
| 81 | <property name="wordWrap"> | 109 | </widget> |
| 82 | <bool>true</bool> | 110 | </item> |
| 111 | </layout> | ||
| 112 | </widget> | ||
| 113 | <widget class="QWizardPage" name="wizard_GamePlay"> | ||
| 114 | <property name="title"> | ||
| 115 | <string>Report Game Compatibility</string> | ||
| 116 | </property> | ||
| 117 | <attribute name="pageId"> | ||
| 118 | <string notr="true">2</string> | ||
| 119 | </attribute> | ||
| 120 | <layout class="QFormLayout" name="formLayout2"> | ||
| 121 | <item row="2" column="0"> | ||
| 122 | <widget class="QRadioButton" name="radioButton_Gameplay_Yes"> | ||
| 123 | <property name="text"> | ||
| 124 | <string>Yes The game gets past the intro/menu and into gameplay</string> | ||
| 83 | </property> | 125 | </property> |
| 84 | </widget> | 126 | </widget> |
| 85 | </item> | 127 | </item> |
| 86 | <item row="4" column="0"> | 128 | <item row="4" column="0"> |
| 87 | <widget class="QRadioButton" name="radioButton_Great"> | 129 | <widget class="QRadioButton" name="radioButton_Gameplay_No"> |
| 88 | <property name="text"> | 130 | <property name="text"> |
| 89 | <string>Great</string> | 131 | <string>No The game crashes or freezes while loading or using the menu</string> |
| 90 | </property> | 132 | </property> |
| 91 | </widget> | 133 | </widget> |
| 92 | </item> | 134 | </item> |
| 93 | <item row="4" column="1"> | 135 | <item row="0" column="0" colspan="2"> |
| 94 | <widget class="QLabel" name="lbl_Great"> | 136 | <widget class="QLabel" name="lbl_Independent2"> |
| 137 | <property name="font"> | ||
| 138 | <font> | ||
| 139 | <pointsize>10</pointsize> | ||
| 140 | </font> | ||
| 141 | </property> | ||
| 95 | <property name="text"> | 142 | <property name="text"> |
| 96 | <string><html><head/><body><p>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.</p></body></html></string> | 143 | <string><html><head/><body><p>Does the game reach gameplay?</p></body></html></string> |
| 97 | </property> | 144 | </property> |
| 98 | <property name="wordWrap"> | 145 | <property name="wordWrap"> |
| 99 | <bool>true</bool> | 146 | <bool>true</bool> |
| 100 | </property> | 147 | </property> |
| 101 | </widget> | 148 | </widget> |
| 102 | </item> | 149 | </item> |
| 103 | <item row="5" column="0"> | 150 | <item row="1" column="0" colspan="2"> |
| 104 | <widget class="QRadioButton" name="radioButton_Okay"> | 151 | <spacer name="verticalSpacer2"> |
| 152 | <property name="orientation"> | ||
| 153 | <enum>Qt::Vertical</enum> | ||
| 154 | </property> | ||
| 155 | <property name="sizeHint" stdset="0"> | ||
| 156 | <size> | ||
| 157 | <width>20</width> | ||
| 158 | <height>0</height> | ||
| 159 | </size> | ||
| 160 | </property> | ||
| 161 | </spacer> | ||
| 162 | </item> | ||
| 163 | </layout> | ||
| 164 | </widget> | ||
| 165 | <widget class="QWizardPage" name="wizard_NoFreeze"> | ||
| 166 | <property name="title"> | ||
| 167 | <string>Report Game Compatibility</string> | ||
| 168 | </property> | ||
| 169 | <attribute name="pageId"> | ||
| 170 | <string notr="true">3</string> | ||
| 171 | </attribute> | ||
| 172 | <layout class="QFormLayout" name="formLayout3"> | ||
| 173 | <item row="2" column="0"> | ||
| 174 | <widget class="QRadioButton" name="radioButton_NoFreeze_Yes"> | ||
| 105 | <property name="text"> | 175 | <property name="text"> |
| 106 | <string>Okay</string> | 176 | <string>Yes The game works without crashes</string> |
| 107 | </property> | 177 | </property> |
| 108 | </widget> | 178 | </widget> |
| 109 | </item> | 179 | </item> |
| 110 | <item row="5" column="1"> | 180 | <item row="4" column="0"> |
| 111 | <widget class="QLabel" name="lbl_Okay"> | 181 | <widget class="QRadioButton" name="radioButton_NoFreeze_No"> |
| 182 | <property name="text"> | ||
| 183 | <string>No The game crashes or freezes during gameplay</string> | ||
| 184 | </property> | ||
| 185 | </widget> | ||
| 186 | </item> | ||
| 187 | <item row="0" column="0" colspan="2"> | ||
| 188 | <widget class="QLabel" name="lbl_Independent3"> | ||
| 189 | <property name="font"> | ||
| 190 | <font> | ||
| 191 | <pointsize>10</pointsize> | ||
| 192 | </font> | ||
| 193 | </property> | ||
| 112 | <property name="text"> | 194 | <property name="text"> |
| 113 | <string><html><head/><body><p>Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.</p></body></html></string> | 195 | <string><html><head/><body><p>Does the game work without crashing, freezing or locking up during gameplay?</p></body></html></string> |
| 114 | </property> | 196 | </property> |
| 115 | <property name="wordWrap"> | 197 | <property name="wordWrap"> |
| 116 | <bool>true</bool> | 198 | <bool>true</bool> |
| 117 | </property> | 199 | </property> |
| 118 | </widget> | 200 | </widget> |
| 119 | </item> | 201 | </item> |
| 120 | <item row="6" column="0"> | 202 | <item row="1" column="0" colspan="2"> |
| 121 | <widget class="QRadioButton" name="radioButton_Bad"> | 203 | <spacer name="verticalSpacer3"> |
| 204 | <property name="orientation"> | ||
| 205 | <enum>Qt::Vertical</enum> | ||
| 206 | </property> | ||
| 207 | <property name="sizeHint" stdset="0"> | ||
| 208 | <size> | ||
| 209 | <width>20</width> | ||
| 210 | <height>0</height> | ||
| 211 | </size> | ||
| 212 | </property> | ||
| 213 | </spacer> | ||
| 214 | </item> | ||
| 215 | </layout> | ||
| 216 | </widget> | ||
| 217 | <widget class="QWizardPage" name="wizard_Complete"> | ||
| 218 | <property name="title"> | ||
| 219 | <string>Report Game Compatibility</string> | ||
| 220 | </property> | ||
| 221 | <attribute name="pageId"> | ||
| 222 | <string notr="true">4</string> | ||
| 223 | </attribute> | ||
| 224 | <layout class="QFormLayout" name="formLayout4"> | ||
| 225 | <item row="2" column="0"> | ||
| 226 | <widget class="QRadioButton" name="radioButton_Complete_Yes"> | ||
| 122 | <property name="text"> | 227 | <property name="text"> |
| 123 | <string>Bad</string> | 228 | <string>Yes The game can be finished without any workarounds</string> |
| 124 | </property> | 229 | </property> |
| 125 | </widget> | 230 | </widget> |
| 126 | </item> | 231 | </item> |
| 127 | <item row="6" column="1"> | 232 | <item row="4" column="0"> |
| 128 | <widget class="QLabel" name="lbl_Bad"> | 233 | <widget class="QRadioButton" name="radioButton_Complete_No"> |
| 234 | <property name="text"> | ||
| 235 | <string>No The game can't progress past a certain area</string> | ||
| 236 | </property> | ||
| 237 | </widget> | ||
| 238 | </item> | ||
| 239 | <item row="0" column="0" colspan="2"> | ||
| 240 | <widget class="QLabel" name="lbl_Independent4"> | ||
| 241 | <property name="font"> | ||
| 242 | <font> | ||
| 243 | <pointsize>10</pointsize> | ||
| 244 | </font> | ||
| 245 | </property> | ||
| 129 | <property name="text"> | 246 | <property name="text"> |
| 130 | <string><html><head/><body><p>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.</p></body></html></string> | 247 | <string><html><head/><body><p>Is the game completely playable from start to finish?</p></body></html></string> |
| 131 | </property> | 248 | </property> |
| 132 | <property name="wordWrap"> | 249 | <property name="wordWrap"> |
| 133 | <bool>true</bool> | 250 | <bool>true</bool> |
| 134 | </property> | 251 | </property> |
| 135 | </widget> | 252 | </widget> |
| 136 | </item> | 253 | </item> |
| 137 | <item row="7" column="0"> | 254 | <item row="1" column="0" colspan="2"> |
| 138 | <widget class="QRadioButton" name="radioButton_IntroMenu"> | 255 | <spacer name="verticalSpacer4"> |
| 256 | <property name="orientation"> | ||
| 257 | <enum>Qt::Vertical</enum> | ||
| 258 | </property> | ||
| 259 | <property name="sizeHint" stdset="0"> | ||
| 260 | <size> | ||
| 261 | <width>20</width> | ||
| 262 | <height>0</height> | ||
| 263 | </size> | ||
| 264 | </property> | ||
| 265 | </spacer> | ||
| 266 | </item> | ||
| 267 | </layout> | ||
| 268 | </widget> | ||
| 269 | <widget class="QWizardPage" name="wizard_Graphical"> | ||
| 270 | <property name="title"> | ||
| 271 | <string>Report Game Compatibility</string> | ||
| 272 | </property> | ||
| 273 | <attribute name="pageId"> | ||
| 274 | <string notr="true">5</string> | ||
| 275 | </attribute> | ||
| 276 | <layout class="QFormLayout" name="formLayout5"> | ||
| 277 | <item row="2" column="0"> | ||
| 278 | <widget class="QRadioButton" name="radioButton_Graphical_Major"> | ||
| 279 | <property name="text"> | ||
| 280 | <string>Major The game has major graphical errors</string> | ||
| 281 | </property> | ||
| 282 | </widget> | ||
| 283 | </item> | ||
| 284 | <item row="4" column="0"> | ||
| 285 | <widget class="QRadioButton" name="radioButton_Graphical_Minor"> | ||
| 139 | <property name="text"> | 286 | <property name="text"> |
| 140 | <string>Intro/Menu</string> | 287 | <string>Minor The game has minor graphical errors</string> |
| 141 | </property> | 288 | </property> |
| 142 | </widget> | 289 | </widget> |
| 143 | </item> | 290 | </item> |
| 144 | <item row="7" column="1"> | 291 | <item row="6" column="0"> |
| 145 | <widget class="QLabel" name="lbl_IntroMenu"> | 292 | <widget class="QRadioButton" name="radioButton_Graphical_No"> |
| 293 | <property name="text"> | ||
| 294 | <string>None Everything is rendered as it looks on the Nintendo Switch</string> | ||
| 295 | </property> | ||
| 296 | </widget> | ||
| 297 | </item> | ||
| 298 | <item row="0" column="0" colspan="2"> | ||
| 299 | <widget class="QLabel" name="lbl_Independent5"> | ||
| 300 | <property name="font"> | ||
| 301 | <font> | ||
| 302 | <pointsize>10</pointsize> | ||
| 303 | </font> | ||
| 304 | </property> | ||
| 146 | <property name="text"> | 305 | <property name="text"> |
| 147 | <string><html><head/><body><p>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.</p></body></html></string> | 306 | <string><html><head/><body><p>Does the game have any graphical glitches?</p></body></html></string> |
| 148 | </property> | 307 | </property> |
| 149 | <property name="wordWrap"> | 308 | <property name="wordWrap"> |
| 150 | <bool>true</bool> | 309 | <bool>true</bool> |
| 151 | </property> | 310 | </property> |
| 152 | </widget> | 311 | </widget> |
| 153 | </item> | 312 | </item> |
| 154 | <item row="8" column="0"> | 313 | <item row="1" column="0" colspan="2"> |
| 155 | <widget class="QRadioButton" name="radioButton_WontBoot"> | 314 | <spacer name="verticalSpacer5"> |
| 156 | <property name="text"> | 315 | <property name="orientation"> |
| 157 | <string>Won't Boot</string> | 316 | <enum>Qt::Vertical</enum> |
| 158 | </property> | 317 | </property> |
| 159 | <property name="checkable"> | 318 | <property name="sizeHint" stdset="0"> |
| 160 | <bool>true</bool> | 319 | <size> |
| 320 | <width>20</width> | ||
| 321 | <height>0</height> | ||
| 322 | </size> | ||
| 161 | </property> | 323 | </property> |
| 162 | <property name="checked"> | 324 | </spacer> |
| 163 | <bool>false</bool> | 325 | </item> |
| 326 | </layout> | ||
| 327 | </widget> | ||
| 328 | <widget class="QWizardPage" name="wizard_Audio"> | ||
| 329 | <property name="title"> | ||
| 330 | <string>Report Game Compatibility</string> | ||
| 331 | </property> | ||
| 332 | <attribute name="pageId"> | ||
| 333 | <string notr="true">6</string> | ||
| 334 | </attribute> | ||
| 335 | <layout class="QFormLayout" name="formLayout6"> | ||
| 336 | <item row="2" column="0"> | ||
| 337 | <widget class="QRadioButton" name="radioButton_Audio_Major"> | ||
| 338 | <property name="text"> | ||
| 339 | <string>Major The game has major audio errors</string> | ||
| 340 | </property> | ||
| 341 | </widget> | ||
| 342 | </item> | ||
| 343 | <item row="4" column="0"> | ||
| 344 | <widget class="QRadioButton" name="radioButton_Audio_Minor"> | ||
| 345 | <property name="text"> | ||
| 346 | <string>Minor The game has minor audio errors</string> | ||
| 164 | </property> | 347 | </property> |
| 165 | </widget> | 348 | </widget> |
| 166 | </item> | 349 | </item> |
| 167 | <item row="8" column="1"> | 350 | <item row="6" column="0"> |
| 168 | <widget class="QLabel" name="lbl_WontBoot"> | 351 | <widget class="QRadioButton" name="radioButton_Audio_No"> |
| 169 | <property name="text"> | 352 | <property name="text"> |
| 170 | <string><html><head/><body><p>The game crashes when attempting to startup.</p></body></html></string> | 353 | <string>None Audio is played perfectly</string> |
| 171 | </property> | 354 | </property> |
| 172 | </widget> | 355 | </widget> |
| 173 | </item> | 356 | </item> |
| 174 | <item row="0" column="0" colspan="2"> | 357 | <item row="0" column="0" colspan="2"> |
| 175 | <widget class="QLabel" name="lbl_Independent"> | 358 | <widget class="QLabel" name="lbl_Independent6"> |
| 176 | <property name="font"> | 359 | <property name="font"> |
| 177 | <font> | 360 | <font> |
| 178 | <pointsize>10</pointsize> | 361 | <pointsize>10</pointsize> |
| 179 | </font> | 362 | </font> |
| 180 | </property> | 363 | </property> |
| 181 | <property name="text"> | 364 | <property name="text"> |
| 182 | <string><html><head/><body><p>Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?</p></body></html></string> | 365 | <string><html><head/><body><p>Does the game have any audio glitches / missing effects?</p></body></html></string> |
| 183 | </property> | 366 | </property> |
| 184 | <property name="wordWrap"> | 367 | <property name="wordWrap"> |
| 185 | <bool>true</bool> | 368 | <bool>true</bool> |
| @@ -187,7 +370,7 @@ | |||
| 187 | </widget> | 370 | </widget> |
| 188 | </item> | 371 | </item> |
| 189 | <item row="1" column="0" colspan="2"> | 372 | <item row="1" column="0" colspan="2"> |
| 190 | <spacer name="verticalSpacer"> | 373 | <spacer name="verticalSpacer6"> |
| 191 | <property name="orientation"> | 374 | <property name="orientation"> |
| 192 | <enum>Qt::Vertical</enum> | 375 | <enum>Qt::Vertical</enum> |
| 193 | </property> | 376 | </property> |
| @@ -206,7 +389,7 @@ | |||
| 206 | <string>Thank you for your submission!</string> | 389 | <string>Thank you for your submission!</string> |
| 207 | </property> | 390 | </property> |
| 208 | <attribute name="pageId"> | 391 | <attribute name="pageId"> |
| 209 | <string notr="true">2</string> | 392 | <string notr="true">7</string> |
| 210 | </attribute> | 393 | </attribute> |
| 211 | </widget> | 394 | </widget> |
| 212 | </widget> | 395 | </widget> |
diff --git a/src/yuzu/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp index 5c0217ba8..a47089988 100644 --- a/src/yuzu/configuration/configure_profile_manager.cpp +++ b/src/yuzu/configuration/configure_profile_manager.cpp | |||
| @@ -2,6 +2,9 @@ | |||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <algorithm> | 4 | #include <algorithm> |
| 5 | #include <functional> | ||
| 6 | #include <QDialog> | ||
| 7 | #include <QDialogButtonBox> | ||
| 5 | #include <QFileDialog> | 8 | #include <QFileDialog> |
| 6 | #include <QGraphicsItem> | 9 | #include <QGraphicsItem> |
| 7 | #include <QHeaderView> | 10 | #include <QHeaderView> |
| @@ -108,9 +111,12 @@ ConfigureProfileManager::ConfigureProfileManager(const Core::System& system_, QW | |||
| 108 | 111 | ||
| 109 | connect(ui->pm_add, &QPushButton::clicked, this, &ConfigureProfileManager::AddUser); | 112 | connect(ui->pm_add, &QPushButton::clicked, this, &ConfigureProfileManager::AddUser); |
| 110 | connect(ui->pm_rename, &QPushButton::clicked, this, &ConfigureProfileManager::RenameUser); | 113 | connect(ui->pm_rename, &QPushButton::clicked, this, &ConfigureProfileManager::RenameUser); |
| 111 | connect(ui->pm_remove, &QPushButton::clicked, this, &ConfigureProfileManager::DeleteUser); | 114 | connect(ui->pm_remove, &QPushButton::clicked, this, |
| 115 | &ConfigureProfileManager::ConfirmDeleteUser); | ||
| 112 | connect(ui->pm_set_image, &QPushButton::clicked, this, &ConfigureProfileManager::SetUserImage); | 116 | connect(ui->pm_set_image, &QPushButton::clicked, this, &ConfigureProfileManager::SetUserImage); |
| 113 | 117 | ||
| 118 | confirm_dialog = new ConfigureProfileManagerDeleteDialog(this); | ||
| 119 | |||
| 114 | scene = new QGraphicsScene; | 120 | scene = new QGraphicsScene; |
| 115 | ui->current_user_icon->setScene(scene); | 121 | ui->current_user_icon->setScene(scene); |
| 116 | 122 | ||
| @@ -230,26 +236,23 @@ void ConfigureProfileManager::RenameUser() { | |||
| 230 | UpdateCurrentUser(); | 236 | UpdateCurrentUser(); |
| 231 | } | 237 | } |
| 232 | 238 | ||
| 233 | void ConfigureProfileManager::DeleteUser() { | 239 | void ConfigureProfileManager::ConfirmDeleteUser() { |
| 234 | const auto index = tree_view->currentIndex().row(); | 240 | const auto index = tree_view->currentIndex().row(); |
| 235 | const auto uuid = profile_manager->GetUser(index); | 241 | const auto uuid = profile_manager->GetUser(index); |
| 236 | ASSERT(uuid); | 242 | ASSERT(uuid); |
| 237 | const auto username = GetAccountUsername(*profile_manager, *uuid); | 243 | const auto username = GetAccountUsername(*profile_manager, *uuid); |
| 238 | 244 | ||
| 239 | const auto confirm = QMessageBox::question( | 245 | confirm_dialog->SetInfo(username, *uuid, [this, uuid]() { DeleteUser(*uuid); }); |
| 240 | this, tr("Confirm Delete"), | 246 | confirm_dialog->show(); |
| 241 | tr("You are about to delete user with name \"%1\". Are you sure?").arg(username)); | 247 | } |
| 242 | |||
| 243 | if (confirm == QMessageBox::No) { | ||
| 244 | return; | ||
| 245 | } | ||
| 246 | 248 | ||
| 249 | void ConfigureProfileManager::DeleteUser(const Common::UUID& uuid) { | ||
| 247 | if (Settings::values.current_user.GetValue() == tree_view->currentIndex().row()) { | 250 | if (Settings::values.current_user.GetValue() == tree_view->currentIndex().row()) { |
| 248 | Settings::values.current_user = 0; | 251 | Settings::values.current_user = 0; |
| 249 | } | 252 | } |
| 250 | UpdateCurrentUser(); | 253 | UpdateCurrentUser(); |
| 251 | 254 | ||
| 252 | if (!profile_manager->RemoveUser(*uuid)) { | 255 | if (!profile_manager->RemoveUser(uuid)) { |
| 253 | return; | 256 | return; |
| 254 | } | 257 | } |
| 255 | 258 | ||
| @@ -319,3 +322,47 @@ void ConfigureProfileManager::SetUserImage() { | |||
| 319 | new QStandardItem{GetIcon(*uuid), FormatUserEntryText(username, *uuid)}); | 322 | new QStandardItem{GetIcon(*uuid), FormatUserEntryText(username, *uuid)}); |
| 320 | UpdateCurrentUser(); | 323 | UpdateCurrentUser(); |
| 321 | } | 324 | } |
| 325 | |||
| 326 | ConfigureProfileManagerDeleteDialog::ConfigureProfileManagerDeleteDialog(QWidget* parent) | ||
| 327 | : QDialog{parent} { | ||
| 328 | auto dialog_vbox_layout = new QVBoxLayout(this); | ||
| 329 | dialog_button_box = | ||
| 330 | new QDialogButtonBox(QDialogButtonBox::Yes | QDialogButtonBox::No, Qt::Horizontal, parent); | ||
| 331 | auto label_message = | ||
| 332 | new QLabel(tr("Delete this user? All of the user's save data will be deleted."), this); | ||
| 333 | label_info = new QLabel(this); | ||
| 334 | auto dialog_hbox_layout_widget = new QWidget(this); | ||
| 335 | auto dialog_hbox_layout = new QHBoxLayout(dialog_hbox_layout_widget); | ||
| 336 | icon_scene = new QGraphicsScene(0, 0, 64, 64, this); | ||
| 337 | auto icon_view = new QGraphicsView(icon_scene, this); | ||
| 338 | |||
| 339 | dialog_hbox_layout_widget->setLayout(dialog_hbox_layout); | ||
| 340 | icon_view->setMaximumSize(64, 64); | ||
| 341 | icon_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); | ||
| 342 | icon_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); | ||
| 343 | this->setLayout(dialog_vbox_layout); | ||
| 344 | this->setWindowTitle(tr("Confirm Delete")); | ||
| 345 | this->setSizeGripEnabled(false); | ||
| 346 | dialog_vbox_layout->addWidget(label_message); | ||
| 347 | dialog_vbox_layout->addWidget(dialog_hbox_layout_widget); | ||
| 348 | dialog_vbox_layout->addWidget(dialog_button_box); | ||
| 349 | dialog_hbox_layout->addWidget(icon_view); | ||
| 350 | dialog_hbox_layout->addWidget(label_info); | ||
| 351 | |||
| 352 | connect(dialog_button_box, &QDialogButtonBox::rejected, this, [this]() { close(); }); | ||
| 353 | } | ||
| 354 | |||
| 355 | ConfigureProfileManagerDeleteDialog::~ConfigureProfileManagerDeleteDialog() = default; | ||
| 356 | |||
| 357 | void ConfigureProfileManagerDeleteDialog::SetInfo(const QString& username, const Common::UUID& uuid, | ||
| 358 | std::function<void()> accept_callback) { | ||
| 359 | label_info->setText( | ||
| 360 | tr("Name: %1\nUUID: %2").arg(username, QString::fromStdString(uuid.FormattedString()))); | ||
| 361 | icon_scene->clear(); | ||
| 362 | icon_scene->addPixmap(GetIcon(uuid)); | ||
| 363 | |||
| 364 | connect(dialog_button_box, &QDialogButtonBox::accepted, this, [this, accept_callback]() { | ||
| 365 | close(); | ||
| 366 | accept_callback(); | ||
| 367 | }); | ||
| 368 | } | ||
diff --git a/src/yuzu/configuration/configure_profile_manager.h b/src/yuzu/configuration/configure_profile_manager.h index fe9033779..c4b1a334e 100644 --- a/src/yuzu/configuration/configure_profile_manager.h +++ b/src/yuzu/configuration/configure_profile_manager.h | |||
| @@ -3,16 +3,24 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <functional> | ||
| 6 | #include <memory> | 7 | #include <memory> |
| 7 | 8 | ||
| 9 | #include <QDialog> | ||
| 8 | #include <QList> | 10 | #include <QList> |
| 9 | #include <QWidget> | 11 | #include <QWidget> |
| 10 | 12 | ||
| 13 | namespace Common { | ||
| 14 | struct UUID; | ||
| 15 | } | ||
| 16 | |||
| 11 | namespace Core { | 17 | namespace Core { |
| 12 | class System; | 18 | class System; |
| 13 | } | 19 | } |
| 14 | 20 | ||
| 15 | class QGraphicsScene; | 21 | class QGraphicsScene; |
| 22 | class QDialogButtonBox; | ||
| 23 | class QLabel; | ||
| 16 | class QStandardItem; | 24 | class QStandardItem; |
| 17 | class QStandardItemModel; | 25 | class QStandardItemModel; |
| 18 | class QTreeView; | 26 | class QTreeView; |
| @@ -26,6 +34,20 @@ namespace Ui { | |||
| 26 | class ConfigureProfileManager; | 34 | class ConfigureProfileManager; |
| 27 | } | 35 | } |
| 28 | 36 | ||
| 37 | class ConfigureProfileManagerDeleteDialog : public QDialog { | ||
| 38 | public: | ||
| 39 | explicit ConfigureProfileManagerDeleteDialog(QWidget* parent); | ||
| 40 | ~ConfigureProfileManagerDeleteDialog(); | ||
| 41 | |||
| 42 | void SetInfo(const QString& username, const Common::UUID& uuid, | ||
| 43 | std::function<void()> accept_callback); | ||
| 44 | |||
| 45 | private: | ||
| 46 | QDialogButtonBox* dialog_button_box; | ||
| 47 | QGraphicsScene* icon_scene; | ||
| 48 | QLabel* label_info; | ||
| 49 | }; | ||
| 50 | |||
| 29 | class ConfigureProfileManager : public QWidget { | 51 | class ConfigureProfileManager : public QWidget { |
| 30 | Q_OBJECT | 52 | Q_OBJECT |
| 31 | 53 | ||
| @@ -47,7 +69,8 @@ private: | |||
| 47 | void SelectUser(const QModelIndex& index); | 69 | void SelectUser(const QModelIndex& index); |
| 48 | void AddUser(); | 70 | void AddUser(); |
| 49 | void RenameUser(); | 71 | void RenameUser(); |
| 50 | void DeleteUser(); | 72 | void ConfirmDeleteUser(); |
| 73 | void DeleteUser(const Common::UUID& uuid); | ||
| 51 | void SetUserImage(); | 74 | void SetUserImage(); |
| 52 | 75 | ||
| 53 | QVBoxLayout* layout; | 76 | QVBoxLayout* layout; |
| @@ -55,6 +78,8 @@ private: | |||
| 55 | QStandardItemModel* item_model; | 78 | QStandardItemModel* item_model; |
| 56 | QGraphicsScene* scene; | 79 | QGraphicsScene* scene; |
| 57 | 80 | ||
| 81 | ConfigureProfileManagerDeleteDialog* confirm_dialog; | ||
| 82 | |||
| 58 | std::vector<QList<QStandardItem*>> list_items; | 83 | std::vector<QList<QStandardItem*>> list_items; |
| 59 | 84 | ||
| 60 | std::unique_ptr<Ui::ConfigureProfileManager> ui; | 85 | std::unique_ptr<Ui::ConfigureProfileManager> ui; |
diff --git a/src/yuzu/configuration/configure_profile_manager.ui b/src/yuzu/configuration/configure_profile_manager.ui index cfe7478c8..bd6dea4f4 100644 --- a/src/yuzu/configuration/configure_profile_manager.ui +++ b/src/yuzu/configuration/configure_profile_manager.ui | |||
| @@ -57,6 +57,12 @@ | |||
| 57 | <height>48</height> | 57 | <height>48</height> |
| 58 | </size> | 58 | </size> |
| 59 | </property> | 59 | </property> |
| 60 | <property name="frameShape"> | ||
| 61 | <enum>QFrame::NoFrame</enum> | ||
| 62 | </property> | ||
| 63 | <property name="frameShadow"> | ||
| 64 | <enum>QFrame::Plain</enum> | ||
| 65 | </property> | ||
| 60 | <property name="verticalScrollBarPolicy"> | 66 | <property name="verticalScrollBarPolicy"> |
| 61 | <enum>Qt::ScrollBarAlwaysOff</enum> | 67 | <enum>Qt::ScrollBarAlwaysOff</enum> |
| 62 | </property> | 68 | </property> |
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index 6198d1e4e..1800f090f 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h | |||
| @@ -145,12 +145,14 @@ public: | |||
| 145 | const char* tooltip; | 145 | const char* tooltip; |
| 146 | }; | 146 | }; |
| 147 | // clang-format off | 147 | // clang-format off |
| 148 | const auto ingame_status = | ||
| 149 | CompatStatus{QStringLiteral("#f2d624"), QT_TR_NOOP("Ingame"), QT_TR_NOOP("Game starts, but crashes or major glitches prevent it from being completed.")}; | ||
| 148 | static const std::map<QString, CompatStatus> status_data = { | 150 | static const std::map<QString, CompatStatus> status_data = { |
| 149 | {QStringLiteral("0"), {QStringLiteral("#5c93ed"), QT_TR_NOOP("Perfect"), QT_TR_NOOP("Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without\nany workarounds needed.")}}, | 151 | {QStringLiteral("0"), {QStringLiteral("#5c93ed"), QT_TR_NOOP("Perfect"), QT_TR_NOOP("Game can be played without issues.")}}, |
| 150 | {QStringLiteral("1"), {QStringLiteral("#47d35c"), QT_TR_NOOP("Great"), QT_TR_NOOP("Game functions with minor graphical or audio glitches and is playable from start to finish. May require some\nworkarounds.")}}, | 152 | {QStringLiteral("1"), {QStringLiteral("#47d35c"), QT_TR_NOOP("Playable"), QT_TR_NOOP("Game functions with minor graphical or audio glitches and is playable from start to finish.")}}, |
| 151 | {QStringLiteral("2"), {QStringLiteral("#94b242"), QT_TR_NOOP("Okay"), QT_TR_NOOP("Game functions with major graphical or audio glitches, but game is playable from start to finish with\nworkarounds.")}}, | 153 | {QStringLiteral("2"), ingame_status}, |
| 152 | {QStringLiteral("3"), {QStringLiteral("#f2d624"), QT_TR_NOOP("Bad"), QT_TR_NOOP("Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches\neven with workarounds.")}}, | 154 | {QStringLiteral("3"), ingame_status}, // Fallback for the removed "Okay" category |
| 153 | {QStringLiteral("4"), {QStringLiteral("#FF0000"), QT_TR_NOOP("Intro/Menu"), QT_TR_NOOP("Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start\nScreen.")}}, | 155 | {QStringLiteral("4"), {QStringLiteral("#FF0000"), QT_TR_NOOP("Intro/Menu"), QT_TR_NOOP("Game loads, but is unable to progress past the Start Screen.")}}, |
| 154 | {QStringLiteral("5"), {QStringLiteral("#828282"), QT_TR_NOOP("Won't Boot"), QT_TR_NOOP("The game crashes when attempting to startup.")}}, | 156 | {QStringLiteral("5"), {QStringLiteral("#828282"), QT_TR_NOOP("Won't Boot"), QT_TR_NOOP("The game crashes when attempting to startup.")}}, |
| 155 | {QStringLiteral("99"), {QStringLiteral("#000000"), QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}}, | 157 | {QStringLiteral("99"), {QStringLiteral("#000000"), QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}}, |
| 156 | }; | 158 | }; |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 59e56633a..7ee2302cc 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -342,6 +342,7 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan | |||
| 342 | const auto override_build = | 342 | const auto override_build = |
| 343 | fmt::format(fmt::runtime(std::string(Common::g_title_bar_format_idle)), build_id); | 343 | fmt::format(fmt::runtime(std::string(Common::g_title_bar_format_idle)), build_id); |
| 344 | const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build; | 344 | const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build; |
| 345 | const auto processor_count = std::thread::hardware_concurrency(); | ||
| 345 | 346 | ||
| 346 | LOG_INFO(Frontend, "yuzu Version: {}", yuzu_build_version); | 347 | LOG_INFO(Frontend, "yuzu Version: {}", yuzu_build_version); |
| 347 | LogRuntimes(); | 348 | LogRuntimes(); |
| @@ -360,7 +361,11 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan | |||
| 360 | } | 361 | } |
| 361 | } | 362 | } |
| 362 | LOG_INFO(Frontend, "Host CPU: {}", cpu_string); | 363 | LOG_INFO(Frontend, "Host CPU: {}", cpu_string); |
| 364 | if (std::optional<int> processor_core = Common::GetProcessorCount()) { | ||
| 365 | LOG_INFO(Frontend, "Host CPU Cores: {}", *processor_core); | ||
| 366 | } | ||
| 363 | #endif | 367 | #endif |
| 368 | LOG_INFO(Frontend, "Host CPU Threads: {}", processor_count); | ||
| 364 | LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString()); | 369 | LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString()); |
| 365 | LOG_INFO(Frontend, "Host RAM: {:.2f} GiB", | 370 | LOG_INFO(Frontend, "Host RAM: {:.2f} GiB", |
| 366 | Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB}); | 371 | Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB}); |
| @@ -1951,6 +1956,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target | |||
| 1951 | } | 1956 | } |
| 1952 | default: | 1957 | default: |
| 1953 | UNIMPLEMENTED(); | 1958 | UNIMPLEMENTED(); |
| 1959 | break; | ||
| 1954 | } | 1960 | } |
| 1955 | 1961 | ||
| 1956 | const QString qpath = QString::fromStdString(Common::FS::PathToUTF8String(path)); | 1962 | const QString qpath = QString::fromStdString(Common::FS::PathToUTF8String(path)); |
| @@ -2018,38 +2024,50 @@ static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src | |||
| 2018 | return true; | 2024 | return true; |
| 2019 | } | 2025 | } |
| 2020 | 2026 | ||
| 2027 | QString GMainWindow::GetGameListErrorRemoving(InstalledEntryType type) const { | ||
| 2028 | switch (type) { | ||
| 2029 | case InstalledEntryType::Game: | ||
| 2030 | return tr("Error Removing Contents"); | ||
| 2031 | case InstalledEntryType::Update: | ||
| 2032 | return tr("Error Removing Update"); | ||
| 2033 | case InstalledEntryType::AddOnContent: | ||
| 2034 | return tr("Error Removing DLC"); | ||
| 2035 | default: | ||
| 2036 | return QStringLiteral("Error Removing <Invalid Type>"); | ||
| 2037 | } | ||
| 2038 | } | ||
| 2021 | void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type) { | 2039 | void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type) { |
| 2022 | const QString entry_type = [type] { | 2040 | const QString entry_question = [type] { |
| 2023 | switch (type) { | 2041 | switch (type) { |
| 2024 | case InstalledEntryType::Game: | 2042 | case InstalledEntryType::Game: |
| 2025 | return tr("Contents"); | 2043 | return tr("Remove Installed Game Contents?"); |
| 2026 | case InstalledEntryType::Update: | 2044 | case InstalledEntryType::Update: |
| 2027 | return tr("Update"); | 2045 | return tr("Remove Installed Game Update?"); |
| 2028 | case InstalledEntryType::AddOnContent: | 2046 | case InstalledEntryType::AddOnContent: |
| 2029 | return tr("DLC"); | 2047 | return tr("Remove Installed Game DLC?"); |
| 2030 | default: | 2048 | default: |
| 2031 | return QString{}; | 2049 | return QStringLiteral("Remove Installed Game <Invalid Type>?"); |
| 2032 | } | 2050 | } |
| 2033 | }(); | 2051 | }(); |
| 2034 | 2052 | ||
| 2035 | if (QMessageBox::question( | 2053 | if (QMessageBox::question(this, tr("Remove Entry"), entry_question, |
| 2036 | this, tr("Remove Entry"), tr("Remove Installed Game %1?").arg(entry_type), | 2054 | QMessageBox::Yes | QMessageBox::No, |
| 2037 | QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) { | 2055 | QMessageBox::No) != QMessageBox::Yes) { |
| 2038 | return; | 2056 | return; |
| 2039 | } | 2057 | } |
| 2040 | 2058 | ||
| 2041 | switch (type) { | 2059 | switch (type) { |
| 2042 | case InstalledEntryType::Game: | 2060 | case InstalledEntryType::Game: |
| 2043 | RemoveBaseContent(program_id, entry_type); | 2061 | RemoveBaseContent(program_id, type); |
| 2044 | [[fallthrough]]; | 2062 | [[fallthrough]]; |
| 2045 | case InstalledEntryType::Update: | 2063 | case InstalledEntryType::Update: |
| 2046 | RemoveUpdateContent(program_id, entry_type); | 2064 | RemoveUpdateContent(program_id, type); |
| 2047 | if (type != InstalledEntryType::Game) { | 2065 | if (type != InstalledEntryType::Game) { |
| 2048 | break; | 2066 | break; |
| 2049 | } | 2067 | } |
| 2050 | [[fallthrough]]; | 2068 | [[fallthrough]]; |
| 2051 | case InstalledEntryType::AddOnContent: | 2069 | case InstalledEntryType::AddOnContent: |
| 2052 | RemoveAddOnContent(program_id, entry_type); | 2070 | RemoveAddOnContent(program_id, type); |
| 2053 | break; | 2071 | break; |
| 2054 | } | 2072 | } |
| 2055 | Common::FS::RemoveDirRecursively(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / | 2073 | Common::FS::RemoveDirRecursively(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / |
| @@ -2057,7 +2075,7 @@ void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryT | |||
| 2057 | game_list->PopulateAsync(UISettings::values.game_dirs); | 2075 | game_list->PopulateAsync(UISettings::values.game_dirs); |
| 2058 | } | 2076 | } |
| 2059 | 2077 | ||
| 2060 | void GMainWindow::RemoveBaseContent(u64 program_id, const QString& entry_type) { | 2078 | void GMainWindow::RemoveBaseContent(u64 program_id, InstalledEntryType type) { |
| 2061 | const auto& fs_controller = system->GetFileSystemController(); | 2079 | const auto& fs_controller = system->GetFileSystemController(); |
| 2062 | const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(program_id) || | 2080 | const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(program_id) || |
| 2063 | fs_controller.GetSDMCContents()->RemoveExistingEntry(program_id); | 2081 | fs_controller.GetSDMCContents()->RemoveExistingEntry(program_id); |
| @@ -2067,12 +2085,12 @@ void GMainWindow::RemoveBaseContent(u64 program_id, const QString& entry_type) { | |||
| 2067 | tr("Successfully removed the installed base game.")); | 2085 | tr("Successfully removed the installed base game.")); |
| 2068 | } else { | 2086 | } else { |
| 2069 | QMessageBox::warning( | 2087 | QMessageBox::warning( |
| 2070 | this, tr("Error Removing %1").arg(entry_type), | 2088 | this, GetGameListErrorRemoving(type), |
| 2071 | tr("The base game is not installed in the NAND and cannot be removed.")); | 2089 | tr("The base game is not installed in the NAND and cannot be removed.")); |
| 2072 | } | 2090 | } |
| 2073 | } | 2091 | } |
| 2074 | 2092 | ||
| 2075 | void GMainWindow::RemoveUpdateContent(u64 program_id, const QString& entry_type) { | 2093 | void GMainWindow::RemoveUpdateContent(u64 program_id, InstalledEntryType type) { |
| 2076 | const auto update_id = program_id | 0x800; | 2094 | const auto update_id = program_id | 0x800; |
| 2077 | const auto& fs_controller = system->GetFileSystemController(); | 2095 | const auto& fs_controller = system->GetFileSystemController(); |
| 2078 | const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(update_id) || | 2096 | const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(update_id) || |
| @@ -2082,12 +2100,12 @@ void GMainWindow::RemoveUpdateContent(u64 program_id, const QString& entry_type) | |||
| 2082 | QMessageBox::information(this, tr("Successfully Removed"), | 2100 | QMessageBox::information(this, tr("Successfully Removed"), |
| 2083 | tr("Successfully removed the installed update.")); | 2101 | tr("Successfully removed the installed update.")); |
| 2084 | } else { | 2102 | } else { |
| 2085 | QMessageBox::warning(this, tr("Error Removing %1").arg(entry_type), | 2103 | QMessageBox::warning(this, GetGameListErrorRemoving(type), |
| 2086 | tr("There is no update installed for this title.")); | 2104 | tr("There is no update installed for this title.")); |
| 2087 | } | 2105 | } |
| 2088 | } | 2106 | } |
| 2089 | 2107 | ||
| 2090 | void GMainWindow::RemoveAddOnContent(u64 program_id, const QString& entry_type) { | 2108 | void GMainWindow::RemoveAddOnContent(u64 program_id, InstalledEntryType type) { |
| 2091 | u32 count{}; | 2109 | u32 count{}; |
| 2092 | const auto& fs_controller = system->GetFileSystemController(); | 2110 | const auto& fs_controller = system->GetFileSystemController(); |
| 2093 | const auto dlc_entries = system->GetContentProvider().ListEntriesFilter( | 2111 | const auto dlc_entries = system->GetContentProvider().ListEntriesFilter( |
| @@ -2105,7 +2123,7 @@ void GMainWindow::RemoveAddOnContent(u64 program_id, const QString& entry_type) | |||
| 2105 | } | 2123 | } |
| 2106 | 2124 | ||
| 2107 | if (count == 0) { | 2125 | if (count == 0) { |
| 2108 | QMessageBox::warning(this, tr("Error Removing %1").arg(entry_type), | 2126 | QMessageBox::warning(this, GetGameListErrorRemoving(type), |
| 2109 | tr("There are no DLC installed for this title.")); | 2127 | tr("There are no DLC installed for this title.")); |
| 2110 | return; | 2128 | return; |
| 2111 | } | 2129 | } |
| @@ -2803,6 +2821,20 @@ void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_tex | |||
| 2803 | } | 2821 | } |
| 2804 | 2822 | ||
| 2805 | void GMainWindow::OnMenuReportCompatibility() { | 2823 | void GMainWindow::OnMenuReportCompatibility() { |
| 2824 | const auto& caps = Common::GetCPUCaps(); | ||
| 2825 | const bool has_fma = caps.fma || caps.fma4; | ||
| 2826 | const auto processor_count = std::thread::hardware_concurrency(); | ||
| 2827 | const bool has_4threads = processor_count == 0 || processor_count >= 4; | ||
| 2828 | const bool has_8gb_ram = Common::GetMemInfo().TotalPhysicalMemory >= 8_GiB; | ||
| 2829 | const bool has_broken_vulkan = UISettings::values.has_broken_vulkan; | ||
| 2830 | |||
| 2831 | if (!has_fma || !has_4threads || !has_8gb_ram || has_broken_vulkan) { | ||
| 2832 | QMessageBox::critical(this, tr("Hardware requirements not met"), | ||
| 2833 | tr("Your system does not meet the recommended hardware requirements. " | ||
| 2834 | "Compatibility reporting has been disabled.")); | ||
| 2835 | return; | ||
| 2836 | } | ||
| 2837 | |||
| 2806 | if (!Settings::values.yuzu_token.GetValue().empty() && | 2838 | if (!Settings::values.yuzu_token.GetValue().empty() && |
| 2807 | !Settings::values.yuzu_username.GetValue().empty()) { | 2839 | !Settings::values.yuzu_username.GetValue().empty()) { |
| 2808 | CompatDB compatdb{system->TelemetrySession(), this}; | 2840 | CompatDB compatdb{system->TelemetrySession(), this}; |
| @@ -3168,6 +3200,7 @@ void GMainWindow::OnToggleGpuAccuracy() { | |||
| 3168 | case Settings::GPUAccuracy::Extreme: | 3200 | case Settings::GPUAccuracy::Extreme: |
| 3169 | default: { | 3201 | default: { |
| 3170 | Settings::values.gpu_accuracy.SetValue(Settings::GPUAccuracy::High); | 3202 | Settings::values.gpu_accuracy.SetValue(Settings::GPUAccuracy::High); |
| 3203 | break; | ||
| 3171 | } | 3204 | } |
| 3172 | } | 3205 | } |
| 3173 | 3206 | ||
| @@ -3500,6 +3533,7 @@ void GMainWindow::UpdateGPUAccuracyButton() { | |||
| 3500 | default: { | 3533 | default: { |
| 3501 | gpu_accuracy_button->setText(tr("GPU ERROR")); | 3534 | gpu_accuracy_button->setText(tr("GPU ERROR")); |
| 3502 | gpu_accuracy_button->setChecked(true); | 3535 | gpu_accuracy_button->setChecked(true); |
| 3536 | break; | ||
| 3503 | } | 3537 | } |
| 3504 | } | 3538 | } |
| 3505 | } | 3539 | } |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 150ada84c..b73f550dd 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -324,9 +324,10 @@ private slots: | |||
| 324 | void OnMouseActivity(); | 324 | void OnMouseActivity(); |
| 325 | 325 | ||
| 326 | private: | 326 | private: |
| 327 | void RemoveBaseContent(u64 program_id, const QString& entry_type); | 327 | QString GetGameListErrorRemoving(InstalledEntryType type) const; |
| 328 | void RemoveUpdateContent(u64 program_id, const QString& entry_type); | 328 | void RemoveBaseContent(u64 program_id, InstalledEntryType type); |
| 329 | void RemoveAddOnContent(u64 program_id, const QString& entry_type); | 329 | void RemoveUpdateContent(u64 program_id, InstalledEntryType type); |
| 330 | void RemoveAddOnContent(u64 program_id, InstalledEntryType type); | ||
| 330 | void RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target); | 331 | void RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target); |
| 331 | void RemoveAllTransferableShaderCaches(u64 program_id); | 332 | void RemoveAllTransferableShaderCaches(u64 program_id); |
| 332 | void RemoveCustomConfiguration(u64 program_id, const std::string& game_path); | 333 | void RemoveCustomConfiguration(u64 program_id, const std::string& game_path); |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp index 65455c86e..25948328c 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp | |||
| @@ -84,6 +84,7 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsyste | |||
| 84 | default: | 84 | default: |
| 85 | LOG_CRITICAL(Frontend, "Window manager subsystem not implemented"); | 85 | LOG_CRITICAL(Frontend, "Window manager subsystem not implemented"); |
| 86 | std::exit(EXIT_FAILURE); | 86 | std::exit(EXIT_FAILURE); |
| 87 | break; | ||
| 87 | } | 88 | } |
| 88 | 89 | ||
| 89 | OnResize(); | 90 | OnResize(); |
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index e16f79eb4..dfe5a30ea 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -351,6 +351,7 @@ int main(int argc, char** argv) { | |||
| 351 | "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}", | 351 | "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}", |
| 352 | loader_id, error_id, static_cast<Loader::ResultStatus>(error_id)); | 352 | loader_id, error_id, static_cast<Loader::ResultStatus>(error_id)); |
| 353 | } | 353 | } |
| 354 | break; | ||
| 354 | } | 355 | } |
| 355 | 356 | ||
| 356 | system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", "SDL"); | 357 | system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", "SDL"); |