diff options
76 files changed, 1372 insertions, 567 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/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/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 3bb111748..a86bec252 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h @@ -149,7 +149,7 @@ public: context->AddDomainObject(std::move(iface)); } else { kernel.CurrentProcess()->GetResourceLimit()->Reserve( - Kernel::LimitableResource::Sessions, 1); + Kernel::LimitableResource::SessionCountMax, 1); auto* session = Kernel::KSession::Create(kernel); session->Initialize(nullptr, iface->GetServiceName()); diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index fd354d484..06010b8d1 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -27,16 +27,12 @@ namespace Kernel { SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_, ServiceThreadType thread_type) - : kernel{kernel_} { - if (thread_type == ServiceThreadType::CreateNew) { - service_thread = kernel.CreateServiceThread(service_name_); - } else { - service_thread = kernel.GetDefaultServiceThread(); - } -} + : kernel{kernel_}, service_thread{thread_type == ServiceThreadType::CreateNew + ? kernel.CreateServiceThread(service_name_) + : kernel.GetDefaultServiceThread()} {} SessionRequestHandler::~SessionRequestHandler() { - kernel.ReleaseServiceThread(service_thread.lock()); + kernel.ReleaseServiceThread(service_thread); } void SessionRequestHandler::AcceptSession(KServerPort* server_port) { @@ -49,7 +45,7 @@ void SessionRequestHandler::AcceptSession(KServerPort* server_port) { void SessionRequestHandler::RegisterSession(KServerSession* server_session, std::shared_ptr<SessionRequestManager> manager) { manager->SetSessionHandler(shared_from_this()); - service_thread.lock()->RegisterServerSession(server_session, manager); + service_thread.RegisterServerSession(server_session, manager); server_session->Close(); } diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 67da8e7e1..d87be72d6 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -82,13 +82,13 @@ public: void RegisterSession(KServerSession* server_session, std::shared_ptr<SessionRequestManager> manager); - std::weak_ptr<ServiceThread> GetServiceThread() const { + ServiceThread& GetServiceThread() const { return service_thread; } protected: KernelCore& kernel; - std::weak_ptr<ServiceThread> service_thread; + ServiceThread& service_thread; }; using SessionRequestHandlerWeakPtr = std::weak_ptr<SessionRequestHandler>; @@ -154,7 +154,7 @@ public: session_handler = std::move(handler); } - std::weak_ptr<ServiceThread> GetServiceThread() const { + ServiceThread& GetServiceThread() const { return session_handler->GetServiceThread(); } diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp index aa2dddcc6..bda098511 100644 --- a/src/core/hle/kernel/init/init_slab_setup.cpp +++ b/src/core/hle/kernel/init/init_slab_setup.cpp @@ -265,7 +265,8 @@ void KPageBufferSlabHeap::Initialize(Core::System& system) { const size_t slab_size = num_pages * PageSize; // Reserve memory from the system resource limit. - ASSERT(kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemory, slab_size)); + ASSERT( + kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemoryMax, slab_size)); // Allocate memory for the slab. constexpr auto AllocateOption = KMemoryManager::EncodeOption( diff --git a/src/core/hle/kernel/k_client_port.cpp b/src/core/hle/kernel/k_client_port.cpp index eaa2e094c..2ec623a58 100644 --- a/src/core/hle/kernel/k_client_port.cpp +++ b/src/core/hle/kernel/k_client_port.cpp @@ -61,7 +61,7 @@ bool KClientPort::IsSignaled() const { Result KClientPort::CreateSession(KClientSession** out) { // Reserve a new session from the resource limit. KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(), - LimitableResource::Sessions); + LimitableResource::SessionCountMax); R_UNLESS(session_reservation.Succeeded(), ResultLimitReached); // Update the session counts. diff --git a/src/core/hle/kernel/k_event.cpp b/src/core/hle/kernel/k_event.cpp index 78ca59463..27f70e5c5 100644 --- a/src/core/hle/kernel/k_event.cpp +++ b/src/core/hle/kernel/k_event.cpp @@ -50,7 +50,7 @@ Result KEvent::Clear() { void KEvent::PostDestroy(uintptr_t arg) { // Release the event count resource the owner process holds. KProcess* owner = reinterpret_cast<KProcess*>(arg); - owner->GetResourceLimit()->Release(LimitableResource::Events, 1); + owner->GetResourceLimit()->Release(LimitableResource::EventCountMax, 1); owner->Close(); } diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h index 6f845d675..3b6e7baff 100644 --- a/src/core/hle/kernel/k_memory_block.h +++ b/src/core/hle/kernel/k_memory_block.h @@ -216,13 +216,15 @@ struct KMemoryInfo { constexpr Svc::MemoryInfo GetSvcMemoryInfo() const { return { - .addr = m_address, + .base_address = m_address, .size = m_size, .state = static_cast<Svc::MemoryState>(m_state & KMemoryState::Mask), - .attr = static_cast<Svc::MemoryAttribute>(m_attribute & KMemoryAttribute::UserMask), - .perm = static_cast<Svc::MemoryPermission>(m_permission & KMemoryPermission::UserMask), - .ipc_refcount = m_ipc_lock_count, - .device_refcount = m_device_use_count, + .attribute = + static_cast<Svc::MemoryAttribute>(m_attribute & KMemoryAttribute::UserMask), + .permission = + static_cast<Svc::MemoryPermission>(m_permission & KMemoryPermission::UserMask), + .ipc_count = m_ipc_lock_count, + .device_count = m_device_use_count, .padding = {}, }; } diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index fab55a057..5387bf5fe 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp @@ -920,8 +920,8 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add // Reserve space for any partial pages we allocate. const size_t unmapped_size = aligned_src_size - mapping_src_size; - KScopedResourceReservation memory_reservation(m_resource_limit, - LimitableResource::PhysicalMemory, unmapped_size); + KScopedResourceReservation memory_reservation( + m_resource_limit, LimitableResource::PhysicalMemoryMax, unmapped_size); R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); // Ensure that we manage page references correctly. @@ -1227,7 +1227,7 @@ Result KPageTable::CleanupForIpcServer(VAddr address, size_t size, KMemoryState const VAddr mapping_start = Common::AlignUp((address), PageSize); const VAddr mapping_end = Common::AlignDown((address) + size, PageSize); const size_t mapping_size = (mapping_start < mapping_end) ? mapping_end - mapping_start : 0; - m_resource_limit->Release(LimitableResource::PhysicalMemory, aligned_size - mapping_size); + m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, aligned_size - mapping_size); R_SUCCEED(); } @@ -1568,7 +1568,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) { { // Reserve the memory from the process resource limit. KScopedResourceReservation memory_reservation( - m_resource_limit, LimitableResource::PhysicalMemory, size - mapped_size); + m_resource_limit, LimitableResource::PhysicalMemoryMax, size - mapped_size); R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); // Allocate pages for the new memory. @@ -1908,7 +1908,7 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) { // Release the memory resource. m_mapped_physical_memory_size -= mapped_size; - m_resource_limit->Release(LimitableResource::PhysicalMemory, mapped_size); + m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, mapped_size); // Update memory blocks. m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize, @@ -2492,7 +2492,7 @@ Result KPageTable::SetHeapSize(VAddr* out, size_t size) { OperationType::Unmap)); // Release the memory from the resource limit. - m_resource_limit->Release(LimitableResource::PhysicalMemory, num_pages * PageSize); + m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, num_pages * PageSize); // Apply the memory block update. m_memory_block_manager.Update(std::addressof(allocator), m_heap_region_start + size, @@ -2522,7 +2522,7 @@ Result KPageTable::SetHeapSize(VAddr* out, size_t size) { // Reserve memory for the heap extension. KScopedResourceReservation memory_reservation( - m_resource_limit, LimitableResource::PhysicalMemory, allocation_size); + m_resource_limit, LimitableResource::PhysicalMemoryMax, allocation_size); R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); // Allocate pages for the heap extension. diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 4ddeea73b..55a9c5fae 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -38,7 +38,7 @@ namespace { */ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority, VAddr stack_top) { const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart(); - ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::Threads, 1)); + ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::ThreadCountMax, 1)); KThread* thread = KThread::Create(system.Kernel()); SCOPE_EXIT({ thread->Close(); }); @@ -124,7 +124,7 @@ void KProcess::DecrementRunningThreadCount() { } u64 KProcess::GetTotalPhysicalMemoryAvailable() { - const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) + + const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemoryMax) + page_table.GetNormalMemorySize() + GetSystemResourceSize() + image_size + main_thread_stack_size}; if (const auto pool_size = kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application); @@ -349,8 +349,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std: // We currently do not support process-specific system resource UNIMPLEMENTED_IF(system_resource_size != 0); - KScopedResourceReservation memory_reservation(resource_limit, LimitableResource::PhysicalMemory, - code_size + system_resource_size); + KScopedResourceReservation memory_reservation( + resource_limit, LimitableResource::PhysicalMemoryMax, code_size + system_resource_size); if (!memory_reservation.Succeeded()) { LOG_ERROR(Kernel, "Could not reserve process memory requirements of size {:X} bytes", code_size + system_resource_size); @@ -406,8 +406,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std: void KProcess::Run(s32 main_thread_priority, u64 stack_size) { AllocateMainThreadStack(stack_size); - resource_limit->Reserve(LimitableResource::Threads, 1); - resource_limit->Reserve(LimitableResource::PhysicalMemory, main_thread_stack_size); + resource_limit->Reserve(LimitableResource::ThreadCountMax, 1); + resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, main_thread_stack_size); const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)}; ASSERT(!page_table.SetMaxHeapSize(heap_capacity).IsError()); @@ -442,7 +442,7 @@ void KProcess::PrepareForTermination() { plr_address = 0; if (resource_limit) { - resource_limit->Release(LimitableResource::PhysicalMemory, + resource_limit->Release(LimitableResource::PhysicalMemoryMax, main_thread_stack_size + image_size); } diff --git a/src/core/hle/kernel/k_resource_limit.cpp b/src/core/hle/kernel/k_resource_limit.cpp index 010dcf99e..b9d22b414 100644 --- a/src/core/hle/kernel/k_resource_limit.cpp +++ b/src/core/hle/kernel/k_resource_limit.cpp @@ -159,12 +159,13 @@ KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical // TODO(bunnei): These values are the system defaults, the limits for service processes are // lower. These should use the correct limit values. - ASSERT(resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, physical_memory_size) + ASSERT(resource_limit->SetLimitValue(LimitableResource::PhysicalMemoryMax, physical_memory_size) .IsSuccess()); - ASSERT(resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess()); - ASSERT(resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess()); - ASSERT(resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200).IsSuccess()); - ASSERT(resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess()); + ASSERT(resource_limit->SetLimitValue(LimitableResource::ThreadCountMax, 800).IsSuccess()); + ASSERT(resource_limit->SetLimitValue(LimitableResource::EventCountMax, 900).IsSuccess()); + ASSERT( + resource_limit->SetLimitValue(LimitableResource::TransferMemoryCountMax, 200).IsSuccess()); + ASSERT(resource_limit->SetLimitValue(LimitableResource::SessionCountMax, 1133).IsSuccess()); return resource_limit; } diff --git a/src/core/hle/kernel/k_resource_limit.h b/src/core/hle/kernel/k_resource_limit.h index 65c98c979..2573d1b7c 100644 --- a/src/core/hle/kernel/k_resource_limit.h +++ b/src/core/hle/kernel/k_resource_limit.h @@ -16,15 +16,8 @@ class CoreTiming; namespace Kernel { class KernelCore; -enum class LimitableResource : u32 { - PhysicalMemory = 0, - Threads = 1, - Events = 2, - TransferMemory = 3, - Sessions = 4, - - Count, -}; + +using LimitableResource = Svc::LimitableResource; constexpr bool IsValidResourceType(LimitableResource type) { return type < LimitableResource::Count; diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index b1cabbca0..d6676904b 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp @@ -384,7 +384,8 @@ void KScheduler::SwitchThread(KThread* next_thread) { void KScheduler::ScheduleImpl() { // First, clear the needs scheduling bool. - m_state.needs_scheduling.store(false, std::memory_order_seq_cst); + m_state.needs_scheduling.store(false, std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_seq_cst); // Load the appropriate thread pointers for scheduling. KThread* const cur_thread{GetCurrentThreadPointer(kernel)}; @@ -400,7 +401,8 @@ void KScheduler::ScheduleImpl() { // If there aren't, we want to check if the highest priority thread is the same as the current // thread. if (highest_priority_thread == cur_thread) { - // If they're the same, then we can just return. + // If they're the same, then we can just issue a memory barrier and return. + std::atomic_thread_fence(std::memory_order_seq_cst); return; } @@ -476,7 +478,8 @@ void KScheduler::ScheduleImplFiber() { // We failed to successfully do the context switch, and need to retry. // Clear needs_scheduling. - m_state.needs_scheduling.store(false, std::memory_order_seq_cst); + m_state.needs_scheduling.store(false, std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_seq_cst); // Refresh the highest priority thread. highest_priority_thread = m_state.highest_priority_thread; diff --git a/src/core/hle/kernel/k_scheduler_lock.h b/src/core/hle/kernel/k_scheduler_lock.h index 73314b45e..129d60472 100644 --- a/src/core/hle/kernel/k_scheduler_lock.h +++ b/src/core/hle/kernel/k_scheduler_lock.h @@ -60,6 +60,9 @@ public: // Release an instance of the lock. if ((--lock_count) == 0) { + // Perform a memory barrier here. + std::atomic_thread_fence(std::memory_order_seq_cst); + // We're no longer going to hold the lock. Take note of what cores need scheduling. const u64 cores_needing_scheduling = SchedulerType::UpdateHighestPriorityThreads(kernel); diff --git a/src/core/hle/kernel/k_session.cpp b/src/core/hle/kernel/k_session.cpp index 7a6534ac3..b6f6fe9d9 100644 --- a/src/core/hle/kernel/k_session.cpp +++ b/src/core/hle/kernel/k_session.cpp @@ -76,7 +76,7 @@ void KSession::OnClientClosed() { void KSession::PostDestroy(uintptr_t arg) { // Release the session count resource the owner process holds. KProcess* owner = reinterpret_cast<KProcess*>(arg); - owner->GetResourceLimit()->Release(LimitableResource::Sessions, 1); + owner->GetResourceLimit()->Release(LimitableResource::SessionCountMax, 1); owner->Close(); } diff --git a/src/core/hle/kernel/k_shared_memory.cpp b/src/core/hle/kernel/k_shared_memory.cpp index a039cc591..10cd4c43d 100644 --- a/src/core/hle/kernel/k_shared_memory.cpp +++ b/src/core/hle/kernel/k_shared_memory.cpp @@ -14,7 +14,7 @@ namespace Kernel { KSharedMemory::KSharedMemory(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_} {} KSharedMemory::~KSharedMemory() { - kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemory, size); + kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemoryMax, size); } Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_, @@ -35,7 +35,7 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o KResourceLimit* reslimit = kernel.GetSystemResourceLimit(); // Reserve memory for ourselves. - KScopedResourceReservation memory_reservation(reslimit, LimitableResource::PhysicalMemory, + KScopedResourceReservation memory_reservation(reslimit, LimitableResource::PhysicalMemoryMax, size_); R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); @@ -57,7 +57,7 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o void KSharedMemory::Finalize() { // Release the memory reservation. - resource_limit->Release(LimitableResource::PhysicalMemory, size); + resource_limit->Release(LimitableResource::PhysicalMemoryMax, size); resource_limit->Close(); // Perform inherited finalization. diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index cc88d08f0..21207fe99 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -263,9 +263,9 @@ Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_ R_SUCCEED(); } -Result KThread::InitializeDummyThread(KThread* thread) { +Result KThread::InitializeDummyThread(KThread* thread, KProcess* owner) { // Initialize the thread. - R_TRY(thread->Initialize({}, {}, {}, DummyThreadPriority, 3, {}, ThreadType::Dummy)); + R_TRY(thread->Initialize({}, {}, {}, DummyThreadPriority, 3, owner, ThreadType::Dummy)); // Initialize emulation parameters. thread->stack_parameters.disable_count = 0; @@ -303,7 +303,7 @@ void KThread::PostDestroy(uintptr_t arg) { const bool resource_limit_release_hint = (arg & 1); const s64 hint_value = (resource_limit_release_hint ? 0 : 1); if (owner != nullptr) { - owner->GetResourceLimit()->Release(LimitableResource::Threads, 1, hint_value); + owner->GetResourceLimit()->Release(LimitableResource::ThreadCountMax, 1, hint_value); owner->Close(); } } @@ -1054,7 +1054,7 @@ void KThread::Exit() { // Release the thread resource hint, running thread count from parent. if (parent != nullptr) { - parent->GetResourceLimit()->Release(Kernel::LimitableResource::Threads, 0, 1); + parent->GetResourceLimit()->Release(Kernel::LimitableResource::ThreadCountMax, 0, 1); resource_limit_release_hint = true; parent->DecrementRunningThreadCount(); } diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index 30aa10c9a..f38c92bff 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -415,7 +415,7 @@ public: static void PostDestroy(uintptr_t arg); - [[nodiscard]] static Result InitializeDummyThread(KThread* thread); + [[nodiscard]] static Result InitializeDummyThread(KThread* thread, KProcess* owner); [[nodiscard]] static Result InitializeMainThread(Core::System& system, KThread* thread, s32 virt_core); diff --git a/src/core/hle/kernel/k_transfer_memory.cpp b/src/core/hle/kernel/k_transfer_memory.cpp index b0320eb73..9f34c2d46 100644 --- a/src/core/hle/kernel/k_transfer_memory.cpp +++ b/src/core/hle/kernel/k_transfer_memory.cpp @@ -37,7 +37,7 @@ void KTransferMemory::Finalize() { void KTransferMemory::PostDestroy(uintptr_t arg) { KProcess* owner = reinterpret_cast<KProcess*>(arg); - owner->GetResourceLimit()->Release(LimitableResource::TransferMemory, 1); + owner->GetResourceLimit()->Release(LimitableResource::TransferMemoryCountMax, 1); owner->Close(); } diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 47b760a9c..b77723503 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -91,9 +91,9 @@ struct KernelCore::Impl { pt_heap_region.GetSize()); } - RegisterHostThread(); + RegisterHostThread(nullptr); - default_service_thread = CreateServiceThread(kernel, "DefaultServiceThread"); + default_service_thread = &CreateServiceThread(kernel, "DefaultServiceThread"); } void InitializeCores() { @@ -229,18 +229,22 @@ struct KernelCore::Impl { const auto kernel_size{sizes.second}; // If setting the default system values fails, then something seriously wrong has occurred. - ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, total_size) + ASSERT( + system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemoryMax, total_size) + .IsSuccess()); + ASSERT(system_resource_limit->SetLimitValue(LimitableResource::ThreadCountMax, 800) .IsSuccess()); - ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess()); - ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess()); - ASSERT(system_resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200) + ASSERT(system_resource_limit->SetLimitValue(LimitableResource::EventCountMax, 900) .IsSuccess()); - ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess()); - system_resource_limit->Reserve(LimitableResource::PhysicalMemory, kernel_size); + ASSERT(system_resource_limit->SetLimitValue(LimitableResource::TransferMemoryCountMax, 200) + .IsSuccess()); + ASSERT(system_resource_limit->SetLimitValue(LimitableResource::SessionCountMax, 1133) + .IsSuccess()); + system_resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, kernel_size); // Reserve secure applet memory, introduced in firmware 5.0.0 constexpr u64 secure_applet_memory_size{4_MiB}; - ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory, + ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, secure_applet_memory_size)); } @@ -373,15 +377,18 @@ struct KernelCore::Impl { } // Gets the dummy KThread for the caller, allocating a new one if this is the first time - KThread* GetHostDummyThread() { + KThread* GetHostDummyThread(KThread* existing_thread) { auto initialize = [this](KThread* thread) { - ASSERT(KThread::InitializeDummyThread(thread).IsSuccess()); + ASSERT(KThread::InitializeDummyThread(thread, nullptr).IsSuccess()); thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId())); return thread; }; - thread_local auto raw_thread = KThread(system.Kernel()); - thread_local auto thread = initialize(&raw_thread); + thread_local KThread raw_thread{system.Kernel()}; + thread_local KThread* thread = nullptr; + if (thread == nullptr) { + thread = (existing_thread == nullptr) ? initialize(&raw_thread) : existing_thread; + } return thread; } @@ -396,9 +403,9 @@ struct KernelCore::Impl { } /// Registers a new host thread by allocating a host thread ID for it - void RegisterHostThread() { + void RegisterHostThread(KThread* existing_thread) { [[maybe_unused]] const auto this_id = GetHostThreadId(); - [[maybe_unused]] const auto dummy_thread = GetHostDummyThread(); + [[maybe_unused]] const auto dummy_thread = GetHostDummyThread(existing_thread); } [[nodiscard]] u32 GetCurrentHostThreadID() { @@ -429,7 +436,7 @@ struct KernelCore::Impl { KThread* GetCurrentEmuThread() { const auto thread_id = GetCurrentHostThreadID(); if (thread_id >= Core::Hardware::NUM_CPU_CORES) { - return GetHostDummyThread(); + return GetHostDummyThread(nullptr); } return current_thread; @@ -779,33 +786,31 @@ struct KernelCore::Impl { search->second(system.ServiceManager(), server_port); } - std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel, - const std::string& name) { - auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, name); + Kernel::ServiceThread& CreateServiceThread(KernelCore& kernel, const std::string& name) { + auto* ptr = new ServiceThread(kernel, name); service_threads_manager.QueueWork( - [this, service_thread]() { service_threads.emplace(service_thread); }); + [this, ptr]() { service_threads.emplace(ptr, std::unique_ptr<ServiceThread>(ptr)); }); - return service_thread; + return *ptr; } - void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) { - if (auto strong_ptr = service_thread.lock()) { - if (strong_ptr == default_service_thread.lock()) { - // Nothing to do here, the service is using default_service_thread, which will be - // released on shutdown. - return; - } + void ReleaseServiceThread(Kernel::ServiceThread& service_thread) { + auto* ptr = &service_thread; - service_threads_manager.QueueWork( - [this, strong_ptr{std::move(strong_ptr)}]() { service_threads.erase(strong_ptr); }); + if (ptr == default_service_thread) { + // Nothing to do here, the service is using default_service_thread, which will be + // released on shutdown. + return; } + + service_threads_manager.QueueWork([this, ptr]() { service_threads.erase(ptr); }); } void ClearServiceThreads() { service_threads_manager.QueueWork([this] { service_threads.clear(); - default_service_thread.reset(); + default_service_thread = nullptr; service_thread_barrier.Sync(); }); service_thread_barrier.Sync(); @@ -881,8 +886,8 @@ struct KernelCore::Impl { std::unique_ptr<KMemoryLayout> memory_layout; // Threads used for services - std::unordered_set<std::shared_ptr<ServiceThread>> service_threads; - std::weak_ptr<ServiceThread> default_service_thread; + std::unordered_map<ServiceThread*, std::unique_ptr<ServiceThread>> service_threads; + ServiceThread* default_service_thread{}; Common::ThreadWorker service_threads_manager; Common::Barrier service_thread_barrier; @@ -1122,8 +1127,12 @@ void KernelCore::RegisterCoreThread(std::size_t core_id) { impl->RegisterCoreThread(core_id); } -void KernelCore::RegisterHostThread() { - impl->RegisterHostThread(); +void KernelCore::RegisterHostThread(KThread* existing_thread) { + impl->RegisterHostThread(existing_thread); + + if (existing_thread != nullptr) { + ASSERT(GetCurrentEmuThread() == existing_thread); + } } u32 KernelCore::GetCurrentHostThreadID() const { @@ -1198,16 +1207,28 @@ void KernelCore::Suspend(bool suspended) { const bool should_suspend{exception_exited || suspended}; const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable; - for (auto* process : GetProcessList()) { - process->SetActivity(activity); + std::vector<KScopedAutoObject<KThread>> process_threads; + { + KScopedSchedulerLock sl{*this}; + + if (auto* process = CurrentProcess(); process != nullptr) { + process->SetActivity(activity); + + if (!should_suspend) { + // Runnable now; no need to wait. + return; + } - if (should_suspend) { - // Wait for execution to stop for (auto* thread : process->GetThreadList()) { - thread->WaitUntilSuspended(); + process_threads.emplace_back(thread); } } } + + // Wait for execution to stop. + for (auto& thread : process_threads) { + thread->WaitUntilSuspended(); + } } void KernelCore::ShutdownCores() { @@ -1239,15 +1260,15 @@ void KernelCore::ExitSVCProfile() { MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[CurrentPhysicalCoreIndex()]); } -std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) { +Kernel::ServiceThread& KernelCore::CreateServiceThread(const std::string& name) { return impl->CreateServiceThread(*this, name); } -std::weak_ptr<Kernel::ServiceThread> KernelCore::GetDefaultServiceThread() const { - return impl->default_service_thread; +Kernel::ServiceThread& KernelCore::GetDefaultServiceThread() const { + return *impl->default_service_thread; } -void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) { +void KernelCore::ReleaseServiceThread(Kernel::ServiceThread& service_thread) { impl->ReleaseServiceThread(service_thread); } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index caca60586..2e22fe0f6 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -240,7 +240,7 @@ public: void RegisterCoreThread(std::size_t core_id); /// Register the current thread as a non CPU core thread. - void RegisterHostThread(); + void RegisterHostThread(KThread* existing_thread = nullptr); /// Gets the virtual memory manager for the kernel. KMemoryManager& MemoryManager(); @@ -309,24 +309,24 @@ public: * See GetDefaultServiceThread. * @param name String name for the ServerSession creating this thread, used for debug * purposes. - * @returns The a weak pointer newly created service thread. + * @returns A reference to the newly created service thread. */ - std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(const std::string& name); + Kernel::ServiceThread& CreateServiceThread(const std::string& name); /** * Gets the default host service thread, which executes HLE service requests. Unless service * requests need to block on the host, the default service thread should be used in favor of * creating a new service thread. - * @returns The a weak pointer for the default service thread. + * @returns A reference to the default service thread. */ - std::weak_ptr<Kernel::ServiceThread> GetDefaultServiceThread() const; + Kernel::ServiceThread& GetDefaultServiceThread() const; /** * Releases a HLE service thread, instructing KernelCore to free it. This should be called when * the ServerSession associated with the thread is destroyed. * @param service_thread Service thread to release. */ - void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread); + void ReleaseServiceThread(Kernel::ServiceThread& service_thread); /// Workaround for single-core mode when preempting threads while idle. bool IsPhantomModeForSingleCore() const; diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp index d4375962f..3044922ac 100644 --- a/src/core/hle/kernel/physical_core.cpp +++ b/src/core/hle/kernel/physical_core.cpp @@ -12,7 +12,7 @@ namespace Kernel { PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_) : core_index{core_index_}, system{system_}, scheduler{scheduler_} { -#ifdef ARCHITECTURE_x86_64 +#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64) // TODO(bunnei): Initialization relies on a core being available. We may later replace this with // a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager. auto& kernel = system.Kernel(); @@ -26,7 +26,7 @@ PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KSche PhysicalCore::~PhysicalCore() = default; void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) { -#ifdef ARCHITECTURE_x86_64 +#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64) auto& kernel = system.Kernel(); if (!is_64_bit) { // We already initialized a 64-bit core, replace with a 32-bit one. diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp index c8fe42537..f5c2ab23f 100644 --- a/src/core/hle/kernel/service_thread.cpp +++ b/src/core/hle/kernel/service_thread.cpp @@ -36,11 +36,12 @@ public: private: KernelCore& kernel; - std::jthread m_thread; + std::jthread m_host_thread; std::mutex m_session_mutex; std::map<KServerSession*, std::shared_ptr<SessionRequestManager>> m_sessions; KEvent* m_wakeup_event; KProcess* m_process; + KThread* m_thread; std::atomic<bool> m_shutdown_requested; const std::string m_service_name; }; @@ -132,7 +133,7 @@ void ServiceThread::Impl::SessionClosed(KServerSession* server_session, void ServiceThread::Impl::LoopProcess() { Common::SetCurrentThreadName(m_service_name.c_str()); - kernel.RegisterHostThread(); + kernel.RegisterHostThread(m_thread); while (!m_shutdown_requested.load()) { WaitAndProcessImpl(); @@ -160,7 +161,7 @@ ServiceThread::Impl::~Impl() { // Shut down the processing thread. m_shutdown_requested.store(true); m_wakeup_event->Signal(); - m_thread.join(); + m_host_thread.join(); // Lock mutex. m_session_mutex.lock(); @@ -177,6 +178,9 @@ ServiceThread::Impl::~Impl() { m_wakeup_event->GetReadableEvent().Close(); m_wakeup_event->Close(); + // Close thread. + m_thread->Close(); + // Close process. m_process->Close(); } @@ -189,7 +193,7 @@ ServiceThread::Impl::Impl(KernelCore& kernel_, const std::string& service_name) KProcess::ProcessType::KernelInternal, kernel.GetSystemResourceLimit()); // Reserve a new event from the process resource limit - KScopedResourceReservation event_reservation(m_process, LimitableResource::Events); + KScopedResourceReservation event_reservation(m_process, LimitableResource::EventCountMax); ASSERT(event_reservation.Succeeded()); // Initialize event. @@ -199,11 +203,19 @@ ServiceThread::Impl::Impl(KernelCore& kernel_, const std::string& service_name) // Commit the event reservation. event_reservation.Commit(); - // Register the event. - KEvent::Register(kernel, m_wakeup_event); + // Reserve a new thread from the process resource limit + KScopedResourceReservation thread_reservation(m_process, LimitableResource::ThreadCountMax); + ASSERT(thread_reservation.Succeeded()); + + // Initialize thread. + m_thread = KThread::Create(kernel); + ASSERT(KThread::InitializeDummyThread(m_thread, m_process).IsSuccess()); + + // Commit the thread reservation. + thread_reservation.Commit(); // Start thread. - m_thread = std::jthread([this] { LoopProcess(); }); + m_host_thread = std::jthread([this] { LoopProcess(); }); } ServiceThread::ServiceThread(KernelCore& kernel, const std::string& name) diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index ecac97a52..9962ad171 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -267,7 +267,7 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien // Reserve a new session from the process resource limit. // FIXME: LimitableResource_SessionCountMax - KScopedResourceReservation session_reservation(&process, LimitableResource::Sessions); + KScopedResourceReservation session_reservation(&process, LimitableResource::SessionCountMax); if (session_reservation.Succeeded()) { session = T::Create(system.Kernel()); } else { @@ -298,7 +298,7 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien // We successfully allocated a session, so add the object we allocated to the resource // limit. - // system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::Sessions, 1); + // system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::SessionCountMax, 1); } // Check that we successfully created a session. @@ -656,27 +656,12 @@ static Result ArbitrateUnlock32(Core::System& system, u32 address) { return ArbitrateUnlock(system, address); } -enum class BreakType : u32 { - Panic = 0, - AssertionFailed = 1, - PreNROLoad = 3, - PostNROLoad = 4, - PreNROUnload = 5, - PostNROUnload = 6, - CppException = 7, -}; - -struct BreakReason { - union { - u32 raw; - BitField<0, 30, BreakType> break_type; - BitField<31, 1, u32> signal_debugger; - }; -}; - /// Break program execution static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { - BreakReason break_reason{reason}; + BreakReason break_reason = + static_cast<BreakReason>(reason & ~static_cast<u32>(BreakReason::NotificationOnlyFlag)); + bool notification_only = (reason & static_cast<u32>(BreakReason::NotificationOnlyFlag)) != 0; + bool has_dumped_buffer{}; std::vector<u8> debug_buffer; @@ -705,57 +690,56 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { } has_dumped_buffer = true; }; - switch (break_reason.break_type) { - case BreakType::Panic: - LOG_CRITICAL(Debug_Emulated, "Signalling debugger, PANIC! info1=0x{:016X}, info2=0x{:016X}", - info1, info2); + switch (break_reason) { + case BreakReason::Panic: + LOG_CRITICAL(Debug_Emulated, "Userspace PANIC! info1=0x{:016X}, info2=0x{:016X}", info1, + info2); handle_debug_buffer(info1, info2); break; - case BreakType::AssertionFailed: - LOG_CRITICAL(Debug_Emulated, - "Signalling debugger, Assertion failed! info1=0x{:016X}, info2=0x{:016X}", + case BreakReason::Assert: + LOG_CRITICAL(Debug_Emulated, "Userspace Assertion failed! info1=0x{:016X}, info2=0x{:016X}", info1, info2); handle_debug_buffer(info1, info2); break; - case BreakType::PreNROLoad: - LOG_WARNING( - Debug_Emulated, - "Signalling debugger, Attempting to load an NRO at 0x{:016X} with size 0x{:016X}", - info1, info2); + case BreakReason::User: + LOG_WARNING(Debug_Emulated, "Userspace Break! 0x{:016X} with size 0x{:016X}", info1, info2); + handle_debug_buffer(info1, info2); break; - case BreakType::PostNROLoad: - LOG_WARNING(Debug_Emulated, - "Signalling debugger, Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1, - info2); + case BreakReason::PreLoadDll: + LOG_INFO(Debug_Emulated, + "Userspace Attempting to load an NRO at 0x{:016X} with size 0x{:016X}", info1, + info2); break; - case BreakType::PreNROUnload: - LOG_WARNING( - Debug_Emulated, - "Signalling debugger, Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}", - info1, info2); + case BreakReason::PostLoadDll: + LOG_INFO(Debug_Emulated, "Userspace Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1, + info2); break; - case BreakType::PostNROUnload: - LOG_WARNING(Debug_Emulated, - "Signalling debugger, Unloaded an NRO at 0x{:016X} with size 0x{:016X}", info1, - info2); + case BreakReason::PreUnloadDll: + LOG_INFO(Debug_Emulated, + "Userspace Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}", info1, + info2); break; - case BreakType::CppException: + case BreakReason::PostUnloadDll: + LOG_INFO(Debug_Emulated, "Userspace Unloaded an NRO at 0x{:016X} with size 0x{:016X}", + info1, info2); + break; + case BreakReason::CppException: LOG_CRITICAL(Debug_Emulated, "Signalling debugger. Uncaught C++ exception encountered."); break; default: LOG_WARNING( Debug_Emulated, - "Signalling debugger, Unknown break reason {}, info1=0x{:016X}, info2=0x{:016X}", - static_cast<u32>(break_reason.break_type.Value()), info1, info2); + "Signalling debugger, Unknown break reason {:#X}, info1=0x{:016X}, info2=0x{:016X}", + reason, info1, info2); handle_debug_buffer(info1, info2); break; } - system.GetReporter().SaveSvcBreakReport( - static_cast<u32>(break_reason.break_type.Value()), break_reason.signal_debugger.As<bool>(), - info1, info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt); + system.GetReporter().SaveSvcBreakReport(reason, notification_only, info1, info2, + has_dumped_buffer ? std::make_optional(debug_buffer) + : std::nullopt); - if (!break_reason.signal_debugger) { + if (!notification_only) { LOG_CRITICAL( Debug_Emulated, "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", @@ -1716,13 +1700,13 @@ static Result QueryProcessMemory(Core::System& system, VAddr memory_info_address auto& memory{system.Memory()}; const auto memory_info{process->PageTable().QueryInfo(address).GetSvcMemoryInfo()}; - memory.Write64(memory_info_address + 0x00, memory_info.addr); + memory.Write64(memory_info_address + 0x00, memory_info.base_address); memory.Write64(memory_info_address + 0x08, memory_info.size); memory.Write32(memory_info_address + 0x10, static_cast<u32>(memory_info.state) & 0xff); - memory.Write32(memory_info_address + 0x14, static_cast<u32>(memory_info.attr)); - memory.Write32(memory_info_address + 0x18, static_cast<u32>(memory_info.perm)); - memory.Write32(memory_info_address + 0x1c, memory_info.ipc_refcount); - memory.Write32(memory_info_address + 0x20, memory_info.device_refcount); + memory.Write32(memory_info_address + 0x14, static_cast<u32>(memory_info.attribute)); + memory.Write32(memory_info_address + 0x18, static_cast<u32>(memory_info.permission)); + memory.Write32(memory_info_address + 0x1c, memory_info.ipc_count); + memory.Write32(memory_info_address + 0x20, memory_info.device_count); memory.Write32(memory_info_address + 0x24, 0); // Page info appears to be currently unused by the kernel and is always set to zero. @@ -1943,7 +1927,7 @@ static Result CreateThread(Core::System& system, Handle* out_handle, VAddr entry // Reserve a new thread from the process resource limit (waiting up to 100ms). KScopedResourceReservation thread_reservation( - kernel.CurrentProcess(), LimitableResource::Threads, 1, + kernel.CurrentProcess(), LimitableResource::ThreadCountMax, 1, system.CoreTiming().GetGlobalTimeNs().count() + 100000000); if (!thread_reservation.Succeeded()) { LOG_ERROR(Kernel_SVC, "Could not reserve a new thread"); @@ -2344,7 +2328,7 @@ static Result CreateTransferMemory(Core::System& system, Handle* out, VAddr addr // Reserve a new transfer memory from the process resource limit. KScopedResourceReservation trmem_reservation(kernel.CurrentProcess(), - LimitableResource::TransferMemory); + LimitableResource::TransferMemoryCountMax); R_UNLESS(trmem_reservation.Succeeded(), ResultLimitReached); // Create the transfer memory. @@ -2496,7 +2480,7 @@ static Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_r // Reserve a new event from the process resource limit KScopedResourceReservation event_reservation(kernel.CurrentProcess(), - LimitableResource::Events); + LimitableResource::EventCountMax); R_UNLESS(event_reservation.Succeeded(), ResultLimitReached); // Create a new event. @@ -2539,11 +2523,6 @@ static Result CreateEvent32(Core::System& system, Handle* out_write, Handle* out static Result GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) { LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type); - // This function currently only allows retrieving a process' status. - enum class InfoType { - Status, - }; - const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle); if (process.IsNull()) { @@ -2552,9 +2531,9 @@ static Result GetProcessInfo(Core::System& system, u64* out, Handle process_hand return ResultInvalidHandle; } - const auto info_type = static_cast<InfoType>(type); - if (info_type != InfoType::Status) { - LOG_ERROR(Kernel_SVC, "Expected info_type to be Status but got {} instead", type); + const auto info_type = static_cast<ProcessInfoType>(type); + if (info_type != ProcessInfoType::ProcessState) { + LOG_ERROR(Kernel_SVC, "Expected info_type to be ProcessState but got {} instead", type); return ResultInvalidEnumValue; } diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h index 9b0305552..33eebcef6 100644 --- a/src/core/hle/kernel/svc_types.h +++ b/src/core/hle/kernel/svc_types.h @@ -8,6 +8,8 @@ namespace Kernel::Svc { +using Handle = u32; + enum class MemoryState : u32 { Free = 0x00, Io = 0x01, @@ -55,17 +57,6 @@ enum class MemoryPermission : u32 { }; DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission); -struct MemoryInfo { - u64 addr{}; - u64 size{}; - MemoryState state{}; - MemoryAttribute attr{}; - MemoryPermission perm{}; - u32 ipc_refcount{}; - u32 device_refcount{}; - u32 padding{}; -}; - enum class SignalType : u32 { Signal = 0, SignalAndIncrementIfEqual = 1, @@ -124,7 +115,57 @@ enum class ProcessExitReason : u32 { constexpr inline size_t ThreadLocalRegionSize = 0x200; -// Debug types. +struct PageInfo { + u32 flags; +}; + +// Info Types. +enum class InfoType : u32 { + CoreMask = 0, + PriorityMask = 1, + AliasRegionAddress = 2, + AliasRegionSize = 3, + HeapRegionAddress = 4, + HeapRegionSize = 5, + TotalMemorySize = 6, + UsedMemorySize = 7, + DebuggerAttached = 8, + ResourceLimit = 9, + IdleTickCount = 10, + RandomEntropy = 11, + AslrRegionAddress = 12, + AslrRegionSize = 13, + StackRegionAddress = 14, + StackRegionSize = 15, + SystemResourceSizeTotal = 16, + SystemResourceSizeUsed = 17, + ProgramId = 18, + InitialProcessIdRange = 19, + UserExceptionContextAddress = 20, + TotalNonSystemMemorySize = 21, + UsedNonSystemMemorySize = 22, + IsApplication = 23, + FreeThreadCount = 24, + ThreadTickCount = 25, + IsSvcPermitted = 26, + + MesosphereMeta = 65000, + MesosphereCurrentProcess = 65001, +}; + +enum class BreakReason : u32 { + Panic = 0, + Assert = 1, + User = 2, + PreLoadDll = 3, + PostLoadDll = 4, + PreUnloadDll = 5, + PostUnloadDll = 6, + CppException = 7, + + NotificationOnlyFlag = 0x80000000, +}; + enum class DebugEvent : u32 { CreateProcess = 0, CreateThread = 1, @@ -133,6 +174,14 @@ enum class DebugEvent : u32 { Exception = 4, }; +enum class DebugThreadParam : u32 { + Priority = 0, + State = 1, + IdealCore = 2, + CurrentCore = 3, + AffinityMask = 4, +}; + enum class DebugException : u32 { UndefinedInstruction = 0, InstructionAbort = 1, @@ -146,4 +195,401 @@ enum class DebugException : u32 { MemorySystemError = 9, }; +enum class DebugEventFlag : u32 { + Stopped = (1u << 0), +}; + +enum class BreakPointType : u32 { + HardwareInstruction = 0, + HardwareData = 1, +}; + +enum class HardwareBreakPointRegisterName : u32 { + I0 = 0, + I1 = 1, + I2 = 2, + I3 = 3, + I4 = 4, + I5 = 5, + I6 = 6, + I7 = 7, + I8 = 8, + I9 = 9, + I10 = 10, + I11 = 11, + I12 = 12, + I13 = 13, + I14 = 14, + I15 = 15, + D0 = 16, + D1 = 17, + D2 = 18, + D3 = 19, + D4 = 20, + D5 = 21, + D6 = 22, + D7 = 23, + D8 = 24, + D9 = 25, + D10 = 26, + D11 = 27, + D12 = 28, + D13 = 29, + D14 = 30, + D15 = 31, +}; + +namespace lp64 { +struct LastThreadContext { + u64 fp; + u64 sp; + u64 lr; + u64 pc; +}; + +struct PhysicalMemoryInfo { + PAddr physical_address; + u64 virtual_address; + u64 size; +}; + +struct DebugInfoCreateProcess { + u64 program_id; + u64 process_id; + std::array<char, 0xC> name; + u32 flags; + u64 user_exception_context_address; // 5.0.0+ +}; + +struct DebugInfoCreateThread { + u64 thread_id; + u64 tls_address; + // Removed in 11.0.0 u64 entrypoint; +}; + +struct DebugInfoExitProcess { + ProcessExitReason reason; +}; + +struct DebugInfoExitThread { + ThreadExitReason reason; +}; + +struct DebugInfoUndefinedInstructionException { + u32 insn; +}; + +struct DebugInfoDataAbortException { + u64 address; +}; + +struct DebugInfoAlignmentFaultException { + u64 address; +}; + +struct DebugInfoBreakPointException { + BreakPointType type; + u64 address; +}; + +struct DebugInfoUserBreakException { + BreakReason break_reason; + u64 address; + u64 size; +}; + +struct DebugInfoDebuggerBreakException { + std::array<u64, 4> active_thread_ids; +}; + +struct DebugInfoUndefinedSystemCallException { + u32 id; +}; + +union DebugInfoSpecificException { + DebugInfoUndefinedInstructionException undefined_instruction; + DebugInfoDataAbortException data_abort; + DebugInfoAlignmentFaultException alignment_fault; + DebugInfoBreakPointException break_point; + DebugInfoUserBreakException user_break; + DebugInfoDebuggerBreakException debugger_break; + DebugInfoUndefinedSystemCallException undefined_system_call; + u64 raw; +}; + +struct DebugInfoException { + DebugException type; + u64 address; + DebugInfoSpecificException specific; +}; + +union DebugInfo { + DebugInfoCreateProcess create_process; + DebugInfoCreateThread create_thread; + DebugInfoExitProcess exit_process; + DebugInfoExitThread exit_thread; + DebugInfoException exception; +}; + +struct DebugEventInfo { + DebugEvent type; + u32 flags; + u64 thread_id; + DebugInfo info; +}; +static_assert(sizeof(DebugEventInfo) >= 0x40); + +struct SecureMonitorArguments { + std::array<u64, 8> r; +}; +static_assert(sizeof(SecureMonitorArguments) == 0x40); +} // namespace lp64 + +namespace ilp32 { +struct LastThreadContext { + u32 fp; + u32 sp; + u32 lr; + u32 pc; +}; + +struct PhysicalMemoryInfo { + PAddr physical_address; + u32 virtual_address; + u32 size; +}; + +struct DebugInfoCreateProcess { + u64 program_id; + u64 process_id; + std::array<char, 0xC> name; + u32 flags; + u32 user_exception_context_address; // 5.0.0+ +}; + +struct DebugInfoCreateThread { + u64 thread_id; + u32 tls_address; + // Removed in 11.0.0 u32 entrypoint; +}; + +struct DebugInfoExitProcess { + ProcessExitReason reason; +}; + +struct DebugInfoExitThread { + ThreadExitReason reason; +}; + +struct DebugInfoUndefinedInstructionException { + u32 insn; +}; + +struct DebugInfoDataAbortException { + u32 address; +}; + +struct DebugInfoAlignmentFaultException { + u32 address; +}; + +struct DebugInfoBreakPointException { + BreakPointType type; + u32 address; +}; + +struct DebugInfoUserBreakException { + BreakReason break_reason; + u32 address; + u32 size; +}; + +struct DebugInfoDebuggerBreakException { + std::array<u64, 4> active_thread_ids; +}; + +struct DebugInfoUndefinedSystemCallException { + u32 id; +}; + +union DebugInfoSpecificException { + DebugInfoUndefinedInstructionException undefined_instruction; + DebugInfoDataAbortException data_abort; + DebugInfoAlignmentFaultException alignment_fault; + DebugInfoBreakPointException break_point; + DebugInfoUserBreakException user_break; + DebugInfoDebuggerBreakException debugger_break; + DebugInfoUndefinedSystemCallException undefined_system_call; + u64 raw; +}; + +struct DebugInfoException { + DebugException type; + u32 address; + DebugInfoSpecificException specific; +}; + +union DebugInfo { + DebugInfoCreateProcess create_process; + DebugInfoCreateThread create_thread; + DebugInfoExitProcess exit_process; + DebugInfoExitThread exit_thread; + DebugInfoException exception; +}; + +struct DebugEventInfo { + DebugEvent type; + u32 flags; + u64 thread_id; + DebugInfo info; +}; + +struct SecureMonitorArguments { + std::array<u32, 8> r; +}; +static_assert(sizeof(SecureMonitorArguments) == 0x20); +} // namespace ilp32 + +struct ThreadContext { + std::array<u64, 29> r; + u64 fp; + u64 lr; + u64 sp; + u64 pc; + u32 pstate; + u32 padding; + std::array<u128, 32> v; + u32 fpcr; + u32 fpsr; + u64 tpidr; +}; +static_assert(sizeof(ThreadContext) == 0x320); + +struct MemoryInfo { + u64 base_address; + u64 size; + MemoryState state; + MemoryAttribute attribute; + MemoryPermission permission; + u32 ipc_count; + u32 device_count; + u32 padding; +}; + +enum class LimitableResource : u32 { + PhysicalMemoryMax = 0, + ThreadCountMax = 1, + EventCountMax = 2, + TransferMemoryCountMax = 3, + SessionCountMax = 4, + Count, +}; + +enum class IoPoolType : u32 { + // Not supported. + Count = 0, +}; + +enum class MemoryMapping : u32 { + IoRegister = 0, + Uncached = 1, + Memory = 2, +}; + +enum class KernelDebugType : u32 { + Thread = 0, + ThreadCallStack = 1, + KernelObject = 2, + Handle_ = 3, + Memory = 4, + PageTable = 5, + CpuUtilization = 6, + Process = 7, + SuspendProcess = 8, + ResumeProcess = 9, + Port = 10, +}; + +enum class KernelTraceState : u32 { + Disabled = 0, + Enabled = 1, +}; + +enum class CodeMemoryOperation : u32 { + Map = 0, + MapToOwner = 1, + Unmap = 2, + UnmapFromOwner = 3, +}; + +enum class InterruptType : u32 { + Edge = 0, + Level = 1, +}; + +enum class DeviceName { + Afi = 0, + Avpc = 1, + Dc = 2, + Dcb = 3, + Hc = 4, + Hda = 5, + Isp2 = 6, + MsencNvenc = 7, + Nv = 8, + Nv2 = 9, + Ppcs = 10, + Sata = 11, + Vi = 12, + Vic = 13, + XusbHost = 14, + XusbDev = 15, + Tsec = 16, + Ppcs1 = 17, + Dc1 = 18, + Sdmmc1a = 19, + Sdmmc2a = 20, + Sdmmc3a = 21, + Sdmmc4a = 22, + Isp2b = 23, + Gpu = 24, + Gpub = 25, + Ppcs2 = 26, + Nvdec = 27, + Ape = 28, + Se = 29, + Nvjpg = 30, + Hc1 = 31, + Se1 = 32, + Axiap = 33, + Etr = 34, + Tsecb = 35, + Tsec1 = 36, + Tsecb1 = 37, + Nvdec1 = 38, + Count, +}; + +enum class SystemInfoType : u32 { + TotalPhysicalMemorySize = 0, + UsedPhysicalMemorySize = 1, + InitialProcessIdRange = 2, +}; + +enum class ProcessInfoType : u32 { + ProcessState = 0, +}; + +struct CreateProcessParameter { + std::array<char, 12> name; + u32 version; + u64 program_id; + u64 code_address; + s32 code_num_pages; + u32 flags; + Handle reslimit; + s32 system_resource_num_pages; +}; +static_assert(sizeof(CreateProcessParameter) == 0x30); + } // namespace Kernel::Svc diff --git a/src/core/hle/service/kernel_helpers.cpp b/src/core/hle/service/kernel_helpers.cpp index af133af93..42991928e 100644 --- a/src/core/hle/service/kernel_helpers.cpp +++ b/src/core/hle/service/kernel_helpers.cpp @@ -31,7 +31,7 @@ ServiceContext::~ServiceContext() { Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) { // Reserve a new event from the process resource limit Kernel::KScopedResourceReservation event_reservation(process, - Kernel::LimitableResource::Events); + Kernel::LimitableResource::EventCountMax); if (!event_reservation.Succeeded()) { LOG_CRITICAL(Service, "Resource limit reached!"); return {}; diff --git a/src/core/hle/service/sm/sm_controller.cpp b/src/core/hle/service/sm/sm_controller.cpp index 69e0fe808..1cf9dd1c4 100644 --- a/src/core/hle/service/sm/sm_controller.cpp +++ b/src/core/hle/service/sm/sm_controller.cpp @@ -34,8 +34,8 @@ void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) { // once this is a proper process // Reserve a new session from the process resource limit. - Kernel::KScopedResourceReservation session_reservation(&process, - Kernel::LimitableResource::Sessions); + Kernel::KScopedResourceReservation session_reservation( + &process, Kernel::LimitableResource::SessionCountMax); ASSERT(session_reservation.Succeeded()); // Create the session. diff --git a/src/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/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..d95915016 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,7 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan } LOG_INFO(Frontend, "Host CPU: {}", cpu_string); #endif + LOG_INFO(Frontend, "Host CPU Threads: {}", processor_count); LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString()); LOG_INFO(Frontend, "Host RAM: {:.2f} GiB", Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB}); @@ -2018,38 +2020,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 +2071,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 +2081,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 +2096,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 +2119,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 +2817,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); |