diff options
50 files changed, 998 insertions, 227 deletions
| diff --git a/externals/microprofile/microprofileui.h b/externals/microprofile/microprofileui.h index 1357a08fd..ca9fe7063 100644 --- a/externals/microprofile/microprofileui.h +++ b/externals/microprofile/microprofileui.h @@ -845,8 +845,6 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int      MicroProfile& S = *MicroProfileGet();      MP_DEBUG_DUMP_RANGE();      int nY = nBaseY - UI.nOffsetY; -    int64_t nNumBoxes = 0; -    int64_t nNumLines = 0;      uint32_t nFrameNext = (S.nFrameCurrent+1) % MICROPROFILE_MAX_FRAME_HISTORY;      MicroProfileFrameState* pFrameCurrent = &S.Frames[S.nFrameCurrent]; @@ -1149,7 +1147,6 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int                                  }                              }  #endif -                            ++nNumBoxes;                          }                          else                          { @@ -1165,7 +1162,6 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int                                  }                                  nLinesDrawn[nStackPos] = nLineX;                                  MicroProfileDrawLineVertical(nLineX, fYStart + 0.5f, fYEnd + 0.5f, nColor|UI.nOpacityForeground); -                                ++nNumLines;                              }                          }                          nStackPos--; diff --git a/src/audio_core/renderer/performance/performance_manager.cpp b/src/audio_core/renderer/performance/performance_manager.cpp index fd5873e1e..8aa0f5ed0 100644 --- a/src/audio_core/renderer/performance/performance_manager.cpp +++ b/src/audio_core/renderer/performance/performance_manager.cpp @@ -26,6 +26,7 @@ void PerformanceManager::CreateImpl(const size_t version) {          impl = std::make_unique<              PerformanceManagerImpl<PerformanceVersion::Version1, PerformanceFrameHeaderVersion1,                                     PerformanceEntryVersion1, PerformanceDetailVersion1>>(); +        break;      }  } diff --git a/src/common/atomic_helpers.h b/src/common/atomic_helpers.h index bef5015c1..aef3b66a4 100644 --- a/src/common/atomic_helpers.h +++ b/src/common/atomic_helpers.h @@ -156,6 +156,7 @@ AE_FORCEINLINE void compiler_fence(memory_order order) AE_NO_TSAN {          break;      default:          assert(false); +        break;      }  } diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp index 1a27532d4..e54383a4a 100644 --- a/src/common/x64/cpu_detect.cpp +++ b/src/common/x64/cpu_detect.cpp @@ -4,14 +4,27 @@  #include <array>  #include <cstring> +#include <fstream>  #include <iterator> +#include <optional>  #include <string_view> +#include <thread> +#include <vector>  #include "common/bit_util.h"  #include "common/common_types.h" +#include "common/logging/log.h"  #include "common/x64/cpu_detect.h" +#ifdef _WIN32 +#include <windows.h> +#endif +  #ifdef _MSC_VER  #include <intrin.h> + +static inline u64 xgetbv(u32 index) { +    return _xgetbv(index); +}  #else  #if defined(__DragonFly__) || defined(__FreeBSD__) @@ -39,12 +52,11 @@ static inline void __cpuid(int info[4], u32 function_id) {  }  #define _XCR_XFEATURE_ENABLED_MASK 0 -static inline u64 _xgetbv(u32 index) { +static inline u64 xgetbv(u32 index) {      u32 eax, edx;      __asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(index));      return ((u64)edx << 32) | eax;  } -  #endif // _MSC_VER  namespace Common { @@ -107,7 +119,7 @@ static CPUCaps Detect() {          //  - Is the XSAVE bit set in CPUID?          //  - XGETBV result has the XCR bit set.          if (Common::Bit<28>(cpu_id[2]) && Common::Bit<27>(cpu_id[2])) { -            if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) { +            if ((xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) {                  caps.avx = true;                  if (Common::Bit<12>(cpu_id[2]))                      caps.fma = true; @@ -192,4 +204,45 @@ const CPUCaps& GetCPUCaps() {      return caps;  } +std::optional<int> GetProcessorCount() { +#if defined(_WIN32) +    // Get the buffer length. +    DWORD length = 0; +    GetLogicalProcessorInformation(nullptr, &length); +    if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { +        LOG_ERROR(Frontend, "Failed to query core count."); +        return std::nullopt; +    } +    std::vector<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> buffer( +        length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION)); +    // Now query the core count. +    if (!GetLogicalProcessorInformation(buffer.data(), &length)) { +        LOG_ERROR(Frontend, "Failed to query core count."); +        return std::nullopt; +    } +    return static_cast<int>( +        std::count_if(buffer.cbegin(), buffer.cend(), [](const auto& proc_info) { +            return proc_info.Relationship == RelationProcessorCore; +        })); +#elif defined(__unix__) +    const int thread_count = std::thread::hardware_concurrency(); +    std::ifstream smt("/sys/devices/system/cpu/smt/active"); +    char state = '0'; +    if (smt) { +        smt.read(&state, sizeof(state)); +    } +    switch (state) { +    case '0': +        return thread_count; +    case '1': +        return thread_count / 2; +    default: +        return std::nullopt; +    } +#else +    // Shame on you +    return std::nullopt; +#endif +} +  } // namespace Common diff --git a/src/common/x64/cpu_detect.h b/src/common/x64/cpu_detect.h index 6830f3795..ca8db19d6 100644 --- a/src/common/x64/cpu_detect.h +++ b/src/common/x64/cpu_detect.h @@ -4,6 +4,7 @@  #pragma once +#include <optional>  #include <string_view>  #include "common/common_types.h" @@ -74,4 +75,7 @@ struct CPUCaps {   */  const CPUCaps& GetCPUCaps(); +/// Detects CPU core count +std::optional<int> GetProcessorCount(); +  } // namespace Common diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp index 339f971e6..1a8e02e6a 100644 --- a/src/core/debugger/debugger.cpp +++ b/src/core/debugger/debugger.cpp @@ -27,12 +27,21 @@ static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) {                  const u8* buffer_start = reinterpret_cast<const u8*>(&buffer);                  std::span<const u8> received_data{buffer_start, buffer_start + bytes_read};                  c(received_data); +                AsyncReceiveInto(r, buffer, c);              } - -            AsyncReceiveInto(r, buffer, c);          });  } +template <typename Callback> +static void AsyncAccept(boost::asio::ip::tcp::acceptor& acceptor, Callback&& c) { +    acceptor.async_accept([&, c](const boost::system::error_code& error, auto&& peer_socket) { +        if (!error.failed()) { +            c(peer_socket); +            AsyncAccept(acceptor, c); +        } +    }); +} +  template <typename Readable, typename Buffer>  static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) {      static_assert(std::is_trivial_v<Buffer>); @@ -59,9 +68,7 @@ namespace Core {  class DebuggerImpl : public DebuggerBackend {  public: -    explicit DebuggerImpl(Core::System& system_, u16 port) -        : system{system_}, signal_pipe{io_context}, client_socket{io_context} { -        frontend = std::make_unique<GDBStub>(*this, system); +    explicit DebuggerImpl(Core::System& system_, u16 port) : system{system_} {          InitializeServer(port);      } @@ -70,39 +77,42 @@ public:      }      bool SignalDebugger(SignalInfo signal_info) { -        { -            std::scoped_lock lk{connection_lock}; +        std::scoped_lock lk{connection_lock}; -            if (stopped) { -                // Do not notify the debugger about another event. -                // It should be ignored. -                return false; -            } - -            // Set up the state. -            stopped = true; -            info = signal_info; +        if (stopped || !state) { +            // Do not notify the debugger about another event. +            // It should be ignored. +            return false;          } +        // Set up the state. +        stopped = true; +        state->info = signal_info; +          // Write a single byte into the pipe to wake up the debug interface. -        boost::asio::write(signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped))); +        boost::asio::write(state->signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped))); +          return true;      } +    // These functions are callbacks from the frontend, and the lock will be held. +    // There is no need to relock it. +      std::span<const u8> ReadFromClient() override { -        return ReceiveInto(client_socket, client_data); +        return ReceiveInto(state->client_socket, state->client_data);      }      void WriteToClient(std::span<const u8> data) override { -        boost::asio::write(client_socket, boost::asio::buffer(data.data(), data.size_bytes())); +        boost::asio::write(state->client_socket, +                           boost::asio::buffer(data.data(), data.size_bytes()));      }      void SetActiveThread(Kernel::KThread* thread) override { -        active_thread = thread; +        state->active_thread = thread;      }      Kernel::KThread* GetActiveThread() override { -        return active_thread; +        return state->active_thread;      }  private: @@ -113,65 +123,78 @@ private:          // Run the connection thread.          connection_thread = std::jthread([&, port](std::stop_token stop_token) { +            Common::SetCurrentThreadName("Debugger"); +              try {                  // Initialize the listening socket and accept a new client.                  tcp::endpoint endpoint{boost::asio::ip::address_v4::any(), port};                  tcp::acceptor acceptor{io_context, endpoint}; -                acceptor.async_accept(client_socket, [](const auto&) {}); -                io_context.run_one(); -                io_context.restart(); +                AsyncAccept(acceptor, [&](auto&& peer) { AcceptConnection(std::move(peer)); }); -                if (stop_token.stop_requested()) { -                    return; +                while (!stop_token.stop_requested() && io_context.run()) {                  } - -                ThreadLoop(stop_token);              } catch (const std::exception& ex) {                  LOG_CRITICAL(Debug_GDBStub, "Stopping server: {}", ex.what());              }          });      } -    void ShutdownServer() { -        connection_thread.request_stop(); -        io_context.stop(); -        connection_thread.join(); -    } +    void AcceptConnection(boost::asio::ip::tcp::socket&& peer) { +        LOG_INFO(Debug_GDBStub, "Accepting new peer connection"); -    void ThreadLoop(std::stop_token stop_token) { -        Common::SetCurrentThreadName("Debugger"); +        std::scoped_lock lk{connection_lock}; + +        // Ensure everything is stopped. +        PauseEmulation(); + +        // Set up the new frontend. +        frontend = std::make_unique<GDBStub>(*this, system); + +        // Set the new state. This will tear down any existing state. +        state = ConnectionState{ +            .client_socket{std::move(peer)}, +            .signal_pipe{io_context}, +            .info{}, +            .active_thread{}, +            .client_data{}, +            .pipe_data{}, +        };          // Set up the client signals for new data. -        AsyncReceiveInto(signal_pipe, pipe_data, [&](auto d) { PipeData(d); }); -        AsyncReceiveInto(client_socket, client_data, [&](auto d) { ClientData(d); }); +        AsyncReceiveInto(state->signal_pipe, state->pipe_data, [&](auto d) { PipeData(d); }); +        AsyncReceiveInto(state->client_socket, state->client_data, [&](auto d) { ClientData(d); });          // Set the active thread.          UpdateActiveThread();          // Set up the frontend.          frontend->Connected(); +    } -        // Main event loop. -        while (!stop_token.stop_requested() && io_context.run()) { -        } +    void ShutdownServer() { +        connection_thread.request_stop(); +        io_context.stop(); +        connection_thread.join();      }      void PipeData(std::span<const u8> data) { -        switch (info.type) { +        std::scoped_lock lk{connection_lock}; + +        switch (state->info.type) {          case SignalType::Stopped:          case SignalType::Watchpoint:              // Stop emulation.              PauseEmulation();              // Notify the client. -            active_thread = info.thread; +            state->active_thread = state->info.thread;              UpdateActiveThread(); -            if (info.type == SignalType::Watchpoint) { -                frontend->Watchpoint(active_thread, *info.watchpoint); +            if (state->info.type == SignalType::Watchpoint) { +                frontend->Watchpoint(state->active_thread, *state->info.watchpoint);              } else { -                frontend->Stopped(active_thread); +                frontend->Stopped(state->active_thread);              }              break; @@ -179,8 +202,8 @@ private:              frontend->ShuttingDown();              // Wait for emulation to shut down gracefully now. -            signal_pipe.close(); -            client_socket.shutdown(boost::asio::socket_base::shutdown_both); +            state->signal_pipe.close(); +            state->client_socket.shutdown(boost::asio::socket_base::shutdown_both);              LOG_INFO(Debug_GDBStub, "Shut down server");              break; @@ -188,17 +211,16 @@ private:      }      void ClientData(std::span<const u8> data) { +        std::scoped_lock lk{connection_lock}; +          const auto actions{frontend->ClientData(data)};          for (const auto action : actions) {              switch (action) {              case DebuggerAction::Interrupt: { -                { -                    std::scoped_lock lk{connection_lock}; -                    stopped = true; -                } +                stopped = true;                  PauseEmulation();                  UpdateActiveThread(); -                frontend->Stopped(active_thread); +                frontend->Stopped(state->active_thread);                  break;              }              case DebuggerAction::Continue: @@ -206,15 +228,15 @@ private:                  break;              case DebuggerAction::StepThreadUnlocked:                  MarkResumed([&] { -                    active_thread->SetStepState(Kernel::StepState::StepPending); -                    active_thread->Resume(Kernel::SuspendType::Debug); -                    ResumeEmulation(active_thread); +                    state->active_thread->SetStepState(Kernel::StepState::StepPending); +                    state->active_thread->Resume(Kernel::SuspendType::Debug); +                    ResumeEmulation(state->active_thread);                  });                  break;              case DebuggerAction::StepThreadLocked: {                  MarkResumed([&] { -                    active_thread->SetStepState(Kernel::StepState::StepPending); -                    active_thread->Resume(Kernel::SuspendType::Debug); +                    state->active_thread->SetStepState(Kernel::StepState::StepPending); +                    state->active_thread->Resume(Kernel::SuspendType::Debug);                  });                  break;              } @@ -254,15 +276,14 @@ private:      template <typename Callback>      void MarkResumed(Callback&& cb) {          Kernel::KScopedSchedulerLock sl{system.Kernel()}; -        std::scoped_lock cl{connection_lock};          stopped = false;          cb();      }      void UpdateActiveThread() {          const auto& threads{ThreadList()}; -        if (std::find(threads.begin(), threads.end(), active_thread) == threads.end()) { -            active_thread = threads[0]; +        if (std::find(threads.begin(), threads.end(), state->active_thread) == threads.end()) { +            state->active_thread = threads[0];          }      } @@ -274,18 +295,22 @@ private:      System& system;      std::unique_ptr<DebuggerFrontend> frontend; +    boost::asio::io_context io_context;      std::jthread connection_thread;      std::mutex connection_lock; -    boost::asio::io_context io_context; -    boost::process::async_pipe signal_pipe; -    boost::asio::ip::tcp::socket client_socket; -    SignalInfo info; -    Kernel::KThread* active_thread; -    bool pipe_data; -    bool stopped; +    struct ConnectionState { +        boost::asio::ip::tcp::socket client_socket; +        boost::process::async_pipe signal_pipe; + +        SignalInfo info; +        Kernel::KThread* active_thread; +        std::array<u8, 4096> client_data; +        bool pipe_data; +    }; -    std::array<u8, 4096> client_data; +    std::optional<ConnectionState> state{}; +    bool stopped{};  };  Debugger::Debugger(Core::System& system, u16 port) { diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp index 884229c77..a64a9ac64 100644 --- a/src/core/debugger/gdbstub.cpp +++ b/src/core/debugger/gdbstub.cpp @@ -606,6 +606,8 @@ void GDBStub::HandleQuery(std::string_view command) {      } else if (command.starts_with("StartNoAckMode")) {          no_ack = true;          SendReply(GDB_STUB_REPLY_OK); +    } else if (command.starts_with("Rcmd,")) { +        HandleRcmd(Common::HexStringToVector(command.substr(5), false));      } else {          SendReply(GDB_STUB_REPLY_EMPTY);      } @@ -645,6 +647,155 @@ void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>&      }  } +constexpr std::array<std::pair<const char*, Kernel::Svc::MemoryState>, 22> MemoryStateNames{{ +    {"----- Free -----", Kernel::Svc::MemoryState::Free}, +    {"Io              ", Kernel::Svc::MemoryState::Io}, +    {"Static          ", Kernel::Svc::MemoryState::Static}, +    {"Code            ", Kernel::Svc::MemoryState::Code}, +    {"CodeData        ", Kernel::Svc::MemoryState::CodeData}, +    {"Normal          ", Kernel::Svc::MemoryState::Normal}, +    {"Shared          ", Kernel::Svc::MemoryState::Shared}, +    {"AliasCode       ", Kernel::Svc::MemoryState::AliasCode}, +    {"AliasCodeData   ", Kernel::Svc::MemoryState::AliasCodeData}, +    {"Ipc             ", Kernel::Svc::MemoryState::Ipc}, +    {"Stack           ", Kernel::Svc::MemoryState::Stack}, +    {"ThreadLocal     ", Kernel::Svc::MemoryState::ThreadLocal}, +    {"Transfered      ", Kernel::Svc::MemoryState::Transfered}, +    {"SharedTransfered", Kernel::Svc::MemoryState::SharedTransfered}, +    {"SharedCode      ", Kernel::Svc::MemoryState::SharedCode}, +    {"Inaccessible    ", Kernel::Svc::MemoryState::Inaccessible}, +    {"NonSecureIpc    ", Kernel::Svc::MemoryState::NonSecureIpc}, +    {"NonDeviceIpc    ", Kernel::Svc::MemoryState::NonDeviceIpc}, +    {"Kernel          ", Kernel::Svc::MemoryState::Kernel}, +    {"GeneratedCode   ", Kernel::Svc::MemoryState::GeneratedCode}, +    {"CodeOut         ", Kernel::Svc::MemoryState::CodeOut}, +    {"Coverage        ", Kernel::Svc::MemoryState::Coverage}, +}}; + +static constexpr const char* GetMemoryStateName(Kernel::Svc::MemoryState state) { +    for (size_t i = 0; i < MemoryStateNames.size(); i++) { +        if (std::get<1>(MemoryStateNames[i]) == state) { +            return std::get<0>(MemoryStateNames[i]); +        } +    } +    return "Unknown         "; +} + +static constexpr const char* GetMemoryPermissionString(const Kernel::Svc::MemoryInfo& info) { +    if (info.state == Kernel::Svc::MemoryState::Free) { +        return "   "; +    } + +    switch (info.permission) { +    case Kernel::Svc::MemoryPermission::ReadExecute: +        return "r-x"; +    case Kernel::Svc::MemoryPermission::Read: +        return "r--"; +    case Kernel::Svc::MemoryPermission::ReadWrite: +        return "rw-"; +    default: +        return "---"; +    } +} + +static VAddr GetModuleEnd(Kernel::KPageTable& page_table, VAddr base) { +    Kernel::Svc::MemoryInfo mem_info; +    VAddr cur_addr{base}; + +    // Expect: r-x Code (.text) +    mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); +    cur_addr = mem_info.base_address + mem_info.size; +    if (mem_info.state != Kernel::Svc::MemoryState::Code || +        mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) { +        return cur_addr - 1; +    } + +    // Expect: r-- Code (.rodata) +    mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); +    cur_addr = mem_info.base_address + mem_info.size; +    if (mem_info.state != Kernel::Svc::MemoryState::Code || +        mem_info.permission != Kernel::Svc::MemoryPermission::Read) { +        return cur_addr - 1; +    } + +    // Expect: rw- CodeData (.data) +    mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); +    cur_addr = mem_info.base_address + mem_info.size; +    return cur_addr - 1; +} + +void GDBStub::HandleRcmd(const std::vector<u8>& command) { +    std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()}; +    std::string reply; + +    auto* process = system.CurrentProcess(); +    auto& page_table = process->PageTable(); + +    if (command_str == "get info") { +        Loader::AppLoader::Modules modules; +        system.GetAppLoader().ReadNSOModules(modules); + +        reply = fmt::format("Process:     {:#x} ({})\n" +                            "Program Id:  {:#018x}\n", +                            process->GetProcessID(), process->GetName(), process->GetProgramID()); +        reply += +            fmt::format("Layout:\n" +                        "  Alias: {:#012x} - {:#012x}\n" +                        "  Heap:  {:#012x} - {:#012x}\n" +                        "  Aslr:  {:#012x} - {:#012x}\n" +                        "  Stack: {:#012x} - {:#012x}\n" +                        "Modules:\n", +                        page_table.GetAliasRegionStart(), page_table.GetAliasRegionEnd(), +                        page_table.GetHeapRegionStart(), page_table.GetHeapRegionEnd(), +                        page_table.GetAliasCodeRegionStart(), page_table.GetAliasCodeRegionEnd(), +                        page_table.GetStackRegionStart(), page_table.GetStackRegionEnd()); + +        for (const auto& [vaddr, name] : modules) { +            reply += fmt::format("  {:#012x} - {:#012x} {}\n", vaddr, +                                 GetModuleEnd(page_table, vaddr), name); +        } +    } else if (command_str == "get mappings") { +        reply = "Mappings:\n"; +        VAddr cur_addr = 0; + +        while (true) { +            using MemoryAttribute = Kernel::Svc::MemoryAttribute; + +            auto mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); + +            if (mem_info.state != Kernel::Svc::MemoryState::Inaccessible || +                mem_info.base_address + mem_info.size - 1 != std::numeric_limits<u64>::max()) { +                const char* state = GetMemoryStateName(mem_info.state); +                const char* perm = GetMemoryPermissionString(mem_info); + +                const char l = True(mem_info.attribute & MemoryAttribute::Locked) ? 'L' : '-'; +                const char i = True(mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-'; +                const char d = True(mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-'; +                const char u = True(mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-'; + +                reply += +                    fmt::format("  {:#012x} - {:#012x} {} {} {}{}{}{} [{}, {}]\n", +                                mem_info.base_address, mem_info.base_address + mem_info.size - 1, +                                perm, state, l, i, d, u, mem_info.ipc_count, mem_info.device_count); +            } + +            const uintptr_t next_address = mem_info.base_address + mem_info.size; +            if (next_address <= cur_addr) { +                break; +            } + +            cur_addr = next_address; +        } +    } else if (command_str == "help") { +        reply = "Commands:\n  get info\n  get mappings\n"; +    } else { +        reply = "Unknown command.\nCommands:\n  get info\n  get mappings\n"; +    } + +    std::span<const u8> reply_span{reinterpret_cast<u8*>(&reply.front()), reply.size()}; +    SendReply(Common::HexToString(reply_span, false)); +} +  Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {      const auto& threads{system.GlobalSchedulerContext().GetThreadList()};      for (auto* thread : threads) { diff --git a/src/core/debugger/gdbstub.h b/src/core/debugger/gdbstub.h index 0b0f56e4b..368197920 100644 --- a/src/core/debugger/gdbstub.h +++ b/src/core/debugger/gdbstub.h @@ -32,6 +32,7 @@ private:      void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions);      void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions);      void HandleQuery(std::string_view command); +    void HandleRcmd(const std::vector<u8>& command);      void HandleBreakpointInsert(std::string_view command);      void HandleBreakpointRemove(std::string_view command);      std::vector<char>::const_iterator CommandEnd() const; diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp index bda098511..7b363eb1e 100644 --- a/src/core/hle/kernel/init/init_slab_setup.cpp +++ b/src/core/hle/kernel/init/init_slab_setup.cpp @@ -243,6 +243,7 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {              // If we somehow get an invalid type, abort.          default:              ASSERT_MSG(false, "Unknown slab type: {}", slab_types[i]); +            break;          }          // If we've hit the end of a gap, free it. diff --git a/src/core/hle/kernel/k_event.cpp b/src/core/hle/kernel/k_event.cpp index 27f70e5c5..d973853ab 100644 --- a/src/core/hle/kernel/k_event.cpp +++ b/src/core/hle/kernel/k_event.cpp @@ -20,8 +20,12 @@ void KEvent::Initialize(KProcess* owner) {      m_readable_event.Initialize(this);      // Set our owner process. -    m_owner = owner; -    m_owner->Open(); +    // HACK: this should never be nullptr, but service threads don't have a +    // proper parent process yet. +    if (owner != nullptr) { +        m_owner = owner; +        m_owner->Open(); +    }      // Mark initialized.      m_initialized = true; @@ -50,8 +54,11 @@ Result KEvent::Clear() {  void KEvent::PostDestroy(uintptr_t arg) {      // Release the event count resource the owner process holds.      KProcess* owner = reinterpret_cast<KProcess*>(arg); -    owner->GetResourceLimit()->Release(LimitableResource::EventCountMax, 1); -    owner->Close(); + +    if (owner != nullptr) { +        owner->GetResourceLimit()->Release(LimitableResource::EventCountMax, 1); +        owner->Close(); +    }  }  } // namespace Kernel diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index 5387bf5fe..612fc76fa 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp @@ -2301,6 +2301,7 @@ Result KPageTable::SetProcessMemoryPermission(VAddr addr, size_t size,              break;          default:              ASSERT(false); +            break;          }      } @@ -2803,6 +2804,7 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, const KPageGroup& page_              break;          default:              ASSERT(false); +            break;          }          addr += size; @@ -2838,6 +2840,7 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, KMemoryPermission perm,          break;      default:          ASSERT(false); +        break;      }      R_SUCCEED();  } diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index 950850291..f1ca785d7 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h @@ -320,6 +320,9 @@ public:      constexpr VAddr GetAliasCodeRegionStart() const {          return m_alias_code_region_start;      } +    constexpr VAddr GetAliasCodeRegionEnd() const { +        return m_alias_code_region_end; +    }      constexpr VAddr GetAliasCodeRegionSize() const {          return m_alias_code_region_end - m_alias_code_region_start;      } diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 55a9c5fae..d1dc62401 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -395,6 +395,7 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:      default:          ASSERT(false); +        break;      }      // Create TLS region diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp index f5c2ab23f..e6e41ac34 100644 --- a/src/core/hle/kernel/service_thread.cpp +++ b/src/core/hle/kernel/service_thread.cpp @@ -40,7 +40,6 @@ private:      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; @@ -180,39 +179,17 @@ ServiceThread::Impl::~Impl() {      // Close thread.      m_thread->Close(); - -    // Close process. -    m_process->Close();  }  ServiceThread::Impl::Impl(KernelCore& kernel_, const std::string& service_name)      : kernel{kernel_}, m_service_name{service_name} { -    // Initialize process. -    m_process = KProcess::Create(kernel); -    KProcess::Initialize(m_process, kernel.System(), service_name, -                         KProcess::ProcessType::KernelInternal, kernel.GetSystemResourceLimit()); - -    // Reserve a new event from the process resource limit -    KScopedResourceReservation event_reservation(m_process, LimitableResource::EventCountMax); -    ASSERT(event_reservation.Succeeded()); -      // Initialize event.      m_wakeup_event = KEvent::Create(kernel); -    m_wakeup_event->Initialize(m_process); - -    // Commit the event reservation. -    event_reservation.Commit(); - -    // Reserve a new thread from the process resource limit -    KScopedResourceReservation thread_reservation(m_process, LimitableResource::ThreadCountMax); -    ASSERT(thread_reservation.Succeeded()); +    m_wakeup_event->Initialize(nullptr);      // Initialize thread.      m_thread = KThread::Create(kernel); -    ASSERT(KThread::InitializeDummyThread(m_thread, m_process).IsSuccess()); - -    // Commit the thread reservation. -    thread_reservation.Commit(); +    ASSERT(KThread::InitializeDummyThread(m_thread, nullptr).IsSuccess());      // Start thread.      m_host_thread = std::jthread([this] { LoopProcess(); }); diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 56c990728..240f06689 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -28,30 +28,49 @@ enum class ErrorModule : u32 {      Loader = 9,      CMIF = 10,      HIPC = 11, +    TMA = 12, +    DMNT = 13, +    GDS = 14,      PM = 15,      NS = 16, +    BSDSockets = 17,      HTC = 18, +    TSC = 19,      NCMContent = 20,      SM = 21,      RO = 22, +    GC = 23,      SDMMC = 24,      OVLN = 25,      SPL = 26, +    Socket = 27, +    HTCLOW = 29, +    DDSF = 30, +    HTCFS = 31, +    Async = 32, +    Util = 33, +    TIPC = 35, +    ANIF = 37,      ETHC = 100,      I2C = 101,      GPIO = 102,      UART = 103, +    CPAD = 104,      Settings = 105, +    FTM = 106,      WLAN = 107,      XCD = 108, +    TMP451 = 109,      NIFM = 110,      Hwopus = 111, +    LSM6DS3 = 112,      Bluetooth = 113,      VI = 114,      NFP = 115,      Time = 116,      FGM = 117,      OE = 118, +    BH1730FVC = 119,      PCIe = 120,      Friends = 121,      BCAT = 122, @@ -65,7 +84,7 @@ enum class ErrorModule : u32 {      AHID = 130,      Qlaunch = 132,      PCV = 133, -    OMM = 134, +    USBPD = 134,      BPC = 135,      PSM = 136,      NIM = 137, @@ -75,18 +94,22 @@ enum class ErrorModule : u32 {      NSD = 141,      PCTL = 142,      BTM = 143, +    LA = 144,      ETicket = 145,      NGC = 146,      ERPT = 147,      APM = 148, +    CEC = 149,      Profiler = 150,      ErrorUpload = 151, +    LIDBE = 152,      Audio = 153,      NPNS = 154,      NPNSHTTPSTREAM = 155,      ARP = 157,      SWKBD = 158,      BOOT = 159, +    NetDiag = 160,      NFCMifare = 161,      UserlandAssert = 162,      Fatal = 163, @@ -94,17 +117,68 @@ enum class ErrorModule : u32 {      SPSM = 165,      BGTC = 167,      UserlandCrash = 168, +    SASBUS = 169, +    PI = 170, +    AudioCtrl = 172, +    LBL = 173, +    JIT = 175, +    HDCP = 176, +    OMM = 177, +    PDM = 178, +    OLSC = 179,      SREPO = 180,      Dauth = 181, +    STDFU = 182, +    DBG = 183, +    DHCPS = 186, +    SPI = 187, +    AVM = 188, +    PWM = 189, +    RTC = 191, +    Regulator = 192, +    LED = 193, +    SIO = 195, +    PCM = 196, +    CLKRST = 197, +    POWCTL = 198, +    AudioOld = 201,      HID = 202,      LDN = 203, +    CS = 204,      Irsensor = 205,      Capture = 206,      Manu = 208,      ATK = 209, +    WEB = 210, +    LCS = 211,      GRC = 212, +    Repair = 213, +    Album = 214, +    RID = 215,      Migration = 216,      MigrationLdcServ = 217, +    HIDBUS = 218, +    ENS = 219, +    WebSocket = 223, +    DCDMTP = 227, +    PGL = 228, +    Notification = 229, +    INS = 230, +    LP2P = 231, +    RCD = 232, +    LCM40607 = 233, +    PRC = 235, +    TMAHTC = 237, +    ECTX = 238, +    MNPP = 239, +    HSHL = 240, +    CAPMTP = 242, +    DP2HDMI = 244, +    Cradle = 245, +    SProfile = 246, +    NDRM = 250, +    TSPM = 499, +    DevMenu = 500,      GeneralWebApplet = 800,      WifiWebAuthApplet = 809,      WhitelistedApplet = 810, diff --git a/src/core/hle/service/am/applets/applet_error.cpp b/src/core/hle/service/am/applets/applet_error.cpp index fcf34bf7e..bae0d99a6 100644 --- a/src/core/hle/service/am/applets/applet_error.cpp +++ b/src/core/hle/service/am/applets/applet_error.cpp @@ -144,6 +144,7 @@ void Error::Initialize() {          break;      default:          UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", mode); +        break;      }  } diff --git a/src/core/hle/service/am/applets/applet_general_backend.cpp b/src/core/hle/service/am/applets/applet_general_backend.cpp index c34ef08b3..e50acdaf6 100644 --- a/src/core/hle/service/am/applets/applet_general_backend.cpp +++ b/src/core/hle/service/am/applets/applet_general_backend.cpp @@ -129,6 +129,7 @@ void Auth::Execute() {      }      default:          unimplemented_log(); +        break;      }  } @@ -192,6 +193,7 @@ void PhotoViewer::Execute() {          break;      default:          UNIMPLEMENTED_MSG("Unimplemented PhotoViewer applet mode={:02X}!", mode); +        break;      }  } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index ced57dfe6..b97813fbc 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -300,11 +300,10 @@ Kernel::KEvent* nvhost_ctrl_gpu::QueryEvent(u32 event_id) {          return error_notifier_event;      case 2:          return unknown_event; -    default: { +    default:          LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id); +        return nullptr;      } -    } -    return nullptr;  }  } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 45a759fa8..e123564c6 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -364,11 +364,10 @@ Kernel::KEvent* nvhost_gpu::QueryEvent(u32 event_id) {          return sm_exception_breakpoint_pause_report_event;      case 3:          return error_notifier_event; -    default: { +    default:          LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id); +        return nullptr;      } -    } -    return nullptr;  }  } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.cpp b/src/core/hle/service/nvflinger/buffer_queue_core.cpp index ea4a14ea4..3d1338e66 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_core.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue_core.cpp @@ -23,15 +23,17 @@ void BufferQueueCore::NotifyShutdown() {  }  void BufferQueueCore::SignalDequeueCondition() { +    dequeue_possible.store(true);      dequeue_condition.notify_all();  } -bool BufferQueueCore::WaitForDequeueCondition() { +bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock<std::mutex>& lk) {      if (is_shutting_down) {          return false;      } -    dequeue_condition.wait(mutex); +    dequeue_condition.wait(lk, [&] { return dequeue_possible.load(); }); +    dequeue_possible.store(false);      return true;  } diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.h b/src/core/hle/service/nvflinger/buffer_queue_core.h index ca6baefaf..85b3bc4c1 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_core.h +++ b/src/core/hle/service/nvflinger/buffer_queue_core.h @@ -38,7 +38,7 @@ public:  private:      void SignalDequeueCondition(); -    bool WaitForDequeueCondition(); +    bool WaitForDequeueCondition(std::unique_lock<std::mutex>& lk);      s32 GetMinUndequeuedBufferCountLocked(bool async) const;      s32 GetMinMaxBufferCountLocked(bool async) const; @@ -60,7 +60,8 @@ private:      BufferQueueDefs::SlotsType slots{};      std::vector<BufferItem> queue;      s32 override_max_buffer_count{}; -    mutable std::condition_variable_any dequeue_condition; +    std::condition_variable dequeue_condition; +    std::atomic<bool> dequeue_possible{};      const bool use_async_buffer{}; // This is always disabled on HOS      bool dequeue_buffer_cannot_block{};      PixelFormat default_buffer_format{PixelFormat::Rgba8888}; diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp index 41ba44b21..e601b5da1 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp @@ -121,8 +121,8 @@ Status BufferQueueProducer::SetBufferCount(s32 buffer_count) {      return Status::NoError;  } -Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, -                                                      Status* return_flags) const { +Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags, +                                                      std::unique_lock<std::mutex>& lk) const {      bool try_again = true;      while (try_again) { @@ -214,7 +214,7 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found,                  return Status::WouldBlock;              } -            if (!core->WaitForDequeueCondition()) { +            if (!core->WaitForDequeueCondition(lk)) {                  // We are no longer running                  return Status::NoError;              } @@ -237,7 +237,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool      Status return_flags = Status::NoError;      bool attached_by_consumer = false;      { -        std::scoped_lock lock{core->mutex}; +        std::unique_lock lock{core->mutex};          core->WaitWhileAllocatingLocked();          if (format == PixelFormat::NoFormat) { @@ -248,7 +248,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool          usage |= core->consumer_usage_bit;          s32 found{}; -        Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags); +        Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags, lock);          if (status != Status::NoError) {              return status;          } @@ -400,13 +400,13 @@ Status BufferQueueProducer::AttachBuffer(s32* out_slot,          return Status::BadValue;      } -    std::scoped_lock lock{core->mutex}; +    std::unique_lock lock{core->mutex};      core->WaitWhileAllocatingLocked();      Status return_flags = Status::NoError;      s32 found{}; -    const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags); +    const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags, lock);      if (status != Status::NoError) {          return status;      } diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.h b/src/core/hle/service/nvflinger/buffer_queue_producer.h index 7526bf8ec..1d380480f 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_producer.h +++ b/src/core/hle/service/nvflinger/buffer_queue_producer.h @@ -70,7 +70,8 @@ public:  private:      BufferQueueProducer(const BufferQueueProducer&) = delete; -    Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags) const; +    Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags, +                                     std::unique_lock<std::mutex>& lk) const;      Kernel::KEvent* buffer_wait_event{};      Service::KernelHelpers::ServiceContext& service_context; diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 5ab41c0c4..0de67f1e1 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -228,6 +228,7 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session,          }          UNIMPLEMENTED_MSG("command_type={}", ctx.GetCommandType()); +        break;      }      // If emulation was shutdown, we are closing service threads, do not write the response back to diff --git a/src/core/hle/service/time/time_zone_manager.cpp b/src/core/hle/service/time/time_zone_manager.cpp index 2aa675df9..f9ada7c93 100644 --- a/src/core/hle/service/time/time_zone_manager.cpp +++ b/src/core/hle/service/time/time_zone_manager.cpp @@ -280,6 +280,7 @@ static constexpr int TransitionTime(int year, Rule rule, int offset) {      }      default:          ASSERT(false); +        break;      }      return value + rule.transition_time + offset;  } diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 4a2f2c1fd..d502d181c 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -249,6 +249,11 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume          return;      case MAXWELL3D_REG_INDEX(fragment_barrier):          return rasterizer->FragmentBarrier(); +    case MAXWELL3D_REG_INDEX(invalidate_texture_data_cache): +        rasterizer->InvalidateGPUCache(); +        return rasterizer->WaitForIdle(); +    case MAXWELL3D_REG_INDEX(tiled_cache_barrier): +        return rasterizer->TiledCacheBarrier();      }  } diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 910ab213a..34b085388 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -707,7 +707,7 @@ public:                  case Size::Size_A2_B10_G10_R10:                      return "2_10_10_10";                  case Size::Size_B10_G11_R11: -                    return "10_11_12"; +                    return "10_11_11";                  default:                      ASSERT(false);                      return {}; @@ -2639,7 +2639,7 @@ public:                  L2CacheControl l2_cache_control;                                       ///< 0x0218                  InvalidateShaderCache invalidate_shader_cache;                         ///< 0x021C                  INSERT_PADDING_BYTES_NOINIT(0xA8); -                SyncInfo sync_info; ///< 0x02C8 +                SyncInfo sync_info;                                                    ///< 0x02C8                  INSERT_PADDING_BYTES_NOINIT(0x4);                  u32 prim_circular_buffer_throttle;                                     ///< 0x02D0                  u32 flush_invalidate_rop_mini_cache;                                   ///< 0x02D4 @@ -2731,7 +2731,11 @@ public:                  s32 stencil_back_ref;                                                  ///< 0x0F54                  u32 stencil_back_mask;                                                 ///< 0x0F58                  u32 stencil_back_func_mask;                                            ///< 0x0F5C -                INSERT_PADDING_BYTES_NOINIT(0x24); +                INSERT_PADDING_BYTES_NOINIT(0x14); +                u32 invalidate_texture_data_cache;                                     ///< 0x0F74 Assumed - Not in official docs. +                INSERT_PADDING_BYTES_NOINIT(0x4); +                u32 tiled_cache_barrier;                                               ///< 0x0F7C Assumed - Not in official docs. +                INSERT_PADDING_BYTES_NOINIT(0x4);                  VertexStreamSubstitute vertex_stream_substitute;                       ///< 0x0F84                  u32 line_mode_clip_generated_edge_do_not_draw;                         ///< 0x0F8C                  u32 color_mask_common;                                                 ///< 0x0F90 @@ -2791,7 +2795,8 @@ public:                  FillViaTriangleMode fill_via_triangle_mode;                            ///< 0x113C                  u32 blend_per_format_snorm8_unorm16_snorm16_enabled;                   ///< 0x1140                  u32 flush_pending_writes_sm_gloal_store;                               ///< 0x1144 -                INSERT_PADDING_BYTES_NOINIT(0x18); +                u32 conservative_raster_enable;                                        ///< 0x1148 Assumed - Not in official docs. +                INSERT_PADDING_BYTES_NOINIT(0x14);                  std::array<VertexAttribute, NumVertexAttributes> vertex_attrib_format; ///< 0x1160                  std::array<MsaaSampleLocation, 4> multisample_sample_locations;        ///< 0x11E0                  u32 offset_render_target_index_by_viewport_index;                      ///< 0x11F0 @@ -3287,6 +3292,8 @@ ASSERT_REG_POSITION(const_color_rendering, 0x0F40);  ASSERT_REG_POSITION(stencil_back_ref, 0x0F54);  ASSERT_REG_POSITION(stencil_back_mask, 0x0F58);  ASSERT_REG_POSITION(stencil_back_func_mask, 0x0F5C); +ASSERT_REG_POSITION(invalidate_texture_data_cache, 0x0F74); +ASSERT_REG_POSITION(tiled_cache_barrier, 0x0F7C);  ASSERT_REG_POSITION(vertex_stream_substitute, 0x0F84);  ASSERT_REG_POSITION(line_mode_clip_generated_edge_do_not_draw, 0x0F8C);  ASSERT_REG_POSITION(color_mask_common, 0x0F90); @@ -3343,6 +3350,7 @@ ASSERT_REG_POSITION(post_ps_use_pre_ps_coverage, 0x1138);  ASSERT_REG_POSITION(fill_via_triangle_mode, 0x113C);  ASSERT_REG_POSITION(blend_per_format_snorm8_unorm16_snorm16_enabled, 0x1140);  ASSERT_REG_POSITION(flush_pending_writes_sm_gloal_store, 0x1144); +ASSERT_REG_POSITION(conservative_raster_enable, 0x1148);  ASSERT_REG_POSITION(vertex_attrib_format, 0x1160);  ASSERT_REG_POSITION(multisample_sample_locations, 0x11E0);  ASSERT_REG_POSITION(offset_render_target_index_by_viewport_index, 0x11F0); diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index 4eb7a100d..1bf6ca2dd 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp @@ -102,26 +102,29 @@ void MaxwellDMA::Launch() {              const bool is_src_pitch = IsPitchKind(static_cast<PTEKind>(src_kind));              const bool is_dst_pitch = IsPitchKind(static_cast<PTEKind>(dst_kind));              if (!is_src_pitch && is_dst_pitch) { -                std::vector<u8> tmp_buffer(regs.line_length_in); -                std::vector<u8> dst_buffer(regs.line_length_in); -                memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(), -                                               regs.line_length_in); -                for (u32 offset = 0; offset < regs.line_length_in; ++offset) { -                    dst_buffer[offset] = -                        tmp_buffer[convert_linear_2_blocklinear_addr(regs.offset_in + offset) - -                                   regs.offset_in]; +                UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0); +                UNIMPLEMENTED_IF(regs.offset_in % 16 != 0); +                UNIMPLEMENTED_IF(regs.offset_out % 16 != 0); +                std::vector<u8> tmp_buffer(16); +                for (u32 offset = 0; offset < regs.line_length_in; offset += 16) { +                    memory_manager.ReadBlockUnsafe( +                        convert_linear_2_blocklinear_addr(regs.offset_in + offset), +                        tmp_buffer.data(), tmp_buffer.size()); +                    memory_manager.WriteBlock(regs.offset_out + offset, tmp_buffer.data(), +                                              tmp_buffer.size());                  } -                memory_manager.WriteBlock(regs.offset_out, dst_buffer.data(), regs.line_length_in);              } else if (is_src_pitch && !is_dst_pitch) { -                std::vector<u8> tmp_buffer(regs.line_length_in); -                std::vector<u8> dst_buffer(regs.line_length_in); -                memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(), -                                               regs.line_length_in); -                for (u32 offset = 0; offset < regs.line_length_in; ++offset) { -                    dst_buffer[convert_linear_2_blocklinear_addr(regs.offset_out + offset) - -                               regs.offset_out] = tmp_buffer[offset]; +                UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0); +                UNIMPLEMENTED_IF(regs.offset_in % 16 != 0); +                UNIMPLEMENTED_IF(regs.offset_out % 16 != 0); +                std::vector<u8> tmp_buffer(16); +                for (u32 offset = 0; offset < regs.line_length_in; offset += 16) { +                    memory_manager.ReadBlockUnsafe(regs.offset_in + offset, tmp_buffer.data(), +                                                   tmp_buffer.size()); +                    memory_manager.WriteBlock( +                        convert_linear_2_blocklinear_addr(regs.offset_out + offset), +                        tmp_buffer.data(), tmp_buffer.size());                  } -                memory_manager.WriteBlock(regs.offset_out, dst_buffer.data(), regs.line_length_in);              } else {                  if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) {                      std::vector<u8> tmp_buffer(regs.line_length_in); @@ -311,6 +314,7 @@ void MaxwellDMA::ReleaseSemaphore() {      }      default:          ASSERT_MSG(false, "Unknown semaphore type: {}", static_cast<u32>(type.Value())); +        break;      }  } diff --git a/src/video_core/engines/puller.cpp b/src/video_core/engines/puller.cpp index 3977bb0fb..4d2278811 100644 --- a/src/video_core/engines/puller.cpp +++ b/src/video_core/engines/puller.cpp @@ -50,6 +50,7 @@ void Puller::ProcessBindMethod(const MethodCall& method_call) {          break;      default:          UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id); +        break;      }  } @@ -65,6 +66,7 @@ void Puller::ProcessFenceActionMethod() {          break;      default:          UNIMPLEMENTED_MSG("Unimplemented operation {}", regs.fence_action.op.Value()); +        break;      }  } @@ -228,6 +230,7 @@ void Puller::CallEngineMethod(const MethodCall& method_call) {          break;      default:          UNIMPLEMENTED_MSG("Unimplemented engine"); +        break;      }  } @@ -254,6 +257,7 @@ void Puller::CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_s          break;      default:          UNIMPLEMENTED_MSG("Unimplemented engine"); +        break;      }  } diff --git a/src/video_core/macro/macro_interpreter.cpp b/src/video_core/macro/macro_interpreter.cpp index c0d32c112..0d63495a9 100644 --- a/src/video_core/macro/macro_interpreter.cpp +++ b/src/video_core/macro/macro_interpreter.cpp @@ -201,6 +201,7 @@ bool MacroInterpreterImpl::Step(bool is_delay_slot) {      }      default:          UNIMPLEMENTED_MSG("Unimplemented macro operation {}", opcode.operation.Value()); +        break;      }      // An instruction with the Exit flag will not actually @@ -297,6 +298,7 @@ void MacroInterpreterImpl::ProcessResult(Macro::ResultOperation operation, u32 r          break;      default:          UNIMPLEMENTED_MSG("Unimplemented result operation {}", operation); +        break;      }  } diff --git a/src/video_core/macro/macro_jit_x64.cpp b/src/video_core/macro/macro_jit_x64.cpp index 25c1ce798..7347cbd88 100644 --- a/src/video_core/macro/macro_jit_x64.cpp +++ b/src/video_core/macro/macro_jit_x64.cpp @@ -652,6 +652,7 @@ void MacroJITx64Impl::Compile_ProcessResult(Macro::ResultOperation operation, u3          break;      default:          UNIMPLEMENTED_MSG("Unimplemented macro operation {}", operation); +        break;      }  } diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 8a8b5ce54..d05a5f60b 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -770,7 +770,7 @@ void RasterizerOpenGL::SyncStencilTestState() {      if (regs.stencil_two_side_enable) {          glStencilFuncSeparate(GL_BACK, MaxwellToGL::ComparisonOp(regs.stencil_back_op.func), -                              regs.stencil_back_ref, regs.stencil_back_mask); +                              regs.stencil_back_ref, regs.stencil_back_func_mask);          glStencilOpSeparate(GL_BACK, MaxwellToGL::StencilOp(regs.stencil_back_op.fail),                              MaxwellToGL::StencilOp(regs.stencil_back_op.zfail),                              MaxwellToGL::StencilOp(regs.stencil_back_op.zpass)); diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index 99cd11d1e..9f7ce7414 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp @@ -891,6 +891,7 @@ void Image::CopyBufferToImage(const VideoCommon::BufferImageCopy& copy, size_t b          break;      default:          ASSERT(false); +        break;      }  } @@ -927,6 +928,7 @@ void Image::CopyImageToBuffer(const VideoCommon::BufferImageCopy& copy, size_t b          break;      default:          ASSERT(false); +        break;      }      // Compressed formats don't have a pixel format or type      const bool is_compressed = gl_format == GL_NONE; diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 8bd5eba7e..f29462f7c 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -340,6 +340,7 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,          texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;          // UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}",          //                   static_cast<u32>(framebuffer.pixel_format)); +        break;      }      texture.resource.Release(); diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index f85ed8e5b..98cc26679 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -90,6 +90,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,      depth_format.Assign(static_cast<u32>(regs.zeta.format));      y_negate.Assign(regs.window_origin.mode != Maxwell::WindowOrigin::Mode::UpperLeft ? 1 : 0);      provoking_vertex_last.Assign(regs.provoking_vertex == Maxwell::ProvokingVertex::Last ? 1 : 0); +    conservative_raster_enable.Assign(regs.conservative_raster_enable != 0 ? 1 : 0);      smooth_lines.Assign(regs.line_anti_alias_enable != 0 ? 1 : 0);      for (size_t i = 0; i < regs.rt.size(); ++i) { diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index 43441209c..1afdef329 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h @@ -193,6 +193,7 @@ struct FixedPipelineState {          BitField<6, 5, u32> depth_format;          BitField<11, 1, u32> y_negate;          BitField<12, 1, u32> provoking_vertex_last; +        BitField<13, 1, u32> conservative_raster_enable;          BitField<14, 1, u32> smooth_lines;      };      std::array<u8, Maxwell::NumRenderTargets> color_formats; diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 1aa116cea..ef75c126c 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -680,6 +680,15 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {          .lineStippleFactor = 0,          .lineStipplePattern = 0,      }; +    VkPipelineRasterizationConservativeStateCreateInfoEXT conservative_raster{ +        .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_CONSERVATIVE_STATE_CREATE_INFO_EXT, +        .pNext = nullptr, +        .flags = 0, +        .conservativeRasterizationMode = key.state.conservative_raster_enable != 0 +                                             ? VK_CONSERVATIVE_RASTERIZATION_MODE_OVERESTIMATE_EXT +                                             : VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT, +        .extraPrimitiveOverestimationSize = 0.0f, +    };      VkPipelineRasterizationProvokingVertexStateCreateInfoEXT provoking_vertex{          .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT,          .pNext = nullptr, @@ -690,6 +699,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {      if (IsLine(input_assembly_topology) && device.IsExtLineRasterizationSupported()) {          line_state.pNext = std::exchange(rasterization_ci.pNext, &line_state);      } +    if (device.IsExtConservativeRasterizationSupported()) { +        conservative_raster.pNext = std::exchange(rasterization_ci.pNext, &conservative_raster); +    }      if (device.IsExtProvokingVertexSupported()) {          provoking_vertex.pNext = std::exchange(rasterization_ci.pNext, &provoking_vertex);      } diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 7934f2a51..4a7b633b7 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -221,6 +221,7 @@ void Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_s              [[fallthrough]];          default:              vk::Check(result); +            break;          }      });      chunk->MarkSubmit(); diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 853b80d8a..a65bbeb1c 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -108,6 +108,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {              break;          default:              ASSERT_MSG(false, "Invalid surface type"); +            break;          }      }      if (info.storage) { diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index fd1a4b987..59120cd09 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp @@ -170,6 +170,7 @@ void Swizzle(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixe  #undef BPP_CASE      default:          ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel); +        break;      }  } @@ -217,6 +218,7 @@ void SwizzleSubrect(std::span<u8> output, std::span<const u8> input, u32 bytes_p  #undef BPP_CASE      default:          ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel); +        break;      }  } @@ -240,6 +242,7 @@ void UnswizzleSubrect(std::span<u8> output, std::span<const u8> input, u32 bytes  #undef BPP_CASE      default:          ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel); +        break;      }  } diff --git a/src/yuzu/compatdb.cpp b/src/yuzu/compatdb.cpp index f46fff340..05f49c0d2 100644 --- a/src/yuzu/compatdb.cpp +++ b/src/yuzu/compatdb.cpp @@ -15,12 +15,22 @@ CompatDB::CompatDB(Core::TelemetrySession& telemetry_session_, QWidget* parent)      : QWizard(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint),        ui{std::make_unique<Ui::CompatDB>()}, telemetry_session{telemetry_session_} {      ui->setupUi(this); -    connect(ui->radioButton_Perfect, &QRadioButton::clicked, this, &CompatDB::EnableNext); -    connect(ui->radioButton_Great, &QRadioButton::clicked, this, &CompatDB::EnableNext); -    connect(ui->radioButton_Okay, &QRadioButton::clicked, this, &CompatDB::EnableNext); -    connect(ui->radioButton_Bad, &QRadioButton::clicked, this, &CompatDB::EnableNext); -    connect(ui->radioButton_IntroMenu, &QRadioButton::clicked, this, &CompatDB::EnableNext); -    connect(ui->radioButton_WontBoot, &QRadioButton::clicked, this, &CompatDB::EnableNext); + +    connect(ui->radioButton_GameBoot_Yes, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_GameBoot_No, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_Gameplay_Yes, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_Gameplay_No, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_NoFreeze_Yes, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_NoFreeze_No, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_Complete_Yes, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_Complete_No, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_Graphical_Major, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_Graphical_Minor, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_Graphical_No, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_Audio_Major, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_Audio_Minor, &QRadioButton::clicked, this, &CompatDB::EnableNext); +    connect(ui->radioButton_Audio_No, &QRadioButton::clicked, this, &CompatDB::EnableNext); +      connect(button(NextButton), &QPushButton::clicked, this, &CompatDB::Submit);      connect(&testcase_watcher, &QFutureWatcher<bool>::finished, this,              &CompatDB::OnTestcaseSubmitted); @@ -30,29 +40,82 @@ CompatDB::~CompatDB() = default;  enum class CompatDBPage {      Intro = 0, -    Selection = 1, -    Final = 2, +    GameBoot = 1, +    GamePlay = 2, +    Freeze = 3, +    Completion = 4, +    Graphical = 5, +    Audio = 6, +    Final = 7,  };  void CompatDB::Submit() { -    QButtonGroup* compatibility = new QButtonGroup(this); -    compatibility->addButton(ui->radioButton_Perfect, 0); -    compatibility->addButton(ui->radioButton_Great, 1); -    compatibility->addButton(ui->radioButton_Okay, 2); -    compatibility->addButton(ui->radioButton_Bad, 3); -    compatibility->addButton(ui->radioButton_IntroMenu, 4); -    compatibility->addButton(ui->radioButton_WontBoot, 5); +    QButtonGroup* compatibility_GameBoot = new QButtonGroup(this); +    compatibility_GameBoot->addButton(ui->radioButton_GameBoot_Yes, 0); +    compatibility_GameBoot->addButton(ui->radioButton_GameBoot_No, 1); + +    QButtonGroup* compatibility_Gameplay = new QButtonGroup(this); +    compatibility_Gameplay->addButton(ui->radioButton_Gameplay_Yes, 0); +    compatibility_Gameplay->addButton(ui->radioButton_Gameplay_No, 1); + +    QButtonGroup* compatibility_NoFreeze = new QButtonGroup(this); +    compatibility_NoFreeze->addButton(ui->radioButton_NoFreeze_Yes, 0); +    compatibility_NoFreeze->addButton(ui->radioButton_NoFreeze_No, 1); + +    QButtonGroup* compatibility_Complete = new QButtonGroup(this); +    compatibility_Complete->addButton(ui->radioButton_Complete_Yes, 0); +    compatibility_Complete->addButton(ui->radioButton_Complete_No, 1); + +    QButtonGroup* compatibility_Graphical = new QButtonGroup(this); +    compatibility_Graphical->addButton(ui->radioButton_Graphical_Major, 0); +    compatibility_Graphical->addButton(ui->radioButton_Graphical_Minor, 1); +    compatibility_Graphical->addButton(ui->radioButton_Graphical_No, 2); + +    QButtonGroup* compatibility_Audio = new QButtonGroup(this); +    compatibility_Audio->addButton(ui->radioButton_Audio_Major, 0); +    compatibility_Graphical->addButton(ui->radioButton_Audio_Minor, 1); +    compatibility_Audio->addButton(ui->radioButton_Audio_No, 2); + +    const int compatiblity = static_cast<int>(CalculateCompatibility()); +      switch ((static_cast<CompatDBPage>(currentId()))) { -    case CompatDBPage::Selection: -        if (compatibility->checkedId() == -1) { +    case CompatDBPage::Intro: +        break; +    case CompatDBPage::GameBoot: +        if (compatibility_GameBoot->checkedId() == -1) { +            button(NextButton)->setEnabled(false); +        } +        break; +    case CompatDBPage::GamePlay: +        if (compatibility_Gameplay->checkedId() == -1) { +            button(NextButton)->setEnabled(false); +        } +        break; +    case CompatDBPage::Freeze: +        if (compatibility_NoFreeze->checkedId() == -1) { +            button(NextButton)->setEnabled(false); +        } +        break; +    case CompatDBPage::Completion: +        if (compatibility_Complete->checkedId() == -1) { +            button(NextButton)->setEnabled(false); +        } +        break; +    case CompatDBPage::Graphical: +        if (compatibility_Graphical->checkedId() == -1) { +            button(NextButton)->setEnabled(false); +        } +        break; +    case CompatDBPage::Audio: +        if (compatibility_Audio->checkedId() == -1) {              button(NextButton)->setEnabled(false);          }          break;      case CompatDBPage::Final:          back(); -        LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId()); +        LOG_INFO(Frontend, "Compatibility Rating: {}", compatiblity);          telemetry_session.AddField(Common::Telemetry::FieldType::UserFeedback, "Compatibility", -                                   compatibility->checkedId()); +                                   compatiblity);          button(NextButton)->setEnabled(false);          button(NextButton)->setText(tr("Submitting")); @@ -63,9 +126,70 @@ void CompatDB::Submit() {          break;      default:          LOG_ERROR(Frontend, "Unexpected page: {}", currentId()); +        break; +    } +} + +int CompatDB::nextId() const { +    switch ((static_cast<CompatDBPage>(currentId()))) { +    case CompatDBPage::Intro: +        return static_cast<int>(CompatDBPage::GameBoot); +    case CompatDBPage::GameBoot: +        if (ui->radioButton_GameBoot_No->isChecked()) { +            return static_cast<int>(CompatDBPage::Final); +        } +        return static_cast<int>(CompatDBPage::GamePlay); +    case CompatDBPage::GamePlay: +        if (ui->radioButton_Gameplay_No->isChecked()) { +            return static_cast<int>(CompatDBPage::Final); +        } +        return static_cast<int>(CompatDBPage::Freeze); +    case CompatDBPage::Freeze: +        if (ui->radioButton_NoFreeze_No->isChecked()) { +            return static_cast<int>(CompatDBPage::Final); +        } +        return static_cast<int>(CompatDBPage::Completion); +    case CompatDBPage::Completion: +        if (ui->radioButton_Complete_No->isChecked()) { +            return static_cast<int>(CompatDBPage::Final); +        } +        return static_cast<int>(CompatDBPage::Graphical); +    case CompatDBPage::Graphical: +        return static_cast<int>(CompatDBPage::Audio); +    case CompatDBPage::Audio: +        return static_cast<int>(CompatDBPage::Final); +    case CompatDBPage::Final: +        return -1; +    default: +        LOG_ERROR(Frontend, "Unexpected page: {}", currentId()); +        return static_cast<int>(CompatDBPage::Intro);      }  } +CompatibilityStatus CompatDB::CalculateCompatibility() const { +    if (ui->radioButton_GameBoot_No->isChecked()) { +        return CompatibilityStatus::WontBoot; +    } + +    if (ui->radioButton_Gameplay_No->isChecked()) { +        return CompatibilityStatus::IntroMenu; +    } + +    if (ui->radioButton_NoFreeze_No->isChecked() || ui->radioButton_Complete_No->isChecked()) { +        return CompatibilityStatus::Ingame; +    } + +    if (ui->radioButton_Graphical_Major->isChecked() || ui->radioButton_Audio_Major->isChecked()) { +        return CompatibilityStatus::Ingame; +    } + +    if (ui->radioButton_Graphical_Minor->isChecked() || ui->radioButton_Audio_Minor->isChecked()) { +        return CompatibilityStatus::Playable; +    } + +    return CompatibilityStatus::Perfect; +} +  void CompatDB::OnTestcaseSubmitted() {      if (!testcase_watcher.result()) {          QMessageBox::critical(this, tr("Communication error"), diff --git a/src/yuzu/compatdb.h b/src/yuzu/compatdb.h index 3252fc47a..37e11278b 100644 --- a/src/yuzu/compatdb.h +++ b/src/yuzu/compatdb.h @@ -12,12 +12,22 @@ namespace Ui {  class CompatDB;  } +enum class CompatibilityStatus { +    Perfect = 0, +    Playable = 1, +    // Unused: Okay = 2, +    Ingame = 3, +    IntroMenu = 4, +    WontBoot = 5, +}; +  class CompatDB : public QWizard {      Q_OBJECT  public:      explicit CompatDB(Core::TelemetrySession& telemetry_session_, QWidget* parent = nullptr);      ~CompatDB(); +    int nextId() const override;  private:      QFutureWatcher<bool> testcase_watcher; @@ -25,6 +35,7 @@ private:      std::unique_ptr<Ui::CompatDB> ui;      void Submit(); +    CompatibilityStatus CalculateCompatibility() const;      void OnTestcaseSubmitted();      void EnableNext(); diff --git a/src/yuzu/compatdb.ui b/src/yuzu/compatdb.ui index 3ca55eda6..d11669df2 100644 --- a/src/yuzu/compatdb.ui +++ b/src/yuzu/compatdb.ui @@ -58,128 +58,311 @@      </item>     </layout>    </widget> -  <widget class="QWizardPage" name="wizard_Report"> +  <widget class="QWizardPage" name="wizard_GameBoot">     <property name="title">      <string>Report Game Compatibility</string>     </property>     <attribute name="pageId">      <string notr="true">1</string>     </attribute> -   <layout class="QFormLayout" name="formLayout"> +   <layout class="QFormLayout" name="formLayout1"> +    <item row="0" column="0" colspan="2"> +     <widget class="QLabel" name="lbl_Independent1"> +      <property name="font"> +       <font> +        <pointsize>10</pointsize> +       </font> +      </property> +      <property name="text"> +       <string><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/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp index 5c0217ba8..a47089988 100644 --- a/src/yuzu/configuration/configure_profile_manager.cpp +++ b/src/yuzu/configuration/configure_profile_manager.cpp @@ -2,6 +2,9 @@  // SPDX-License-Identifier: GPL-2.0-or-later  #include <algorithm> +#include <functional> +#include <QDialog> +#include <QDialogButtonBox>  #include <QFileDialog>  #include <QGraphicsItem>  #include <QHeaderView> @@ -108,9 +111,12 @@ ConfigureProfileManager::ConfigureProfileManager(const Core::System& system_, QW      connect(ui->pm_add, &QPushButton::clicked, this, &ConfigureProfileManager::AddUser);      connect(ui->pm_rename, &QPushButton::clicked, this, &ConfigureProfileManager::RenameUser); -    connect(ui->pm_remove, &QPushButton::clicked, this, &ConfigureProfileManager::DeleteUser); +    connect(ui->pm_remove, &QPushButton::clicked, this, +            &ConfigureProfileManager::ConfirmDeleteUser);      connect(ui->pm_set_image, &QPushButton::clicked, this, &ConfigureProfileManager::SetUserImage); +    confirm_dialog = new ConfigureProfileManagerDeleteDialog(this); +      scene = new QGraphicsScene;      ui->current_user_icon->setScene(scene); @@ -230,26 +236,23 @@ void ConfigureProfileManager::RenameUser() {      UpdateCurrentUser();  } -void ConfigureProfileManager::DeleteUser() { +void ConfigureProfileManager::ConfirmDeleteUser() {      const auto index = tree_view->currentIndex().row();      const auto uuid = profile_manager->GetUser(index);      ASSERT(uuid);      const auto username = GetAccountUsername(*profile_manager, *uuid); -    const auto confirm = QMessageBox::question( -        this, tr("Confirm Delete"), -        tr("You are about to delete user with name \"%1\". Are you sure?").arg(username)); - -    if (confirm == QMessageBox::No) { -        return; -    } +    confirm_dialog->SetInfo(username, *uuid, [this, uuid]() { DeleteUser(*uuid); }); +    confirm_dialog->show(); +} +void ConfigureProfileManager::DeleteUser(const Common::UUID& uuid) {      if (Settings::values.current_user.GetValue() == tree_view->currentIndex().row()) {          Settings::values.current_user = 0;      }      UpdateCurrentUser(); -    if (!profile_manager->RemoveUser(*uuid)) { +    if (!profile_manager->RemoveUser(uuid)) {          return;      } @@ -319,3 +322,47 @@ void ConfigureProfileManager::SetUserImage() {                          new QStandardItem{GetIcon(*uuid), FormatUserEntryText(username, *uuid)});      UpdateCurrentUser();  } + +ConfigureProfileManagerDeleteDialog::ConfigureProfileManagerDeleteDialog(QWidget* parent) +    : QDialog{parent} { +    auto dialog_vbox_layout = new QVBoxLayout(this); +    dialog_button_box = +        new QDialogButtonBox(QDialogButtonBox::Yes | QDialogButtonBox::No, Qt::Horizontal, parent); +    auto label_message = +        new QLabel(tr("Delete this user? All of the user's save data will be deleted."), this); +    label_info = new QLabel(this); +    auto dialog_hbox_layout_widget = new QWidget(this); +    auto dialog_hbox_layout = new QHBoxLayout(dialog_hbox_layout_widget); +    icon_scene = new QGraphicsScene(0, 0, 64, 64, this); +    auto icon_view = new QGraphicsView(icon_scene, this); + +    dialog_hbox_layout_widget->setLayout(dialog_hbox_layout); +    icon_view->setMaximumSize(64, 64); +    icon_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); +    icon_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); +    this->setLayout(dialog_vbox_layout); +    this->setWindowTitle(tr("Confirm Delete")); +    this->setSizeGripEnabled(false); +    dialog_vbox_layout->addWidget(label_message); +    dialog_vbox_layout->addWidget(dialog_hbox_layout_widget); +    dialog_vbox_layout->addWidget(dialog_button_box); +    dialog_hbox_layout->addWidget(icon_view); +    dialog_hbox_layout->addWidget(label_info); + +    connect(dialog_button_box, &QDialogButtonBox::rejected, this, [this]() { close(); }); +} + +ConfigureProfileManagerDeleteDialog::~ConfigureProfileManagerDeleteDialog() = default; + +void ConfigureProfileManagerDeleteDialog::SetInfo(const QString& username, const Common::UUID& uuid, +                                                  std::function<void()> accept_callback) { +    label_info->setText( +        tr("Name: %1\nUUID: %2").arg(username, QString::fromStdString(uuid.FormattedString()))); +    icon_scene->clear(); +    icon_scene->addPixmap(GetIcon(uuid)); + +    connect(dialog_button_box, &QDialogButtonBox::accepted, this, [this, accept_callback]() { +        close(); +        accept_callback(); +    }); +} diff --git a/src/yuzu/configuration/configure_profile_manager.h b/src/yuzu/configuration/configure_profile_manager.h index fe9033779..c4b1a334e 100644 --- a/src/yuzu/configuration/configure_profile_manager.h +++ b/src/yuzu/configuration/configure_profile_manager.h @@ -3,16 +3,24 @@  #pragma once +#include <functional>  #include <memory> +#include <QDialog>  #include <QList>  #include <QWidget> +namespace Common { +struct UUID; +} +  namespace Core {  class System;  }  class QGraphicsScene; +class QDialogButtonBox; +class QLabel;  class QStandardItem;  class QStandardItemModel;  class QTreeView; @@ -26,6 +34,20 @@ namespace Ui {  class ConfigureProfileManager;  } +class ConfigureProfileManagerDeleteDialog : public QDialog { +public: +    explicit ConfigureProfileManagerDeleteDialog(QWidget* parent); +    ~ConfigureProfileManagerDeleteDialog(); + +    void SetInfo(const QString& username, const Common::UUID& uuid, +                 std::function<void()> accept_callback); + +private: +    QDialogButtonBox* dialog_button_box; +    QGraphicsScene* icon_scene; +    QLabel* label_info; +}; +  class ConfigureProfileManager : public QWidget {      Q_OBJECT @@ -47,7 +69,8 @@ private:      void SelectUser(const QModelIndex& index);      void AddUser();      void RenameUser(); -    void DeleteUser(); +    void ConfirmDeleteUser(); +    void DeleteUser(const Common::UUID& uuid);      void SetUserImage();      QVBoxLayout* layout; @@ -55,6 +78,8 @@ private:      QStandardItemModel* item_model;      QGraphicsScene* scene; +    ConfigureProfileManagerDeleteDialog* confirm_dialog; +      std::vector<QList<QStandardItem*>> list_items;      std::unique_ptr<Ui::ConfigureProfileManager> ui; diff --git a/src/yuzu/configuration/configure_profile_manager.ui b/src/yuzu/configuration/configure_profile_manager.ui index cfe7478c8..bd6dea4f4 100644 --- a/src/yuzu/configuration/configure_profile_manager.ui +++ b/src/yuzu/configuration/configure_profile_manager.ui @@ -57,6 +57,12 @@                <height>48</height>               </size>              </property> +            <property name="frameShape"> +             <enum>QFrame::NoFrame</enum> +            </property> +            <property name="frameShadow"> +             <enum>QFrame::Plain</enum> +            </property>              <property name="verticalScrollBarPolicy">               <enum>Qt::ScrollBarAlwaysOff</enum>              </property> diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index 6198d1e4e..1800f090f 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h @@ -145,12 +145,14 @@ public:              const char* tooltip;          };          // clang-format off +        const auto ingame_status = +                       CompatStatus{QStringLiteral("#f2d624"), QT_TR_NOOP("Ingame"),     QT_TR_NOOP("Game starts, but crashes or major glitches prevent it from being completed.")};          static const std::map<QString, CompatStatus> status_data = { -            {QStringLiteral("0"),  {QStringLiteral("#5c93ed"), QT_TR_NOOP("Perfect"),    QT_TR_NOOP("Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without\nany workarounds needed.")}}, -            {QStringLiteral("1"),  {QStringLiteral("#47d35c"), QT_TR_NOOP("Great"),      QT_TR_NOOP("Game functions with minor graphical or audio glitches and is playable from start to finish. May require some\nworkarounds.")}}, -            {QStringLiteral("2"),  {QStringLiteral("#94b242"), QT_TR_NOOP("Okay"),       QT_TR_NOOP("Game functions with major graphical or audio glitches, but game is playable from start to finish with\nworkarounds.")}}, -            {QStringLiteral("3"),  {QStringLiteral("#f2d624"), QT_TR_NOOP("Bad"),        QT_TR_NOOP("Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches\neven with workarounds.")}}, -            {QStringLiteral("4"),  {QStringLiteral("#FF0000"), QT_TR_NOOP("Intro/Menu"), QT_TR_NOOP("Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start\nScreen.")}}, +            {QStringLiteral("0"),  {QStringLiteral("#5c93ed"), QT_TR_NOOP("Perfect"),    QT_TR_NOOP("Game can be played without issues.")}}, +            {QStringLiteral("1"),  {QStringLiteral("#47d35c"), QT_TR_NOOP("Playable"),   QT_TR_NOOP("Game functions with minor graphical or audio glitches and is playable from start to finish.")}}, +            {QStringLiteral("2"),  ingame_status}, +            {QStringLiteral("3"),  ingame_status}, // Fallback for the removed "Okay" category +            {QStringLiteral("4"),  {QStringLiteral("#FF0000"), QT_TR_NOOP("Intro/Menu"), QT_TR_NOOP("Game loads, but is unable to progress past the Start Screen.")}},              {QStringLiteral("5"),  {QStringLiteral("#828282"), QT_TR_NOOP("Won't Boot"), QT_TR_NOOP("The game crashes when attempting to startup.")}},              {QStringLiteral("99"), {QStringLiteral("#000000"), QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}},          }; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 01a002e4f..7ee2302cc 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -361,6 +361,9 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan          }      }      LOG_INFO(Frontend, "Host CPU: {}", cpu_string); +    if (std::optional<int> processor_core = Common::GetProcessorCount()) { +        LOG_INFO(Frontend, "Host CPU Cores: {}", *processor_core); +    }  #endif      LOG_INFO(Frontend, "Host CPU Threads: {}", processor_count);      LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString()); @@ -1953,6 +1956,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target      }      default:          UNIMPLEMENTED(); +        break;      }      const QString qpath = QString::fromStdString(Common::FS::PathToUTF8String(path)); @@ -2817,6 +2821,20 @@ void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_tex  }  void GMainWindow::OnMenuReportCompatibility() { +    const auto& caps = Common::GetCPUCaps(); +    const bool has_fma = caps.fma || caps.fma4; +    const auto processor_count = std::thread::hardware_concurrency(); +    const bool has_4threads = processor_count == 0 || processor_count >= 4; +    const bool has_8gb_ram = Common::GetMemInfo().TotalPhysicalMemory >= 8_GiB; +    const bool has_broken_vulkan = UISettings::values.has_broken_vulkan; + +    if (!has_fma || !has_4threads || !has_8gb_ram || has_broken_vulkan) { +        QMessageBox::critical(this, tr("Hardware requirements not met"), +                              tr("Your system does not meet the recommended hardware requirements. " +                                 "Compatibility reporting has been disabled.")); +        return; +    } +      if (!Settings::values.yuzu_token.GetValue().empty() &&          !Settings::values.yuzu_username.GetValue().empty()) {          CompatDB compatdb{system->TelemetrySession(), this}; @@ -3182,6 +3200,7 @@ void GMainWindow::OnToggleGpuAccuracy() {      case Settings::GPUAccuracy::Extreme:      default: {          Settings::values.gpu_accuracy.SetValue(Settings::GPUAccuracy::High); +        break;      }      } @@ -3514,6 +3533,7 @@ void GMainWindow::UpdateGPUAccuracyButton() {      default: {          gpu_accuracy_button->setText(tr("GPU ERROR"));          gpu_accuracy_button->setChecked(true); +        break;      }      }  } diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp index 65455c86e..25948328c 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp @@ -84,6 +84,7 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsyste      default:          LOG_CRITICAL(Frontend, "Window manager subsystem not implemented");          std::exit(EXIT_FAILURE); +        break;      }      OnResize(); diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index e16f79eb4..dfe5a30ea 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -351,6 +351,7 @@ int main(int argc, char** argv) {                           "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}",                           loader_id, error_id, static_cast<Loader::ResultStatus>(error_id));          } +        break;      }      system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", "SDL"); | 
