diff options
54 files changed, 796 insertions, 384 deletions
| diff --git a/CMakeLists.txt b/CMakeLists.txt index c6fc5dd9e..1d13dc74e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -133,13 +133,13 @@ if (NOT ENABLE_GENERIC)      if (MSVC)          detect_architecture("_M_AMD64" x86_64)          detect_architecture("_M_IX86" x86) -        detect_architecture("_M_ARM" ARM) -        detect_architecture("_M_ARM64" ARM64) +        detect_architecture("_M_ARM" arm) +        detect_architecture("_M_ARM64" arm64)      else()          detect_architecture("__x86_64__" x86_64)          detect_architecture("__i386__" x86) -        detect_architecture("__arm__" ARM) -        detect_architecture("__aarch64__" ARM64) +        detect_architecture("__arm__" arm) +        detect_architecture("__aarch64__" arm64)      endif()  endif() diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index e80fd124e..7f0a6d069 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -7,15 +7,14 @@ include(DownloadExternals)  # xbyak  if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) -    add_library(xbyak INTERFACE) -    file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include) -    file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/xbyak/xbyak DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include) -    target_include_directories(xbyak SYSTEM INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include) -    target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES) +    add_subdirectory(xbyak)  endif()  # Dynarmic -if (ARCHITECTURE_x86_64) +if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) +    if (ARCHITECTURE_arm64) +       set(DYNARMIC_FRONTENDS "A32") +    endif()      set(DYNARMIC_NO_BUNDLED_FMT ON)      set(DYNARMIC_IGNORE_ASSERTS ON CACHE BOOL "" FORCE)      add_subdirectory(dynarmic) diff --git a/externals/dynarmic b/externals/dynarmic -Subproject 2d4602a6516c67d547000d4c80bcc5f74976abd +Subproject 424fdb5c5026ec5bdd7553271190397f63fb503 diff --git a/externals/xbyak b/externals/xbyak -Subproject c306b8e5786eeeb87b8925a8af5c3bf057ff5a9 +Subproject 348e3e548ebac06d243e5881caec8440e249f65 diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index 0a1f3bf18..8e3a8f5a8 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -217,7 +217,7 @@ else()  endif()  target_link_libraries(audio_core PUBLIC common core) -if (ARCHITECTURE_x86_64) +if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)      target_link_libraries(audio_core PRIVATE dynarmic)  endif() diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index e1e2a90fc..0dad9338a 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h @@ -31,8 +31,10 @@  #ifndef _MSC_VER -#ifdef ARCHITECTURE_x86_64 +#if defined(ARCHITECTURE_x86_64)  #define Crash() __asm__ __volatile__("int $3") +#elif defined(ARCHITECTURE_arm64) +#define Crash() __asm__ __volatile__("brk #0")  #else  #define Crash() exit(1)  #endif diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp index 7f9659612..909f6cf3f 100644 --- a/src/common/host_memory.cpp +++ b/src/common/host_memory.cpp @@ -359,6 +359,12 @@ public:              }          }); +        long page_size = sysconf(_SC_PAGESIZE); +        if (page_size != 0x1000) { +            LOG_CRITICAL(HW_Memory, "page size {:#x} is incompatible with 4K paging", page_size); +            throw std::bad_alloc{}; +        } +          // Backing memory initialization  #if defined(__FreeBSD__) && __FreeBSD__ < 13          // XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30 diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 0a560ebb7..8173462cb 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -151,6 +151,7 @@ void UpdateRescalingInfo() {          ASSERT(false);          info.up_scale = 1;          info.down_shift = 0; +        break;      }      info.up_factor = static_cast<f32>(info.up_scale) / (1U << info.down_shift);      info.down_factor = static_cast<f32>(1U << info.down_shift) / info.up_scale; diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp index 1a27532d4..e54383a4a 100644 --- a/src/common/x64/cpu_detect.cpp +++ b/src/common/x64/cpu_detect.cpp @@ -4,14 +4,27 @@  #include <array>  #include <cstring> +#include <fstream>  #include <iterator> +#include <optional>  #include <string_view> +#include <thread> +#include <vector>  #include "common/bit_util.h"  #include "common/common_types.h" +#include "common/logging/log.h"  #include "common/x64/cpu_detect.h" +#ifdef _WIN32 +#include <windows.h> +#endif +  #ifdef _MSC_VER  #include <intrin.h> + +static inline u64 xgetbv(u32 index) { +    return _xgetbv(index); +}  #else  #if defined(__DragonFly__) || defined(__FreeBSD__) @@ -39,12 +52,11 @@ static inline void __cpuid(int info[4], u32 function_id) {  }  #define _XCR_XFEATURE_ENABLED_MASK 0 -static inline u64 _xgetbv(u32 index) { +static inline u64 xgetbv(u32 index) {      u32 eax, edx;      __asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(index));      return ((u64)edx << 32) | eax;  } -  #endif // _MSC_VER  namespace Common { @@ -107,7 +119,7 @@ static CPUCaps Detect() {          //  - Is the XSAVE bit set in CPUID?          //  - XGETBV result has the XCR bit set.          if (Common::Bit<28>(cpu_id[2]) && Common::Bit<27>(cpu_id[2])) { -            if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) { +            if ((xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) {                  caps.avx = true;                  if (Common::Bit<12>(cpu_id[2]))                      caps.fma = true; @@ -192,4 +204,45 @@ const CPUCaps& GetCPUCaps() {      return caps;  } +std::optional<int> GetProcessorCount() { +#if defined(_WIN32) +    // Get the buffer length. +    DWORD length = 0; +    GetLogicalProcessorInformation(nullptr, &length); +    if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { +        LOG_ERROR(Frontend, "Failed to query core count."); +        return std::nullopt; +    } +    std::vector<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> buffer( +        length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION)); +    // Now query the core count. +    if (!GetLogicalProcessorInformation(buffer.data(), &length)) { +        LOG_ERROR(Frontend, "Failed to query core count."); +        return std::nullopt; +    } +    return static_cast<int>( +        std::count_if(buffer.cbegin(), buffer.cend(), [](const auto& proc_info) { +            return proc_info.Relationship == RelationProcessorCore; +        })); +#elif defined(__unix__) +    const int thread_count = std::thread::hardware_concurrency(); +    std::ifstream smt("/sys/devices/system/cpu/smt/active"); +    char state = '0'; +    if (smt) { +        smt.read(&state, sizeof(state)); +    } +    switch (state) { +    case '0': +        return thread_count; +    case '1': +        return thread_count / 2; +    default: +        return std::nullopt; +    } +#else +    // Shame on you +    return std::nullopt; +#endif +} +  } // namespace Common diff --git a/src/common/x64/cpu_detect.h b/src/common/x64/cpu_detect.h index 6830f3795..ca8db19d6 100644 --- a/src/common/x64/cpu_detect.h +++ b/src/common/x64/cpu_detect.h @@ -4,6 +4,7 @@  #pragma once +#include <optional>  #include <string_view>  #include "common/common_types.h" @@ -74,4 +75,7 @@ struct CPUCaps {   */  const CPUCaps& GetCPUCaps(); +/// Detects CPU core count +std::optional<int> GetProcessorCount(); +  } // namespace Common diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index f6e082c36..f67f1ce92 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -497,10 +497,6 @@ add_library(core STATIC      hle/service/hid/irsensor/processor_base.h      hle/service/hid/irsensor/tera_plugin_processor.cpp      hle/service/hid/irsensor/tera_plugin_processor.h -    hle/service/jit/jit_context.cpp -    hle/service/jit/jit_context.h -    hle/service/jit/jit.cpp -    hle/service/jit/jit.h      hle/service/lbl/lbl.cpp      hle/service/lbl/lbl.h      hle/service/ldn/lan_discovery.cpp @@ -805,14 +801,18 @@ if (ENABLE_WEB_SERVICE)      target_link_libraries(core PRIVATE web_service)  endif() -if (ARCHITECTURE_x86_64) +if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)      target_sources(core PRIVATE -        arm/dynarmic/arm_dynarmic_32.cpp -        arm/dynarmic/arm_dynarmic_32.h          arm/dynarmic/arm_dynarmic_64.cpp          arm/dynarmic/arm_dynarmic_64.h +        arm/dynarmic/arm_dynarmic_32.cpp +        arm/dynarmic/arm_dynarmic_32.h          arm/dynarmic/arm_dynarmic_cp15.cpp          arm/dynarmic/arm_dynarmic_cp15.h +        hle/service/jit/jit_context.cpp +        hle/service/jit/jit_context.h +        hle/service/jit/jit.cpp +        hle/service/jit/jit.h      )      target_link_libraries(core PRIVATE dynarmic)  endif() diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 287ba102e..227e06ea1 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -301,6 +301,11 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*          }      } +#ifdef ARCHITECTURE_arm64 +    // TODO: remove when fixed in dynarmic +    config.optimizations &= ~Dynarmic::OptimizationFlag::BlockLinking; +#endif +      return std::make_unique<Dynarmic::A32::Jit>(config);  } diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp index 200efe4db..5a4eba3eb 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp @@ -52,12 +52,16 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1          case 4:              // CP15_DATA_SYNC_BARRIER              return Callback{ -                [](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t { -#ifdef _MSC_VER +                [](void*, std::uint32_t, std::uint32_t) -> std::uint64_t { +#if defined(_MSC_VER) && defined(ARCHITECTURE_x86_64)                      _mm_mfence();                      _mm_lfence(); -#else +#elif defined(ARCHITECTURE_x86_64)                      asm volatile("mfence\n\tlfence\n\t" : : : "memory"); +#elif defined(ARCHITECTURE_arm64) +                    asm volatile("dsb sy\n\t" : : : "memory"); +#else +#error Unsupported architecture  #endif                      return 0;                  }, @@ -66,11 +70,15 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1          case 5:              // CP15_DATA_MEMORY_BARRIER              return Callback{ -                [](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t { -#ifdef _MSC_VER +                [](void*, std::uint32_t, std::uint32_t) -> std::uint64_t { +#if defined(_MSC_VER) && defined(ARCHITECTURE_x86_64)                      _mm_mfence(); -#else +#elif defined(ARCHITECTURE_x86_64)                      asm volatile("mfence\n\t" : : : "memory"); +#elif defined(ARCHITECTURE_arm64) +                    asm volatile("dmb sy\n\t" : : : "memory"); +#else +#error Unsupported architecture  #endif                      return 0;                  }, @@ -115,7 +123,7 @@ CallbackOrAccessOneWord DynarmicCP15::CompileGetOneWord(bool two, unsigned opc1,  CallbackOrAccessTwoWords DynarmicCP15::CompileGetTwoWords(bool two, unsigned opc, CoprocReg CRm) {      if (!two && opc == 0 && CRm == CoprocReg::C14) {          // CNTPCT -        const auto callback = [](Dynarmic::A32::Jit*, void* arg, u32, u32) -> u64 { +        const auto callback = [](void* arg, u32, u32) -> u64 {              const auto& parent_arg = *static_cast<ARM_Dynarmic_32*>(arg);              return parent_arg.system.CoreTiming().GetClockTicks();          }; diff --git a/src/core/arm/exclusive_monitor.cpp b/src/core/arm/exclusive_monitor.cpp index 2db0b035d..20550faeb 100644 --- a/src/core/arm/exclusive_monitor.cpp +++ b/src/core/arm/exclusive_monitor.cpp @@ -1,7 +1,7 @@  // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project  // SPDX-License-Identifier: GPL-2.0-or-later -#ifdef ARCHITECTURE_x86_64 +#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)  #include "core/arm/dynarmic/arm_exclusive_monitor.h"  #endif  #include "core/arm/exclusive_monitor.h" @@ -13,7 +13,7 @@ ExclusiveMonitor::~ExclusiveMonitor() = default;  std::unique_ptr<Core::ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory,                                                               std::size_t num_cores) { -#ifdef ARCHITECTURE_x86_64 +#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)      return std::make_unique<Core::DynarmicExclusiveMonitor>(memory, num_cores);  #else      // TODO(merry): Passthrough exclusive monitor diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp index d4375962f..3044922ac 100644 --- a/src/core/hle/kernel/physical_core.cpp +++ b/src/core/hle/kernel/physical_core.cpp @@ -12,7 +12,7 @@ namespace Kernel {  PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_)      : core_index{core_index_}, system{system_}, scheduler{scheduler_} { -#ifdef ARCHITECTURE_x86_64 +#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)      // TODO(bunnei): Initialization relies on a core being available. We may later replace this with      // a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager.      auto& kernel = system.Kernel(); @@ -26,7 +26,7 @@ PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KSche  PhysicalCore::~PhysicalCore() = default;  void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) { -#ifdef ARCHITECTURE_x86_64 +#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)      auto& kernel = system.Kernel();      if (!is_64_bit) {          // We already initialized a 64-bit core, replace with a 32-bit one. diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp index 0a7d42dda..d6562c842 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp @@ -379,6 +379,18 @@ void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) {      ctx.Add("MOV.S {}.x,primitive_invocation.x;", inst);  } +void EmitInvocationInfo(EmitContext& ctx, IR::Inst& inst) { +    switch (ctx.stage) { +    case Stage::TessellationControl: +    case Stage::TessellationEval: +        ctx.Add("SHL.U {}.x,primitive.vertexcount,16;", inst); +        break; +    default: +        LOG_WARNING(Shader, "(STUBBED) called"); +        ctx.Add("MOV.S {}.x,0x00ff0000;", inst); +    } +} +  void EmitSampleId(EmitContext& ctx, IR::Inst& inst) {      ctx.Add("MOV.S {}.x,fragment.sampleid.x;", inst);  } diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h index d645fd532..eaaf9ba39 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h +++ b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h @@ -69,6 +69,7 @@ void EmitSetOFlag(EmitContext& ctx);  void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst);  void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst);  void EmitInvocationId(EmitContext& ctx, IR::Inst& inst); +void EmitInvocationInfo(EmitContext& ctx, IR::Inst& inst);  void EmitSampleId(EmitContext& ctx, IR::Inst& inst);  void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst);  void EmitYDirection(EmitContext& ctx, IR::Inst& inst); diff --git a/src/shader_recompiler/backend/glasm/glasm_emit_context.cpp b/src/shader_recompiler/backend/glasm/glasm_emit_context.cpp index 89603c1c4..333a91cc5 100644 --- a/src/shader_recompiler/backend/glasm/glasm_emit_context.cpp +++ b/src/shader_recompiler/backend/glasm/glasm_emit_context.cpp @@ -95,6 +95,10 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile      if (info.uses_invocation_id) {          Add("ATTRIB primitive_invocation=primitive.invocation;");      } +    if (info.uses_invocation_info && +        (stage == Stage::TessellationControl || stage == Stage::TessellationEval)) { +        Add("ATTRIB primitive_vertexcount = primitive.vertexcount;"); +    }      if (info.stores_tess_level_outer) {          Add("OUTPUT result_patch_tessouter[]={{result.patch.tessouter[0..3]}};");      } diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp index d7c845469..c1671c37b 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp @@ -399,6 +399,18 @@ void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) {      ctx.AddU32("{}=uint(gl_InvocationID);", inst);  } +void EmitInvocationInfo(EmitContext& ctx, IR::Inst& inst) { +    switch (ctx.stage) { +    case Stage::TessellationControl: +    case Stage::TessellationEval: +        ctx.AddU32("{}=uint(gl_PatchVerticesIn)<<16;", inst); +        break; +    default: +        LOG_WARNING(Shader, "(STUBBED) called"); +        ctx.AddU32("{}=uint(0x00ff0000);", inst); +    } +} +  void EmitSampleId(EmitContext& ctx, IR::Inst& inst) {      ctx.AddU32("{}=uint(gl_SampleID);", inst);  } diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h index 96e683b5e..4151c89de 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h +++ b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h @@ -83,6 +83,7 @@ void EmitSetOFlag(EmitContext& ctx);  void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst);  void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst);  void EmitInvocationId(EmitContext& ctx, IR::Inst& inst); +void EmitInvocationInfo(EmitContext& ctx, IR::Inst& inst);  void EmitSampleId(EmitContext& ctx, IR::Inst& inst);  void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst);  void EmitYDirection(EmitContext& ctx, IR::Inst& inst); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp index a4751b42d..5b3b5d1f3 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp @@ -512,6 +512,18 @@ Id EmitInvocationId(EmitContext& ctx) {      return ctx.OpLoad(ctx.U32[1], ctx.invocation_id);  } +Id EmitInvocationInfo(EmitContext& ctx) { +    switch (ctx.stage) { +    case Stage::TessellationControl: +    case Stage::TessellationEval: +        return ctx.OpShiftLeftLogical(ctx.U32[1], ctx.OpLoad(ctx.U32[1], ctx.patch_vertices_in), +                                      ctx.Const(16u)); +    default: +        LOG_WARNING(Shader, "(STUBBED) called"); +        return ctx.Const(0x00ff0000u); +    } +} +  Id EmitSampleId(EmitContext& ctx) {      return ctx.OpLoad(ctx.U32[1], ctx.sample_id);  } diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index 7070c8fda..e31cdc5e8 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -72,6 +72,7 @@ void EmitSetOFlag(EmitContext& ctx);  Id EmitWorkgroupId(EmitContext& ctx);  Id EmitLocalInvocationId(EmitContext& ctx);  Id EmitInvocationId(EmitContext& ctx); +Id EmitInvocationInfo(EmitContext& ctx);  Id EmitSampleId(EmitContext& ctx);  Id EmitIsHelperInvocation(EmitContext& ctx);  Id EmitYDirection(EmitContext& ctx); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index c26ad8f93..0bfc2dd89 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -1325,6 +1325,10 @@ void EmitContext::DefineInputs(const IR::Program& program) {      if (info.uses_invocation_id) {          invocation_id = DefineInput(*this, U32[1], false, spv::BuiltIn::InvocationId);      } +    if (info.uses_invocation_info && +        (stage == Shader::Stage::TessellationControl || stage == Shader::Stage::TessellationEval)) { +        patch_vertices_in = DefineInput(*this, U32[1], false, spv::BuiltIn::PatchVertices); +    }      if (info.uses_sample_id) {          sample_id = DefineInput(*this, U32[1], false, spv::BuiltIn::SampleId);      } diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index c86e50911..dde45b4bc 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -204,6 +204,7 @@ public:      Id workgroup_id{};      Id local_invocation_id{};      Id invocation_id{}; +    Id patch_vertices_in{};      Id sample_id{};      Id is_helper_invocation{};      Id subgroup_local_invocation_id{}; diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp index d4425f06d..0cdac0eff 100644 --- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp +++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp @@ -362,6 +362,10 @@ U32 IREmitter::InvocationId() {      return Inst<U32>(Opcode::InvocationId);  } +U32 IREmitter::InvocationInfo() { +    return Inst<U32>(Opcode::InvocationInfo); +} +  U32 IREmitter::SampleId() {      return Inst<U32>(Opcode::SampleId);  } diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h index f163c18d9..2df992feb 100644 --- a/src/shader_recompiler/frontend/ir/ir_emitter.h +++ b/src/shader_recompiler/frontend/ir/ir_emitter.h @@ -97,6 +97,7 @@ public:      [[nodiscard]] U32 LocalInvocationIdZ();      [[nodiscard]] U32 InvocationId(); +    [[nodiscard]] U32 InvocationInfo();      [[nodiscard]] U32 SampleId();      [[nodiscard]] U1 IsHelperInvocation();      [[nodiscard]] F32 YDirection(); diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc index 88aa077ee..1fe3749cc 100644 --- a/src/shader_recompiler/frontend/ir/opcodes.inc +++ b/src/shader_recompiler/frontend/ir/opcodes.inc @@ -59,6 +59,7 @@ OPCODE(SetOFlag,                                            Void,           U1,  OPCODE(WorkgroupId,                                         U32x3,                                                                                          )  OPCODE(LocalInvocationId,                                   U32x3,                                                                                          )  OPCODE(InvocationId,                                        U32,                                                                                            ) +OPCODE(InvocationInfo,                                      U32,                                                                                            )  OPCODE(SampleId,                                            U32,                                                                                            )  OPCODE(IsHelperInvocation,                                  U1,                                                                                             )  OPCODE(YDirection,                                          F32,                                                                                            ) diff --git a/src/shader_recompiler/frontend/ir/patch.h b/src/shader_recompiler/frontend/ir/patch.h index 1e37c8eb6..5077e56c2 100644 --- a/src/shader_recompiler/frontend/ir/patch.h +++ b/src/shader_recompiler/frontend/ir/patch.h @@ -14,8 +14,6 @@ enum class Patch : u64 {      TessellationLodBottom,      TessellationLodInteriorU,      TessellationLodInteriorV, -    ComponentPadding0, -    ComponentPadding1,      Component0,      Component1,      Component2, @@ -137,7 +135,7 @@ enum class Patch : u64 {      Component118,      Component119,  }; -static_assert(static_cast<u64>(Patch::Component119) == 127); +static_assert(static_cast<u64>(Patch::Component119) == 125);  [[nodiscard]] bool IsGeneric(Patch patch) noexcept; diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp index 52be12f9c..753c62098 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp @@ -117,8 +117,7 @@ enum class SpecialRegister : u64 {      case SpecialRegister::SR_THREAD_KILL:          return IR::U32{ir.Select(ir.IsHelperInvocation(), ir.Imm32(-1), ir.Imm32(0))};      case SpecialRegister::SR_INVOCATION_INFO: -        LOG_WARNING(Shader, "(STUBBED) SR_INVOCATION_INFO"); -        return ir.Imm32(0x00ff'0000); +        return ir.InvocationInfo();      case SpecialRegister::SR_TID: {          const IR::Value tid{ir.LocalInvocationId()};          return ir.BitFieldInsert(ir.BitFieldInsert(IR::U32{ir.CompositeExtract(tid, 0)}, diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp index b7162f719..376aae0ea 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp @@ -223,7 +223,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo      Optimization::PositionPass(env, program);      Optimization::GlobalMemoryToStorageBufferPass(program); -    Optimization::TexturePass(env, program); +    Optimization::TexturePass(env, program, host_info);      if (Settings::values.resolution_info.active) {          Optimization::RescalingPass(program); diff --git a/src/shader_recompiler/host_translate_info.h b/src/shader_recompiler/host_translate_info.h index 881874310..cc1500690 100644 --- a/src/shader_recompiler/host_translate_info.h +++ b/src/shader_recompiler/host_translate_info.h @@ -13,6 +13,7 @@ struct HostTranslateInfo {      bool support_float16{};      ///< True when the device supports 16-bit floats      bool support_int64{};        ///< True when the device supports 64-bit integers      bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered +    bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers  };  } // namespace Shader diff --git a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp index 7cff8ecdc..5a4195217 100644 --- a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp +++ b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp @@ -468,6 +468,9 @@ void VisitUsages(Info& info, IR::Inst& inst) {      case IR::Opcode::InvocationId:          info.uses_invocation_id = true;          break; +    case IR::Opcode::InvocationInfo: +        info.uses_invocation_info = true; +        break;      case IR::Opcode::SampleId:          info.uses_sample_id = true;          break; diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h index 24f609d69..586a0668f 100644 --- a/src/shader_recompiler/ir_opt/passes.h +++ b/src/shader_recompiler/ir_opt/passes.h @@ -6,6 +6,10 @@  #include "shader_recompiler/environment.h"  #include "shader_recompiler/frontend/ir/program.h" +namespace Shader { +struct HostTranslateInfo; +} +  namespace Shader::Optimization {  void CollectShaderInfoPass(Environment& env, IR::Program& program); @@ -18,7 +22,7 @@ void LowerInt64ToInt32(IR::Program& program);  void RescalingPass(IR::Program& program);  void SsaRewritePass(IR::Program& program);  void PositionPass(Environment& env, IR::Program& program); -void TexturePass(Environment& env, IR::Program& program); +void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo& host_info);  void VerificationPass(const IR::Program& program);  // Dual Vertex diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp index 9eff84a3d..f5c86fcb1 100644 --- a/src/shader_recompiler/ir_opt/texture_pass.cpp +++ b/src/shader_recompiler/ir_opt/texture_pass.cpp @@ -7,11 +7,11 @@  #include <boost/container/small_vector.hpp> -#include "common/settings.h"  #include "shader_recompiler/environment.h"  #include "shader_recompiler/frontend/ir/basic_block.h"  #include "shader_recompiler/frontend/ir/breadth_first_search.h"  #include "shader_recompiler/frontend/ir/ir_emitter.h" +#include "shader_recompiler/host_translate_info.h"  #include "shader_recompiler/ir_opt/passes.h"  #include "shader_recompiler/shader_info.h" @@ -461,7 +461,7 @@ void PatchImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {                          ir.FPRecip(ir.ConvertUToF(32, 32, ir.CompositeExtract(texture_size, 1))))));  } -void PathTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_format) { +void PatchTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_format) {      const auto it{IR::Block::InstructionList::s_iterator_to(inst)};      IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};      auto get_max_value = [pixel_format]() -> float { @@ -494,7 +494,7 @@ void PathTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_f  }  } // Anonymous namespace -void TexturePass(Environment& env, IR::Program& program) { +void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo& host_info) {      TextureInstVector to_replace;      for (IR::Block* const block : program.post_order_blocks) {          for (IR::Inst& inst : block->Instructions()) { @@ -639,11 +639,11 @@ void TexturePass(Environment& env, IR::Program& program) {              inst->SetArg(0, IR::Value{});          } -        if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL && -            inst->GetOpcode() == IR::Opcode::ImageFetch && flags.type == TextureType::Buffer) { +        if (!host_info.support_snorm_render_buffer && inst->GetOpcode() == IR::Opcode::ImageFetch && +            flags.type == TextureType::Buffer) {              const auto pixel_format = ReadTexturePixelFormat(env, cbuf);              if (pixel_format != TexturePixelFormat::OTHER) { -                PathTexelFetch(*texture_inst.block, *texture_inst.inst, pixel_format); +                PatchTexelFetch(*texture_inst.block, *texture_inst.inst, pixel_format);              }          }      } diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h index f31e1f821..ee6252bb5 100644 --- a/src/shader_recompiler/shader_info.h +++ b/src/shader_recompiler/shader_info.h @@ -127,6 +127,7 @@ struct Info {      bool uses_workgroup_id{};      bool uses_local_invocation_id{};      bool uses_invocation_id{}; +    bool uses_invocation_info{};      bool uses_sample_id{};      bool uses_is_helper_invocation{};      bool uses_subgroup_invocation_id{}; diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 106991969..d7f7d336c 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -73,8 +73,6 @@ add_library(video_core STATIC      macro/macro_hle.h      macro/macro_interpreter.cpp      macro/macro_interpreter.h -    macro/macro_jit_x64.cpp -    macro/macro_jit_x64.h      fence_manager.h      gpu.cpp      gpu.h @@ -245,7 +243,7 @@ add_library(video_core STATIC  create_target_directory_groups(video_core)  target_link_libraries(video_core PUBLIC common core) -target_link_libraries(video_core PUBLIC glad shader_recompiler xbyak) +target_link_libraries(video_core PUBLIC glad shader_recompiler)  if (YUZU_USE_BUNDLED_FFMPEG AND NOT WIN32)      add_dependencies(video_core ffmpeg-build) @@ -282,8 +280,19 @@ else()          -Wno-sign-conversion      ) + +    # xbyak +    set_source_files_properties(macro/macro_jit_x64.cpp PROPERTIES COMPILE_OPTIONS "-Wno-conversion;-Wno-shadow")  endif()  if (ARCHITECTURE_x86_64) +    target_sources(video_core PRIVATE +        macro/macro_jit_x64.cpp +        macro/macro_jit_x64.h +    ) +    target_link_libraries(video_core PUBLIC xbyak) +endif() + +if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)      target_link_libraries(video_core PRIVATE dynarmic)  endif() diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index a948fcb14..910ab213a 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -2970,7 +2970,7 @@ public:                  CullFace gl_cull_face;                                                 ///< 0x1920                  Viewport::PixelCenter viewport_pixel_center;                           ///< 0x1924                  INSERT_PADDING_BYTES_NOINIT(0x4); -                u32 viewport_scale_offset_enbled;                                      ///< 0x192C +                u32 viewport_scale_offset_enabled;                                     ///< 0x192C                  INSERT_PADDING_BYTES_NOINIT(0xC);                  ViewportClipControl viewport_clip_control;                             ///< 0x193C                  UserClip::Op user_clip_op;                                             ///< 0x1940 @@ -3482,7 +3482,7 @@ ASSERT_REG_POSITION(gl_cull_test_enabled, 0x1918);  ASSERT_REG_POSITION(gl_front_face, 0x191C);  ASSERT_REG_POSITION(gl_cull_face, 0x1920);  ASSERT_REG_POSITION(viewport_pixel_center, 0x1924); -ASSERT_REG_POSITION(viewport_scale_offset_enbled, 0x192C); +ASSERT_REG_POSITION(viewport_scale_offset_enabled, 0x192C);  ASSERT_REG_POSITION(viewport_clip_control, 0x193C);  ASSERT_REG_POSITION(user_clip_op, 0x1940);  ASSERT_REG_POSITION(render_enable_override, 0x1944); diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index 4eb7a100d..54523a4b2 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp @@ -102,26 +102,29 @@ void MaxwellDMA::Launch() {              const bool is_src_pitch = IsPitchKind(static_cast<PTEKind>(src_kind));              const bool is_dst_pitch = IsPitchKind(static_cast<PTEKind>(dst_kind));              if (!is_src_pitch && is_dst_pitch) { -                std::vector<u8> tmp_buffer(regs.line_length_in); -                std::vector<u8> dst_buffer(regs.line_length_in); -                memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(), -                                               regs.line_length_in); -                for (u32 offset = 0; offset < regs.line_length_in; ++offset) { -                    dst_buffer[offset] = -                        tmp_buffer[convert_linear_2_blocklinear_addr(regs.offset_in + offset) - -                                   regs.offset_in]; +                UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0); +                UNIMPLEMENTED_IF(regs.offset_in % 16 != 0); +                UNIMPLEMENTED_IF(regs.offset_out % 16 != 0); +                std::vector<u8> tmp_buffer(16); +                for (u32 offset = 0; offset < regs.line_length_in; offset += 16) { +                    memory_manager.ReadBlockUnsafe( +                        convert_linear_2_blocklinear_addr(regs.offset_in + offset), +                        tmp_buffer.data(), tmp_buffer.size()); +                    memory_manager.WriteBlock(regs.offset_out + offset, tmp_buffer.data(), +                                              tmp_buffer.size());                  } -                memory_manager.WriteBlock(regs.offset_out, dst_buffer.data(), regs.line_length_in);              } else if (is_src_pitch && !is_dst_pitch) { -                std::vector<u8> tmp_buffer(regs.line_length_in); -                std::vector<u8> dst_buffer(regs.line_length_in); -                memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(), -                                               regs.line_length_in); -                for (u32 offset = 0; offset < regs.line_length_in; ++offset) { -                    dst_buffer[convert_linear_2_blocklinear_addr(regs.offset_out + offset) - -                               regs.offset_out] = tmp_buffer[offset]; +                UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0); +                UNIMPLEMENTED_IF(regs.offset_in % 16 != 0); +                UNIMPLEMENTED_IF(regs.offset_out % 16 != 0); +                std::vector<u8> tmp_buffer(16); +                for (u32 offset = 0; offset < regs.line_length_in; offset += 16) { +                    memory_manager.ReadBlockUnsafe(regs.offset_in + offset, tmp_buffer.data(), +                                                   tmp_buffer.size()); +                    memory_manager.WriteBlock( +                        convert_linear_2_blocklinear_addr(regs.offset_out + offset), +                        tmp_buffer.data(), tmp_buffer.size());                  } -                memory_manager.WriteBlock(regs.offset_out, dst_buffer.data(), regs.line_length_in);              } else {                  if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) {                      std::vector<u8> tmp_buffer(regs.line_length_in); diff --git a/src/video_core/macro/macro.cpp b/src/video_core/macro/macro.cpp index f61d5998e..505d81c1e 100644 --- a/src/video_core/macro/macro.cpp +++ b/src/video_core/macro/macro.cpp @@ -16,7 +16,10 @@  #include "video_core/macro/macro.h"  #include "video_core/macro/macro_hle.h"  #include "video_core/macro/macro_interpreter.h" + +#ifdef ARCHITECTURE_x86_64  #include "video_core/macro/macro_jit_x64.h" +#endif  namespace Tegra { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 72e314d39..8a8b5ce54 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -618,7 +618,7 @@ void RasterizerOpenGL::SyncViewport() {              }              flags[Dirty::Viewport0 + index] = false; -            if (!regs.viewport_scale_offset_enbled) { +            if (!regs.viewport_scale_offset_enabled) {                  const auto x = static_cast<GLfloat>(regs.surface_clip.x);                  const auto y = static_cast<GLfloat>(regs.surface_clip.y);                  const auto width = static_cast<GLfloat>(regs.surface_clip.width); diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 977709518..3fe04a115 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -76,7 +76,8 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key,          }          break;      case Shader::Stage::TessellationEval: -        info.tess_clockwise = key.tessellation_clockwise != 0; +        // Flip the face, as OpenGL's drawing is flipped. +        info.tess_clockwise = key.tessellation_clockwise == 0;          info.tess_primitive = [&key] {              switch (key.tessellation_primitive) {              case Maxwell::Tessellation::DomainType::Isolines: @@ -218,6 +219,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo            .support_float16 = false,            .support_int64 = device.HasShaderInt64(),            .needs_demote_reorder = device.IsAmd(), +          .support_snorm_render_buffer = false,        } {      if (use_asynchronous_shaders) {          workers = CreateWorkers(); diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp index a359f96f1..d53b422ca 100644 --- a/src/video_core/renderer_opengl/gl_state_tracker.cpp +++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp @@ -70,8 +70,8 @@ void SetupDirtyViewports(Tables& tables) {      FillBlock(tables[1], OFF(viewport_transform), NUM(viewport_transform), Viewports);      FillBlock(tables[1], OFF(viewports), NUM(viewports), Viewports); -    tables[0][OFF(viewport_scale_offset_enbled)] = ViewportTransform; -    tables[1][OFF(viewport_scale_offset_enbled)] = Viewports; +    tables[0][OFF(viewport_scale_offset_enabled)] = ViewportTransform; +    tables[1][OFF(viewport_scale_offset_enabled)] = Viewports;  }  void SetupDirtyScissors(Tables& tables) { diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index b42e5be1e..d4b0a542a 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -166,6 +166,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program          }          break;      case Shader::Stage::TessellationEval: +        info.tess_clockwise = key.state.tessellation_clockwise != 0;          info.tess_primitive = [&key] {              const u32 raw{key.state.tessellation_primitive.Value()};              switch (static_cast<Maxwell::Tessellation::DomainType>(raw)) { @@ -325,6 +326,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device          .support_int64 = device.IsShaderInt64Supported(),          .needs_demote_reorder = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY_KHR ||                                  driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR, +        .support_snorm_render_buffer = true,      };  } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index f79fa8313..f69c0c50f 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -683,7 +683,7 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg      if (!state_tracker.TouchViewports()) {          return;      } -    if (!regs.viewport_scale_offset_enbled) { +    if (!regs.viewport_scale_offset_enabled) {          const auto x = static_cast<float>(regs.surface_clip.x);          const auto y = static_cast<float>(regs.surface_clip.y);          const auto width = static_cast<float>(regs.surface_clip.width); diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp index b87c3be66..edb41b171 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp +++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp @@ -51,7 +51,7 @@ Flags MakeInvalidationFlags() {  void SetupDirtyViewports(Tables& tables) {      FillBlock(tables[0], OFF(viewport_transform), NUM(viewport_transform), Viewports);      FillBlock(tables[0], OFF(viewports), NUM(viewports), Viewports); -    tables[0][OFF(viewport_scale_offset_enbled)] = Viewports; +    tables[0][OFF(viewport_scale_offset_enabled)] = Viewports;      tables[1][OFF(window_origin)] = Viewports;  } diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp index 37bb76b72..f24f320b6 100644 --- a/src/video_core/shader_environment.cpp +++ b/src/video_core/shader_environment.cpp @@ -352,7 +352,7 @@ Shader::TexturePixelFormat GraphicsEnvironment::ReadTexturePixelFormat(u32 handl  u32 GraphicsEnvironment::ReadViewportTransformState() {      const auto& regs{maxwell3d->regs}; -    viewport_transform_state = regs.viewport_scale_offset_enbled; +    viewport_transform_state = regs.viewport_scale_offset_enabled;      return viewport_transform_state;  } diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 239f12382..5cc1fbf32 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -385,6 +385,6 @@ if (NOT APPLE)      target_compile_definitions(yuzu PRIVATE HAS_OPENGL)  endif() -if (ARCHITECTURE_x86_64) +if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)      target_link_libraries(yuzu PRIVATE dynarmic)  endif() diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 6acfb7b06..d88efacd7 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -401,224 +401,127 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {  }  int GRenderWindow::QtKeyToSwitchKey(Qt::Key qt_key) { -    switch (qt_key) { -    case Qt::Key_A: -        return Settings::NativeKeyboard::A; -    case Qt::Key_B: -        return Settings::NativeKeyboard::B; -    case Qt::Key_C: -        return Settings::NativeKeyboard::C; -    case Qt::Key_D: -        return Settings::NativeKeyboard::D; -    case Qt::Key_E: -        return Settings::NativeKeyboard::E; -    case Qt::Key_F: -        return Settings::NativeKeyboard::F; -    case Qt::Key_G: -        return Settings::NativeKeyboard::G; -    case Qt::Key_H: -        return Settings::NativeKeyboard::H; -    case Qt::Key_I: -        return Settings::NativeKeyboard::I; -    case Qt::Key_J: -        return Settings::NativeKeyboard::J; -    case Qt::Key_K: -        return Settings::NativeKeyboard::K; -    case Qt::Key_L: -        return Settings::NativeKeyboard::L; -    case Qt::Key_M: -        return Settings::NativeKeyboard::M; -    case Qt::Key_N: -        return Settings::NativeKeyboard::N; -    case Qt::Key_O: -        return Settings::NativeKeyboard::O; -    case Qt::Key_P: -        return Settings::NativeKeyboard::P; -    case Qt::Key_Q: -        return Settings::NativeKeyboard::Q; -    case Qt::Key_R: -        return Settings::NativeKeyboard::R; -    case Qt::Key_S: -        return Settings::NativeKeyboard::S; -    case Qt::Key_T: -        return Settings::NativeKeyboard::T; -    case Qt::Key_U: -        return Settings::NativeKeyboard::U; -    case Qt::Key_V: -        return Settings::NativeKeyboard::V; -    case Qt::Key_W: -        return Settings::NativeKeyboard::W; -    case Qt::Key_X: -        return Settings::NativeKeyboard::X; -    case Qt::Key_Y: -        return Settings::NativeKeyboard::Y; -    case Qt::Key_Z: -        return Settings::NativeKeyboard::Z; -    case Qt::Key_1: -        return Settings::NativeKeyboard::N1; -    case Qt::Key_2: -        return Settings::NativeKeyboard::N2; -    case Qt::Key_3: -        return Settings::NativeKeyboard::N3; -    case Qt::Key_4: -        return Settings::NativeKeyboard::N4; -    case Qt::Key_5: -        return Settings::NativeKeyboard::N5; -    case Qt::Key_6: -        return Settings::NativeKeyboard::N6; -    case Qt::Key_7: -        return Settings::NativeKeyboard::N7; -    case Qt::Key_8: -        return Settings::NativeKeyboard::N8; -    case Qt::Key_9: -        return Settings::NativeKeyboard::N9; -    case Qt::Key_0: -        return Settings::NativeKeyboard::N0; -    case Qt::Key_Return: -        return Settings::NativeKeyboard::Return; -    case Qt::Key_Escape: -        return Settings::NativeKeyboard::Escape; -    case Qt::Key_Backspace: -        return Settings::NativeKeyboard::Backspace; -    case Qt::Key_Tab: -        return Settings::NativeKeyboard::Tab; -    case Qt::Key_Space: -        return Settings::NativeKeyboard::Space; -    case Qt::Key_Minus: -        return Settings::NativeKeyboard::Minus; -    case Qt::Key_Plus: -    case Qt::Key_questiondown: -        return Settings::NativeKeyboard::Plus; -    case Qt::Key_BracketLeft: -    case Qt::Key_BraceLeft: -        return Settings::NativeKeyboard::OpenBracket; -    case Qt::Key_BracketRight: -    case Qt::Key_BraceRight: -        return Settings::NativeKeyboard::CloseBracket; -    case Qt::Key_Bar: -        return Settings::NativeKeyboard::Pipe; -    case Qt::Key_Dead_Tilde: -        return Settings::NativeKeyboard::Tilde; -    case Qt::Key_Ntilde: -    case Qt::Key_Semicolon: -        return Settings::NativeKeyboard::Semicolon; -    case Qt::Key_Apostrophe: -        return Settings::NativeKeyboard::Quote; -    case Qt::Key_Dead_Grave: -        return Settings::NativeKeyboard::Backquote; -    case Qt::Key_Comma: -        return Settings::NativeKeyboard::Comma; -    case Qt::Key_Period: -        return Settings::NativeKeyboard::Period; -    case Qt::Key_Slash: -        return Settings::NativeKeyboard::Slash; -    case Qt::Key_CapsLock: -        return Settings::NativeKeyboard::CapsLock; -    case Qt::Key_F1: -        return Settings::NativeKeyboard::F1; -    case Qt::Key_F2: -        return Settings::NativeKeyboard::F2; -    case Qt::Key_F3: -        return Settings::NativeKeyboard::F3; -    case Qt::Key_F4: -        return Settings::NativeKeyboard::F4; -    case Qt::Key_F5: -        return Settings::NativeKeyboard::F5; -    case Qt::Key_F6: -        return Settings::NativeKeyboard::F6; -    case Qt::Key_F7: -        return Settings::NativeKeyboard::F7; -    case Qt::Key_F8: -        return Settings::NativeKeyboard::F8; -    case Qt::Key_F9: -        return Settings::NativeKeyboard::F9; -    case Qt::Key_F10: -        return Settings::NativeKeyboard::F10; -    case Qt::Key_F11: -        return Settings::NativeKeyboard::F11; -    case Qt::Key_F12: -        return Settings::NativeKeyboard::F12; -    case Qt::Key_Print: -        return Settings::NativeKeyboard::PrintScreen; -    case Qt::Key_ScrollLock: -        return Settings::NativeKeyboard::ScrollLock; -    case Qt::Key_Pause: -        return Settings::NativeKeyboard::Pause; -    case Qt::Key_Insert: -        return Settings::NativeKeyboard::Insert; -    case Qt::Key_Home: -        return Settings::NativeKeyboard::Home; -    case Qt::Key_PageUp: -        return Settings::NativeKeyboard::PageUp; -    case Qt::Key_Delete: -        return Settings::NativeKeyboard::Delete; -    case Qt::Key_End: -        return Settings::NativeKeyboard::End; -    case Qt::Key_PageDown: -        return Settings::NativeKeyboard::PageDown; -    case Qt::Key_Right: -        return Settings::NativeKeyboard::Right; -    case Qt::Key_Left: -        return Settings::NativeKeyboard::Left; -    case Qt::Key_Down: -        return Settings::NativeKeyboard::Down; -    case Qt::Key_Up: -        return Settings::NativeKeyboard::Up; -    case Qt::Key_NumLock: -        return Settings::NativeKeyboard::NumLock; -    // Numpad keys are missing here -    case Qt::Key_F13: -        return Settings::NativeKeyboard::F13; -    case Qt::Key_F14: -        return Settings::NativeKeyboard::F14; -    case Qt::Key_F15: -        return Settings::NativeKeyboard::F15; -    case Qt::Key_F16: -        return Settings::NativeKeyboard::F16; -    case Qt::Key_F17: -        return Settings::NativeKeyboard::F17; -    case Qt::Key_F18: -        return Settings::NativeKeyboard::F18; -    case Qt::Key_F19: -        return Settings::NativeKeyboard::F19; -    case Qt::Key_F20: -        return Settings::NativeKeyboard::F20; -    case Qt::Key_F21: -        return Settings::NativeKeyboard::F21; -    case Qt::Key_F22: -        return Settings::NativeKeyboard::F22; -    case Qt::Key_F23: -        return Settings::NativeKeyboard::F23; -    case Qt::Key_F24: -        return Settings::NativeKeyboard::F24; -    // case Qt::: -    //    return Settings::NativeKeyboard::KPComma; -    // case Qt::: -    //    return Settings::NativeKeyboard::Ro; -    case Qt::Key_Hiragana_Katakana: -        return Settings::NativeKeyboard::KatakanaHiragana; -    case Qt::Key_yen: -        return Settings::NativeKeyboard::Yen; -    case Qt::Key_Henkan: -        return Settings::NativeKeyboard::Henkan; -    case Qt::Key_Muhenkan: -        return Settings::NativeKeyboard::Muhenkan; -    // case Qt::: -    //    return Settings::NativeKeyboard::NumPadCommaPc98; -    case Qt::Key_Hangul: -        return Settings::NativeKeyboard::HangulEnglish; -    case Qt::Key_Hangul_Hanja: -        return Settings::NativeKeyboard::Hanja; -    case Qt::Key_Katakana: -        return Settings::NativeKeyboard::KatakanaKey; -    case Qt::Key_Hiragana: -        return Settings::NativeKeyboard::HiraganaKey; -    case Qt::Key_Zenkaku_Hankaku: -        return Settings::NativeKeyboard::ZenkakuHankaku; -    // Modifier keys are handled by the modifier property -    default: -        return Settings::NativeKeyboard::None; +    static constexpr std::array<std::pair<Qt::Key, Settings::NativeKeyboard::Keys>, 106> key_map = { +        std::pair<Qt::Key, Settings::NativeKeyboard::Keys>{Qt::Key_A, Settings::NativeKeyboard::A}, +        {Qt::Key_A, Settings::NativeKeyboard::A}, +        {Qt::Key_B, Settings::NativeKeyboard::B}, +        {Qt::Key_C, Settings::NativeKeyboard::C}, +        {Qt::Key_D, Settings::NativeKeyboard::D}, +        {Qt::Key_E, Settings::NativeKeyboard::E}, +        {Qt::Key_F, Settings::NativeKeyboard::F}, +        {Qt::Key_G, Settings::NativeKeyboard::G}, +        {Qt::Key_H, Settings::NativeKeyboard::H}, +        {Qt::Key_I, Settings::NativeKeyboard::I}, +        {Qt::Key_J, Settings::NativeKeyboard::J}, +        {Qt::Key_K, Settings::NativeKeyboard::K}, +        {Qt::Key_L, Settings::NativeKeyboard::L}, +        {Qt::Key_M, Settings::NativeKeyboard::M}, +        {Qt::Key_N, Settings::NativeKeyboard::N}, +        {Qt::Key_O, Settings::NativeKeyboard::O}, +        {Qt::Key_P, Settings::NativeKeyboard::P}, +        {Qt::Key_Q, Settings::NativeKeyboard::Q}, +        {Qt::Key_R, Settings::NativeKeyboard::R}, +        {Qt::Key_S, Settings::NativeKeyboard::S}, +        {Qt::Key_T, Settings::NativeKeyboard::T}, +        {Qt::Key_U, Settings::NativeKeyboard::U}, +        {Qt::Key_V, Settings::NativeKeyboard::V}, +        {Qt::Key_W, Settings::NativeKeyboard::W}, +        {Qt::Key_X, Settings::NativeKeyboard::X}, +        {Qt::Key_Y, Settings::NativeKeyboard::Y}, +        {Qt::Key_Z, Settings::NativeKeyboard::Z}, +        {Qt::Key_1, Settings::NativeKeyboard::N1}, +        {Qt::Key_2, Settings::NativeKeyboard::N2}, +        {Qt::Key_3, Settings::NativeKeyboard::N3}, +        {Qt::Key_4, Settings::NativeKeyboard::N4}, +        {Qt::Key_5, Settings::NativeKeyboard::N5}, +        {Qt::Key_6, Settings::NativeKeyboard::N6}, +        {Qt::Key_7, Settings::NativeKeyboard::N7}, +        {Qt::Key_8, Settings::NativeKeyboard::N8}, +        {Qt::Key_9, Settings::NativeKeyboard::N9}, +        {Qt::Key_0, Settings::NativeKeyboard::N0}, +        {Qt::Key_Return, Settings::NativeKeyboard::Return}, +        {Qt::Key_Escape, Settings::NativeKeyboard::Escape}, +        {Qt::Key_Backspace, Settings::NativeKeyboard::Backspace}, +        {Qt::Key_Tab, Settings::NativeKeyboard::Tab}, +        {Qt::Key_Space, Settings::NativeKeyboard::Space}, +        {Qt::Key_Minus, Settings::NativeKeyboard::Minus}, +        {Qt::Key_Plus, Settings::NativeKeyboard::Plus}, +        {Qt::Key_questiondown, Settings::NativeKeyboard::Plus}, +        {Qt::Key_BracketLeft, Settings::NativeKeyboard::OpenBracket}, +        {Qt::Key_BraceLeft, Settings::NativeKeyboard::OpenBracket}, +        {Qt::Key_BracketRight, Settings::NativeKeyboard::CloseBracket}, +        {Qt::Key_BraceRight, Settings::NativeKeyboard::CloseBracket}, +        {Qt::Key_Bar, Settings::NativeKeyboard::Pipe}, +        {Qt::Key_Dead_Tilde, Settings::NativeKeyboard::Tilde}, +        {Qt::Key_Ntilde, Settings::NativeKeyboard::Semicolon}, +        {Qt::Key_Semicolon, Settings::NativeKeyboard::Semicolon}, +        {Qt::Key_Apostrophe, Settings::NativeKeyboard::Quote}, +        {Qt::Key_Dead_Grave, Settings::NativeKeyboard::Backquote}, +        {Qt::Key_Comma, Settings::NativeKeyboard::Comma}, +        {Qt::Key_Period, Settings::NativeKeyboard::Period}, +        {Qt::Key_Slash, Settings::NativeKeyboard::Slash}, +        {Qt::Key_CapsLock, Settings::NativeKeyboard::CapsLockKey}, +        {Qt::Key_F1, Settings::NativeKeyboard::F1}, +        {Qt::Key_F2, Settings::NativeKeyboard::F2}, +        {Qt::Key_F3, Settings::NativeKeyboard::F3}, +        {Qt::Key_F4, Settings::NativeKeyboard::F4}, +        {Qt::Key_F5, Settings::NativeKeyboard::F5}, +        {Qt::Key_F6, Settings::NativeKeyboard::F6}, +        {Qt::Key_F7, Settings::NativeKeyboard::F7}, +        {Qt::Key_F8, Settings::NativeKeyboard::F8}, +        {Qt::Key_F9, Settings::NativeKeyboard::F9}, +        {Qt::Key_F10, Settings::NativeKeyboard::F10}, +        {Qt::Key_F11, Settings::NativeKeyboard::F11}, +        {Qt::Key_F12, Settings::NativeKeyboard::F12}, +        {Qt::Key_Print, Settings::NativeKeyboard::PrintScreen}, +        {Qt::Key_ScrollLock, Settings::NativeKeyboard::ScrollLockKey}, +        {Qt::Key_Pause, Settings::NativeKeyboard::Pause}, +        {Qt::Key_Insert, Settings::NativeKeyboard::Insert}, +        {Qt::Key_Home, Settings::NativeKeyboard::Home}, +        {Qt::Key_PageUp, Settings::NativeKeyboard::PageUp}, +        {Qt::Key_Delete, Settings::NativeKeyboard::Delete}, +        {Qt::Key_End, Settings::NativeKeyboard::End}, +        {Qt::Key_PageDown, Settings::NativeKeyboard::PageDown}, +        {Qt::Key_Right, Settings::NativeKeyboard::Right}, +        {Qt::Key_Left, Settings::NativeKeyboard::Left}, +        {Qt::Key_Down, Settings::NativeKeyboard::Down}, +        {Qt::Key_Up, Settings::NativeKeyboard::Up}, +        {Qt::Key_NumLock, Settings::NativeKeyboard::NumLockKey}, +        // Numpad keys are missing here +        {Qt::Key_F13, Settings::NativeKeyboard::F13}, +        {Qt::Key_F14, Settings::NativeKeyboard::F14}, +        {Qt::Key_F15, Settings::NativeKeyboard::F15}, +        {Qt::Key_F16, Settings::NativeKeyboard::F16}, +        {Qt::Key_F17, Settings::NativeKeyboard::F17}, +        {Qt::Key_F18, Settings::NativeKeyboard::F18}, +        {Qt::Key_F19, Settings::NativeKeyboard::F19}, +        {Qt::Key_F20, Settings::NativeKeyboard::F20}, +        {Qt::Key_F21, Settings::NativeKeyboard::F21}, +        {Qt::Key_F22, Settings::NativeKeyboard::F22}, +        {Qt::Key_F23, Settings::NativeKeyboard::F23}, +        {Qt::Key_F24, Settings::NativeKeyboard::F24}, +        // {Qt::..., Settings::NativeKeyboard::KPComma}, +        // {Qt::..., Settings::NativeKeyboard::Ro}, +        {Qt::Key_Hiragana_Katakana, Settings::NativeKeyboard::KatakanaHiragana}, +        {Qt::Key_yen, Settings::NativeKeyboard::Yen}, +        {Qt::Key_Henkan, Settings::NativeKeyboard::Henkan}, +        {Qt::Key_Muhenkan, Settings::NativeKeyboard::Muhenkan}, +        // {Qt::..., Settings::NativeKeyboard::NumPadCommaPc98}, +        {Qt::Key_Hangul, Settings::NativeKeyboard::HangulEnglish}, +        {Qt::Key_Hangul_Hanja, Settings::NativeKeyboard::Hanja}, +        {Qt::Key_Katakana, Settings::NativeKeyboard::KatakanaKey}, +        {Qt::Key_Hiragana, Settings::NativeKeyboard::HiraganaKey}, +        {Qt::Key_Zenkaku_Hankaku, Settings::NativeKeyboard::ZenkakuHankaku}, +        // Modifier keys are handled by the modifier property +    }; + +    for (const auto& [qkey, nkey] : key_map) { +        if (qt_key == qkey) { +            return nkey; +        }      } + +    return Settings::NativeKeyboard::None;  }  int GRenderWindow::QtModifierToSwitchModifier(Qt::KeyboardModifiers qt_modifiers) { diff --git a/src/yuzu/compatdb.cpp b/src/yuzu/compatdb.cpp index f46fff340..b03e71248 100644 --- a/src/yuzu/compatdb.cpp +++ b/src/yuzu/compatdb.cpp @@ -15,12 +15,22 @@ CompatDB::CompatDB(Core::TelemetrySession& telemetry_session_, QWidget* parent)      : QWizard(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint),        ui{std::make_unique<Ui::CompatDB>()}, telemetry_session{telemetry_session_} {      ui->setupUi(this); -    connect(ui->radioButton_Perfect, &QRadioButton::clicked, this, &CompatDB::EnableNext); -    connect(ui->radioButton_Great, &QRadioButton::clicked, this, &CompatDB::EnableNext); -    connect(ui->radioButton_Okay, &QRadioButton::clicked, this, &CompatDB::EnableNext); -    connect(ui->radioButton_Bad, &QRadioButton::clicked, this, &CompatDB::EnableNext); -    connect(ui->radioButton_IntroMenu, &QRadioButton::clicked, this, &CompatDB::EnableNext); -    connect(ui->radioButton_WontBoot, &QRadioButton::clicked, this, &CompatDB::EnableNext); + +    connect(ui->radioButton_GameBoot_Yes, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_GameBoot_No, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_Gameplay_Yes, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_Gameplay_No, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_NoFreeze_Yes, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_NoFreeze_No, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_Complete_Yes, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_Complete_No, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_Graphical_Major, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_Graphical_Minor, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_Graphical_No, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_Audio_Major, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_Audio_Minor, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_Audio_No, &QRadioButton::clicked, this, &CompatDB::EnableNext); +      connect(button(NextButton), &QPushButton::clicked, this, &CompatDB::Submit);      connect(&testcase_watcher, &QFutureWatcher<bool>::finished, this,              &CompatDB::OnTestcaseSubmitted); @@ -30,29 +40,82 @@ CompatDB::~CompatDB() = default;  enum class CompatDBPage {      Intro = 0, -    Selection = 1, -    Final = 2, +    GameBoot = 1, +    GamePlay = 2, +    Freeze = 3, +    Completion = 4, +    Graphical = 5, +    Audio = 6, +    Final = 7,  };  void CompatDB::Submit() { -    QButtonGroup* compatibility = new QButtonGroup(this); -    compatibility->addButton(ui->radioButton_Perfect, 0); -    compatibility->addButton(ui->radioButton_Great, 1); -    compatibility->addButton(ui->radioButton_Okay, 2); -    compatibility->addButton(ui->radioButton_Bad, 3); -    compatibility->addButton(ui->radioButton_IntroMenu, 4); -    compatibility->addButton(ui->radioButton_WontBoot, 5); +    QButtonGroup* compatibility_GameBoot = new QButtonGroup(this); +    compatibility_GameBoot->addButton(ui->radioButton_GameBoot_Yes, 0); +    compatibility_GameBoot->addButton(ui->radioButton_GameBoot_No, 1); + +    QButtonGroup* compatibility_Gameplay = new QButtonGroup(this); +    compatibility_Gameplay->addButton(ui->radioButton_Gameplay_Yes, 0); +    compatibility_Gameplay->addButton(ui->radioButton_Gameplay_No, 1); + +    QButtonGroup* compatibility_NoFreeze = new QButtonGroup(this); +    compatibility_NoFreeze->addButton(ui->radioButton_NoFreeze_Yes, 0); +    compatibility_NoFreeze->addButton(ui->radioButton_NoFreeze_No, 1); + +    QButtonGroup* compatibility_Complete = new QButtonGroup(this); +    compatibility_Complete->addButton(ui->radioButton_Complete_Yes, 0); +    compatibility_Complete->addButton(ui->radioButton_Complete_No, 1); + +    QButtonGroup* compatibility_Graphical = new QButtonGroup(this); +    compatibility_Graphical->addButton(ui->radioButton_Graphical_Major, 0); +    compatibility_Graphical->addButton(ui->radioButton_Graphical_Minor, 1); +    compatibility_Graphical->addButton(ui->radioButton_Graphical_No, 2); + +    QButtonGroup* compatibility_Audio = new QButtonGroup(this); +    compatibility_Audio->addButton(ui->radioButton_Audio_Major, 0); +    compatibility_Graphical->addButton(ui->radioButton_Audio_Minor, 1); +    compatibility_Audio->addButton(ui->radioButton_Audio_No, 2); + +    const int compatiblity = static_cast<int>(CalculateCompatibility()); +      switch ((static_cast<CompatDBPage>(currentId()))) { -    case CompatDBPage::Selection: -        if (compatibility->checkedId() == -1) { +    case CompatDBPage::Intro: +        break; +    case CompatDBPage::GameBoot: +        if (compatibility_GameBoot->checkedId() == -1) { +            button(NextButton)->setEnabled(false); +        } +        break; +    case CompatDBPage::GamePlay: +        if (compatibility_Gameplay->checkedId() == -1) { +            button(NextButton)->setEnabled(false); +        } +        break; +    case CompatDBPage::Freeze: +        if (compatibility_NoFreeze->checkedId() == -1) { +            button(NextButton)->setEnabled(false); +        } +        break; +    case CompatDBPage::Completion: +        if (compatibility_Complete->checkedId() == -1) { +            button(NextButton)->setEnabled(false); +        } +        break; +    case CompatDBPage::Graphical: +        if (compatibility_Graphical->checkedId() == -1) { +            button(NextButton)->setEnabled(false); +        } +        break; +    case CompatDBPage::Audio: +        if (compatibility_Audio->checkedId() == -1) {              button(NextButton)->setEnabled(false);          }          break;      case CompatDBPage::Final:          back(); -        LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId()); +        LOG_INFO(Frontend, "Compatibility Rating: {}", compatiblity);          telemetry_session.AddField(Common::Telemetry::FieldType::UserFeedback, "Compatibility", -                                   compatibility->checkedId()); +                                   compatiblity);          button(NextButton)->setEnabled(false);          button(NextButton)->setText(tr("Submitting")); @@ -66,6 +129,66 @@ void CompatDB::Submit() {      }  } +int CompatDB::nextId() const { +    switch ((static_cast<CompatDBPage>(currentId()))) { +    case CompatDBPage::Intro: +        return static_cast<int>(CompatDBPage::GameBoot); +    case CompatDBPage::GameBoot: +        if (ui->radioButton_GameBoot_No->isChecked()) { +            return static_cast<int>(CompatDBPage::Final); +        } +        return static_cast<int>(CompatDBPage::GamePlay); +    case CompatDBPage::GamePlay: +        if (ui->radioButton_Gameplay_No->isChecked()) { +            return static_cast<int>(CompatDBPage::Final); +        } +        return static_cast<int>(CompatDBPage::Freeze); +    case CompatDBPage::Freeze: +        if (ui->radioButton_NoFreeze_No->isChecked()) { +            return static_cast<int>(CompatDBPage::Final); +        } +        return static_cast<int>(CompatDBPage::Completion); +    case CompatDBPage::Completion: +        if (ui->radioButton_Complete_No->isChecked()) { +            return static_cast<int>(CompatDBPage::Final); +        } +        return static_cast<int>(CompatDBPage::Graphical); +    case CompatDBPage::Graphical: +        return static_cast<int>(CompatDBPage::Audio); +    case CompatDBPage::Audio: +        return static_cast<int>(CompatDBPage::Final); +    case CompatDBPage::Final: +        return -1; +    default: +        LOG_ERROR(Frontend, "Unexpected page: {}", currentId()); +        return static_cast<int>(CompatDBPage::Intro); +    } +} + +CompatibilityStatus CompatDB::CalculateCompatibility() const { +    if (ui->radioButton_GameBoot_No->isChecked()) { +        return CompatibilityStatus::WontBoot; +    } + +    if (ui->radioButton_Gameplay_No->isChecked()) { +        return CompatibilityStatus::IntroMenu; +    } + +    if (ui->radioButton_NoFreeze_No->isChecked() || ui->radioButton_Complete_No->isChecked()) { +        return CompatibilityStatus::Ingame; +    } + +    if (ui->radioButton_Graphical_Major->isChecked() || ui->radioButton_Audio_Major->isChecked()) { +        return CompatibilityStatus::Ingame; +    } + +    if (ui->radioButton_Graphical_Minor->isChecked() || ui->radioButton_Audio_Minor->isChecked()) { +        return CompatibilityStatus::Playable; +    } + +    return CompatibilityStatus::Perfect; +} +  void CompatDB::OnTestcaseSubmitted() {      if (!testcase_watcher.result()) {          QMessageBox::critical(this, tr("Communication error"), diff --git a/src/yuzu/compatdb.h b/src/yuzu/compatdb.h index 3252fc47a..37e11278b 100644 --- a/src/yuzu/compatdb.h +++ b/src/yuzu/compatdb.h @@ -12,12 +12,22 @@ namespace Ui {  class CompatDB;  } +enum class CompatibilityStatus { +    Perfect = 0, +    Playable = 1, +    // Unused: Okay = 2, +    Ingame = 3, +    IntroMenu = 4, +    WontBoot = 5, +}; +  class CompatDB : public QWizard {      Q_OBJECT  public:      explicit CompatDB(Core::TelemetrySession& telemetry_session_, QWidget* parent = nullptr);      ~CompatDB(); +    int nextId() const override;  private:      QFutureWatcher<bool> testcase_watcher; @@ -25,6 +35,7 @@ private:      std::unique_ptr<Ui::CompatDB> ui;      void Submit(); +    CompatibilityStatus CalculateCompatibility() const;      void OnTestcaseSubmitted();      void EnableNext(); diff --git a/src/yuzu/compatdb.ui b/src/yuzu/compatdb.ui index 3ca55eda6..d11669df2 100644 --- a/src/yuzu/compatdb.ui +++ b/src/yuzu/compatdb.ui @@ -58,128 +58,311 @@      </item>     </layout>    </widget> -  <widget class="QWizardPage" name="wizard_Report"> +  <widget class="QWizardPage" name="wizard_GameBoot">     <property name="title">      <string>Report Game Compatibility</string>     </property>     <attribute name="pageId">      <string notr="true">1</string>     </attribute> -   <layout class="QFormLayout" name="formLayout"> +   <layout class="QFormLayout" name="formLayout1"> +    <item row="0" column="0" colspan="2"> +     <widget class="QLabel" name="lbl_Independent1"> +      <property name="font"> +       <font> +        <pointsize>10</pointsize> +       </font> +      </property> +      <property name="text"> +       <string><html><head/><body><p>Does the game boot?</p></body></html></string> +      </property> +      <property name="wordWrap"> +       <bool>true</bool> +      </property> +     </widget> +    </item> +    <item row="1" column="0" colspan="2"> +     <spacer name="verticalSpacer1"> +      <property name="orientation"> +       <enum>Qt::Vertical</enum> +      </property> +      <property name="sizeHint" stdset="0"> +       <size> +        <width>20</width> +        <height>0</height> +       </size> +      </property> +     </spacer> +    </item>      <item row="2" column="0"> -     <widget class="QRadioButton" name="radioButton_Perfect"> +     <widget class="QRadioButton" name="radioButton_GameBoot_Yes">        <property name="text"> -       <string>Perfect</string> +       <string>Yes   The game starts to output video or audio</string>        </property>       </widget>      </item> -    <item row="2" column="1"> -     <widget class="QLabel" name="lbl_Perfect"> +    <item row="4" column="0"> +     <widget class="QRadioButton" name="radioButton_GameBoot_No">        <property name="text"> -       <string><html><head/><body><p>Game functions flawlessly with no audio or graphical glitches.</p></body></html></string> +       <string>No    The game doesn't get past the "Launching..." screen</string>        </property> -      <property name="wordWrap"> -       <bool>true</bool> +     </widget> +    </item> +   </layout> +  </widget> +  <widget class="QWizardPage" name="wizard_GamePlay"> +   <property name="title"> +    <string>Report Game Compatibility</string> +   </property> +   <attribute name="pageId"> +    <string notr="true">2</string> +   </attribute> +   <layout class="QFormLayout" name="formLayout2"> +    <item row="2" column="0"> +     <widget class="QRadioButton" name="radioButton_Gameplay_Yes"> +      <property name="text"> +       <string>Yes   The game gets past the intro/menu and into gameplay</string>        </property>       </widget>      </item>      <item row="4" column="0"> -     <widget class="QRadioButton" name="radioButton_Great"> +     <widget class="QRadioButton" name="radioButton_Gameplay_No">        <property name="text"> -       <string>Great</string> +       <string>No    The game crashes or freezes while loading or using the menu</string>        </property>       </widget>      </item> -    <item row="4" column="1"> -     <widget class="QLabel" name="lbl_Great"> +    <item row="0" column="0" colspan="2"> +     <widget class="QLabel" name="lbl_Independent2"> +      <property name="font"> +       <font> +        <pointsize>10</pointsize> +       </font> +      </property>        <property name="text"> -       <string><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> +       <string><html><head/><body><p>Does the game reach gameplay?</p></body></html></string>        </property>        <property name="wordWrap">         <bool>true</bool>        </property>       </widget>      </item> -    <item row="5" column="0"> -     <widget class="QRadioButton" name="radioButton_Okay"> +    <item row="1" column="0" colspan="2"> +     <spacer name="verticalSpacer2"> +      <property name="orientation"> +       <enum>Qt::Vertical</enum> +      </property> +      <property name="sizeHint" stdset="0"> +       <size> +        <width>20</width> +        <height>0</height> +       </size> +      </property> +     </spacer> +    </item> +   </layout> +  </widget> +  <widget class="QWizardPage" name="wizard_NoFreeze"> +   <property name="title"> +    <string>Report Game Compatibility</string> +   </property> +   <attribute name="pageId"> +    <string notr="true">3</string> +   </attribute> +   <layout class="QFormLayout" name="formLayout3"> +    <item row="2" column="0"> +     <widget class="QRadioButton" name="radioButton_NoFreeze_Yes">        <property name="text"> -       <string>Okay</string> +       <string>Yes   The game works without crashes</string>        </property>       </widget>      </item> -    <item row="5" column="1"> -     <widget class="QLabel" name="lbl_Okay"> +    <item row="4" column="0"> +     <widget class="QRadioButton" name="radioButton_NoFreeze_No"> +      <property name="text"> +       <string>No    The game crashes or freezes during gameplay</string> +      </property> +     </widget> +    </item> +    <item row="0" column="0" colspan="2"> +     <widget class="QLabel" name="lbl_Independent3"> +      <property name="font"> +       <font> +        <pointsize>10</pointsize> +       </font> +      </property>        <property name="text"> -       <string><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> +       <string><html><head/><body><p>Does the game work without crashing, freezing or locking up during gameplay?</p></body></html></string>        </property>        <property name="wordWrap">         <bool>true</bool>        </property>       </widget>      </item> -    <item row="6" column="0"> -     <widget class="QRadioButton" name="radioButton_Bad"> +    <item row="1" column="0" colspan="2"> +     <spacer name="verticalSpacer3"> +      <property name="orientation"> +       <enum>Qt::Vertical</enum> +      </property> +      <property name="sizeHint" stdset="0"> +       <size> +        <width>20</width> +        <height>0</height> +       </size> +      </property> +     </spacer> +    </item> +   </layout> +  </widget> +  <widget class="QWizardPage" name="wizard_Complete"> +   <property name="title"> +    <string>Report Game Compatibility</string> +   </property> +   <attribute name="pageId"> +    <string notr="true">4</string> +   </attribute> +   <layout class="QFormLayout" name="formLayout4"> +    <item row="2" column="0"> +     <widget class="QRadioButton" name="radioButton_Complete_Yes">        <property name="text"> -       <string>Bad</string> +       <string>Yes   The game can be finished without any workarounds</string>        </property>       </widget>      </item> -    <item row="6" column="1"> -     <widget class="QLabel" name="lbl_Bad"> +    <item row="4" column="0"> +     <widget class="QRadioButton" name="radioButton_Complete_No"> +      <property name="text"> +       <string>No    The game can't progress past a certain area</string> +      </property> +     </widget> +    </item> +    <item row="0" column="0" colspan="2"> +     <widget class="QLabel" name="lbl_Independent4"> +      <property name="font"> +       <font> +        <pointsize>10</pointsize> +       </font> +      </property>        <property name="text"> -       <string><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> +       <string><html><head/><body><p>Is the game completely playable from start to finish?</p></body></html></string>        </property>        <property name="wordWrap">         <bool>true</bool>        </property>       </widget>      </item> -    <item row="7" column="0"> -     <widget class="QRadioButton" name="radioButton_IntroMenu"> +    <item row="1" column="0" colspan="2"> +     <spacer name="verticalSpacer4"> +      <property name="orientation"> +       <enum>Qt::Vertical</enum> +      </property> +      <property name="sizeHint" stdset="0"> +       <size> +        <width>20</width> +        <height>0</height> +       </size> +      </property> +     </spacer> +    </item> +   </layout> +  </widget> +  <widget class="QWizardPage" name="wizard_Graphical"> +   <property name="title"> +    <string>Report Game Compatibility</string> +   </property> +   <attribute name="pageId"> +    <string notr="true">5</string> +   </attribute> +   <layout class="QFormLayout" name="formLayout5"> +    <item row="2" column="0"> +     <widget class="QRadioButton" name="radioButton_Graphical_Major"> +      <property name="text"> +       <string>Major   The game has major graphical errors</string> +      </property> +     </widget> +    </item> +    <item row="4" column="0"> +     <widget class="QRadioButton" name="radioButton_Graphical_Minor">        <property name="text"> -       <string>Intro/Menu</string> +       <string>Minor   The game has minor graphical errors</string>        </property>       </widget>      </item> -    <item row="7" column="1"> -     <widget class="QLabel" name="lbl_IntroMenu"> +    <item row="6" column="0"> +     <widget class="QRadioButton" name="radioButton_Graphical_No"> +      <property name="text"> +       <string>None     Everything is rendered as it looks on the Nintendo Switch</string> +      </property> +     </widget> +    </item> +    <item row="0" column="0" colspan="2"> +     <widget class="QLabel" name="lbl_Independent5"> +      <property name="font"> +       <font> +        <pointsize>10</pointsize> +       </font> +      </property>        <property name="text"> -       <string><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> +       <string><html><head/><body><p>Does the game have any graphical glitches?</p></body></html></string>        </property>        <property name="wordWrap">         <bool>true</bool>        </property>       </widget>      </item> -    <item row="8" column="0"> -     <widget class="QRadioButton" name="radioButton_WontBoot"> -      <property name="text"> -       <string>Won't Boot</string> +    <item row="1" column="0" colspan="2"> +     <spacer name="verticalSpacer5"> +      <property name="orientation"> +       <enum>Qt::Vertical</enum>        </property> -      <property name="checkable"> -       <bool>true</bool> +      <property name="sizeHint" stdset="0"> +       <size> +        <width>20</width> +        <height>0</height> +       </size>        </property> -      <property name="checked"> -       <bool>false</bool> +     </spacer> +    </item> +   </layout> +  </widget> +  <widget class="QWizardPage" name="wizard_Audio"> +   <property name="title"> +    <string>Report Game Compatibility</string> +   </property> +   <attribute name="pageId"> +    <string notr="true">6</string> +   </attribute> +   <layout class="QFormLayout" name="formLayout6"> +    <item row="2" column="0"> +     <widget class="QRadioButton" name="radioButton_Audio_Major"> +      <property name="text"> +       <string>Major   The game has major audio errors</string> +      </property> +     </widget> +    </item> +    <item row="4" column="0"> +     <widget class="QRadioButton" name="radioButton_Audio_Minor"> +      <property name="text"> +       <string>Minor   The game has minor audio errors</string>        </property>       </widget>      </item> -    <item row="8" column="1"> -     <widget class="QLabel" name="lbl_WontBoot"> +    <item row="6" column="0"> +     <widget class="QRadioButton" name="radioButton_Audio_No">        <property name="text"> -       <string><html><head/><body><p>The game crashes when attempting to startup.</p></body></html></string> +       <string>None     Audio is played perfectly</string>        </property>       </widget>      </item>      <item row="0" column="0" colspan="2"> -     <widget class="QLabel" name="lbl_Independent"> +     <widget class="QLabel" name="lbl_Independent6">        <property name="font">         <font>          <pointsize>10</pointsize>         </font>        </property>        <property name="text"> -       <string><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> +       <string><html><head/><body><p>Does the game have any audio glitches / missing effects?</p></body></html></string>        </property>        <property name="wordWrap">         <bool>true</bool> @@ -187,7 +370,7 @@       </widget>      </item>      <item row="1" column="0" colspan="2"> -     <spacer name="verticalSpacer"> +     <spacer name="verticalSpacer6">        <property name="orientation">         <enum>Qt::Vertical</enum>        </property> @@ -206,7 +389,7 @@      <string>Thank you for your submission!</string>     </property>     <attribute name="pageId"> -    <string notr="true">2</string> +    <string notr="true">7</string>     </attribute>    </widget>   </widget> diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index 6198d1e4e..1800f090f 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h @@ -145,12 +145,14 @@ public:              const char* tooltip;          };          // clang-format off +        const auto ingame_status = +                       CompatStatus{QStringLiteral("#f2d624"), QT_TR_NOOP("Ingame"),     QT_TR_NOOP("Game starts, but crashes or major glitches prevent it from being completed.")};          static const std::map<QString, CompatStatus> status_data = { -            {QStringLiteral("0"),  {QStringLiteral("#5c93ed"), QT_TR_NOOP("Perfect"),    QT_TR_NOOP("Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without\nany workarounds needed.")}}, -            {QStringLiteral("1"),  {QStringLiteral("#47d35c"), QT_TR_NOOP("Great"),      QT_TR_NOOP("Game functions with minor graphical or audio glitches and is playable from start to finish. May require some\nworkarounds.")}}, -            {QStringLiteral("2"),  {QStringLiteral("#94b242"), QT_TR_NOOP("Okay"),       QT_TR_NOOP("Game functions with major graphical or audio glitches, but game is playable from start to finish with\nworkarounds.")}}, -            {QStringLiteral("3"),  {QStringLiteral("#f2d624"), QT_TR_NOOP("Bad"),        QT_TR_NOOP("Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches\neven with workarounds.")}}, -            {QStringLiteral("4"),  {QStringLiteral("#FF0000"), QT_TR_NOOP("Intro/Menu"), QT_TR_NOOP("Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start\nScreen.")}}, +            {QStringLiteral("0"),  {QStringLiteral("#5c93ed"), QT_TR_NOOP("Perfect"),    QT_TR_NOOP("Game can be played without issues.")}}, +            {QStringLiteral("1"),  {QStringLiteral("#47d35c"), QT_TR_NOOP("Playable"),   QT_TR_NOOP("Game functions with minor graphical or audio glitches and is playable from start to finish.")}}, +            {QStringLiteral("2"),  ingame_status}, +            {QStringLiteral("3"),  ingame_status}, // Fallback for the removed "Okay" category +            {QStringLiteral("4"),  {QStringLiteral("#FF0000"), QT_TR_NOOP("Intro/Menu"), QT_TR_NOOP("Game loads, but is unable to progress past the Start Screen.")}},              {QStringLiteral("5"),  {QStringLiteral("#828282"), QT_TR_NOOP("Won't Boot"), QT_TR_NOOP("The game crashes when attempting to startup.")}},              {QStringLiteral("99"), {QStringLiteral("#000000"), QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}},          }; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 59e56633a..032ff1cbc 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -342,6 +342,7 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan      const auto override_build =          fmt::format(fmt::runtime(std::string(Common::g_title_bar_format_idle)), build_id);      const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build; +    const auto processor_count = std::thread::hardware_concurrency();      LOG_INFO(Frontend, "yuzu Version: {}", yuzu_build_version);      LogRuntimes(); @@ -361,6 +362,11 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan      }      LOG_INFO(Frontend, "Host CPU: {}", cpu_string);  #endif + +    if (std::optional<int> processor_core = Common::GetProcessorCount()) { +        LOG_INFO(Frontend, "Host CPU Cores: {}", *processor_core); +    } +    LOG_INFO(Frontend, "Host CPU Threads: {}", processor_count);      LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString());      LOG_INFO(Frontend, "Host RAM: {:.2f} GiB",               Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB}); @@ -2018,38 +2024,50 @@ static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src      return true;  } +QString GMainWindow::GetGameListErrorRemoving(InstalledEntryType type) const { +    switch (type) { +    case InstalledEntryType::Game: +        return tr("Error Removing Contents"); +    case InstalledEntryType::Update: +        return tr("Error Removing Update"); +    case InstalledEntryType::AddOnContent: +        return tr("Error Removing DLC"); +    default: +        return QStringLiteral("Error Removing <Invalid Type>"); +    } +}  void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type) { -    const QString entry_type = [type] { +    const QString entry_question = [type] {          switch (type) {          case InstalledEntryType::Game: -            return tr("Contents"); +            return tr("Remove Installed Game Contents?");          case InstalledEntryType::Update: -            return tr("Update"); +            return tr("Remove Installed Game Update?");          case InstalledEntryType::AddOnContent: -            return tr("DLC"); +            return tr("Remove Installed Game DLC?");          default: -            return QString{}; +            return QStringLiteral("Remove Installed Game <Invalid Type>?");          }      }(); -    if (QMessageBox::question( -            this, tr("Remove Entry"), tr("Remove Installed Game %1?").arg(entry_type), -            QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) { +    if (QMessageBox::question(this, tr("Remove Entry"), entry_question, +                              QMessageBox::Yes | QMessageBox::No, +                              QMessageBox::No) != QMessageBox::Yes) {          return;      }      switch (type) {      case InstalledEntryType::Game: -        RemoveBaseContent(program_id, entry_type); +        RemoveBaseContent(program_id, type);          [[fallthrough]];      case InstalledEntryType::Update: -        RemoveUpdateContent(program_id, entry_type); +        RemoveUpdateContent(program_id, type);          if (type != InstalledEntryType::Game) {              break;          }          [[fallthrough]];      case InstalledEntryType::AddOnContent: -        RemoveAddOnContent(program_id, entry_type); +        RemoveAddOnContent(program_id, type);          break;      }      Common::FS::RemoveDirRecursively(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / @@ -2057,7 +2075,7 @@ void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryT      game_list->PopulateAsync(UISettings::values.game_dirs);  } -void GMainWindow::RemoveBaseContent(u64 program_id, const QString& entry_type) { +void GMainWindow::RemoveBaseContent(u64 program_id, InstalledEntryType type) {      const auto& fs_controller = system->GetFileSystemController();      const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(program_id) ||                       fs_controller.GetSDMCContents()->RemoveExistingEntry(program_id); @@ -2067,12 +2085,12 @@ void GMainWindow::RemoveBaseContent(u64 program_id, const QString& entry_type) {                                   tr("Successfully removed the installed base game."));      } else {          QMessageBox::warning( -            this, tr("Error Removing %1").arg(entry_type), +            this, GetGameListErrorRemoving(type),              tr("The base game is not installed in the NAND and cannot be removed."));      }  } -void GMainWindow::RemoveUpdateContent(u64 program_id, const QString& entry_type) { +void GMainWindow::RemoveUpdateContent(u64 program_id, InstalledEntryType type) {      const auto update_id = program_id | 0x800;      const auto& fs_controller = system->GetFileSystemController();      const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(update_id) || @@ -2082,12 +2100,12 @@ void GMainWindow::RemoveUpdateContent(u64 program_id, const QString& entry_type)          QMessageBox::information(this, tr("Successfully Removed"),                                   tr("Successfully removed the installed update."));      } else { -        QMessageBox::warning(this, tr("Error Removing %1").arg(entry_type), +        QMessageBox::warning(this, GetGameListErrorRemoving(type),                               tr("There is no update installed for this title."));      }  } -void GMainWindow::RemoveAddOnContent(u64 program_id, const QString& entry_type) { +void GMainWindow::RemoveAddOnContent(u64 program_id, InstalledEntryType type) {      u32 count{};      const auto& fs_controller = system->GetFileSystemController();      const auto dlc_entries = system->GetContentProvider().ListEntriesFilter( @@ -2105,7 +2123,7 @@ void GMainWindow::RemoveAddOnContent(u64 program_id, const QString& entry_type)      }      if (count == 0) { -        QMessageBox::warning(this, tr("Error Removing %1").arg(entry_type), +        QMessageBox::warning(this, GetGameListErrorRemoving(type),                               tr("There are no DLC installed for this title."));          return;      } @@ -2803,6 +2821,20 @@ void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_tex  }  void GMainWindow::OnMenuReportCompatibility() { +    const auto& caps = Common::GetCPUCaps(); +    const bool has_fma = caps.fma || caps.fma4; +    const auto processor_count = std::thread::hardware_concurrency(); +    const bool has_4threads = processor_count == 0 || processor_count >= 4; +    const bool has_8gb_ram = Common::GetMemInfo().TotalPhysicalMemory >= 8_GiB; +    const bool has_broken_vulkan = UISettings::values.has_broken_vulkan; + +    if (!has_fma || !has_4threads || !has_8gb_ram || has_broken_vulkan) { +        QMessageBox::critical(this, tr("Hardware requirements not met"), +                              tr("Your system does not meet the recommended hardware requirements. " +                                 "Compatibility reporting has been disabled.")); +        return; +    } +      if (!Settings::values.yuzu_token.GetValue().empty() &&          !Settings::values.yuzu_username.GetValue().empty()) {          CompatDB compatdb{system->TelemetrySession(), this}; diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 150ada84c..b73f550dd 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -324,9 +324,10 @@ private slots:      void OnMouseActivity();  private: -    void RemoveBaseContent(u64 program_id, const QString& entry_type); -    void RemoveUpdateContent(u64 program_id, const QString& entry_type); -    void RemoveAddOnContent(u64 program_id, const QString& entry_type); +    QString GetGameListErrorRemoving(InstalledEntryType type) const; +    void RemoveBaseContent(u64 program_id, InstalledEntryType type); +    void RemoveUpdateContent(u64 program_id, InstalledEntryType type); +    void RemoveAddOnContent(u64 program_id, InstalledEntryType type);      void RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target);      void RemoveAllTransferableShaderCaches(u64 program_id);      void RemoveCustomConfiguration(u64 program_id, const std::string& game_path); | 
