diff options
Diffstat (limited to 'src')
73 files changed, 1635 insertions, 1174 deletions
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index aecb66c32..2d9374783 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -78,16 +78,17 @@ namespace FileUtil { // Remove any ending forward slashes from directory paths // Modifies argument. static void StripTailDirSlashes(std::string& fname) { - if (fname.length() > 1) { - std::size_t i = fname.length(); - while (i > 0 && fname[i - 1] == DIR_SEP_CHR) - --i; - fname.resize(i); + if (fname.length() <= 1) { + return; + } + + std::size_t i = fname.length(); + while (i > 0 && fname[i - 1] == DIR_SEP_CHR) { + --i; } - return; + fname.resize(i); } -// Returns true if file filename exists bool Exists(const std::string& filename) { struct stat file_info; @@ -107,7 +108,6 @@ bool Exists(const std::string& filename) { return (result == 0); } -// Returns true if filename is a directory bool IsDirectory(const std::string& filename) { struct stat file_info; @@ -132,8 +132,6 @@ bool IsDirectory(const std::string& filename) { return S_ISDIR(file_info.st_mode); } -// Deletes a given filename, return true on success -// Doesn't supports deleting a directory bool Delete(const std::string& filename) { LOG_TRACE(Common_Filesystem, "file {}", filename); @@ -165,7 +163,6 @@ bool Delete(const std::string& filename) { return true; } -// Returns true if successful, or path already exists. bool CreateDir(const std::string& path) { LOG_TRACE(Common_Filesystem, "directory {}", path); #ifdef _WIN32 @@ -194,7 +191,6 @@ bool CreateDir(const std::string& path) { #endif } -// Creates the full path of fullPath returns true on success bool CreateFullPath(const std::string& fullPath) { int panicCounter = 100; LOG_TRACE(Common_Filesystem, "path {}", fullPath); @@ -230,7 +226,6 @@ bool CreateFullPath(const std::string& fullPath) { } } -// Deletes a directory filename, returns true on success bool DeleteDir(const std::string& filename) { LOG_TRACE(Common_Filesystem, "directory {}", filename); @@ -252,7 +247,6 @@ bool DeleteDir(const std::string& filename) { return false; } -// renames file srcFilename to destFilename, returns true on success bool Rename(const std::string& srcFilename, const std::string& destFilename) { LOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename); #ifdef _WIN32 @@ -268,7 +262,6 @@ bool Rename(const std::string& srcFilename, const std::string& destFilename) { return false; } -// copies file srcFilename to destFilename, returns true on success bool Copy(const std::string& srcFilename, const std::string& destFilename) { LOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename); #ifdef _WIN32 @@ -324,7 +317,6 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) { #endif } -// Returns the size of filename (64bit) u64 GetSize(const std::string& filename) { if (!Exists(filename)) { LOG_ERROR(Common_Filesystem, "failed {}: No such file", filename); @@ -351,7 +343,6 @@ u64 GetSize(const std::string& filename) { return 0; } -// Overloaded GetSize, accepts file descriptor u64 GetSize(const int fd) { struct stat buf; if (fstat(fd, &buf) != 0) { @@ -361,7 +352,6 @@ u64 GetSize(const int fd) { return buf.st_size; } -// Overloaded GetSize, accepts FILE* u64 GetSize(FILE* f) { // can't use off_t here because it can be 32-bit u64 pos = ftello(f); @@ -377,7 +367,6 @@ u64 GetSize(FILE* f) { return size; } -// creates an empty file filename, returns true on success bool CreateEmptyFile(const std::string& filename) { LOG_TRACE(Common_Filesystem, "{}", filename); @@ -502,7 +491,6 @@ bool DeleteDirRecursively(const std::string& directory, unsigned int recursion) return true; } -// Create directory and copy contents (does not overwrite existing files) void CopyDir(const std::string& source_path, const std::string& dest_path) { #ifndef _WIN32 if (source_path == dest_path) @@ -539,8 +527,7 @@ void CopyDir(const std::string& source_path, const std::string& dest_path) { #endif } -// Returns the current directory -std::string GetCurrentDir() { +std::optional<std::string> GetCurrentDir() { // Get the current working directory (getcwd uses malloc) #ifdef _WIN32 wchar_t* dir; @@ -550,7 +537,7 @@ std::string GetCurrentDir() { if (!(dir = getcwd(nullptr, 0))) { #endif LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: {}", GetLastErrorMsg()); - return nullptr; + return {}; } #ifdef _WIN32 std::string strDir = Common::UTF16ToUTF8(dir); @@ -561,7 +548,6 @@ std::string GetCurrentDir() { return strDir; } -// Sets the current directory to the given directory bool SetCurrentDir(const std::string& directory) { #ifdef _WIN32 return _wchdir(Common::UTF8ToUTF16W(directory).c_str()) == 0; @@ -673,8 +659,6 @@ std::string GetSysDirectory() { return sysDir; } -// Returns a string with a yuzu data dir or file in the user's home -// directory. To be used in "multi-user" mode (that is, installed). const std::string& GetUserPath(UserPath path, const std::string& new_path) { static std::unordered_map<UserPath, std::string> paths; auto& user_path = paths[UserPath::UserDir]; @@ -762,11 +746,11 @@ std::string GetNANDRegistrationDir(bool system) { return GetUserPath(UserPath::NANDDir) + "user/Contents/registered/"; } -std::size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) { - return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size()); +std::size_t WriteStringToFile(bool text_file, const std::string& filename, std::string_view str) { + return IOFile(filename, text_file ? "w" : "wb").WriteString(str); } -std::size_t ReadFileToString(bool text_file, const char* filename, std::string& str) { +std::size_t ReadFileToString(bool text_file, const std::string& filename, std::string& str) { IOFile file(filename, text_file ? "r" : "rb"); if (!file.IsOpen()) @@ -776,13 +760,6 @@ std::size_t ReadFileToString(bool text_file, const char* filename, std::string& return file.ReadArray(&str[0], str.size()); } -/** - * Splits the filename into 8.3 format - * Loosely implemented following https://en.wikipedia.org/wiki/8.3_filename - * @param filename The normal filename to use - * @param short_name A 9-char array in which the short name will be written - * @param extension A 4-char array in which the extension will be written - */ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name, std::array<char, 4>& extension) { const std::string forbidden_characters = ".\"/\\[]:;=, "; diff --git a/src/common/file_util.h b/src/common/file_util.h index 38cc7f059..cde7ddf2d 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h @@ -9,6 +9,7 @@ #include <fstream> #include <functional> #include <limits> +#include <optional> #include <string> #include <string_view> #include <type_traits> @@ -118,7 +119,7 @@ u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry, bool DeleteDirRecursively(const std::string& directory, unsigned int recursion = 256); // Returns the current directory -std::string GetCurrentDir(); +std::optional<std::string> GetCurrentDir(); // Create directory and copy contents (does not overwrite existing files) void CopyDir(const std::string& source_path, const std::string& dest_path); @@ -146,9 +147,9 @@ const std::string& GetExeDirectory(); std::string AppDataRoamingDirectory(); #endif -std::size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename); +std::size_t WriteStringToFile(bool text_file, const std::string& filename, std::string_view str); -std::size_t ReadFileToString(bool text_file, const char* filename, std::string& str); +std::size_t ReadFileToString(bool text_file, const std::string& filename, std::string& str); /** * Splits the filename into 8.3 format @@ -257,8 +258,8 @@ public: return WriteArray(&object, 1); } - std::size_t WriteString(const std::string& str) { - return WriteArray(str.c_str(), str.length()); + std::size_t WriteString(std::string_view str) { + return WriteArray(str.data(), str.length()); } bool IsOpen() const { @@ -286,8 +287,8 @@ private: template <typename T> void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) { #ifdef _MSC_VER - fstream.open(Common::UTF8ToUTF16W(filename).c_str(), openmode); + fstream.open(Common::UTF8ToUTF16W(filename), openmode); #else - fstream.open(filename.c_str(), openmode); + fstream.open(filename, openmode); #endif } diff --git a/src/core/core_timing_util.cpp b/src/core/core_timing_util.cpp index 7942f30d6..c0f08cddb 100644 --- a/src/core/core_timing_util.cpp +++ b/src/core/core_timing_util.cpp @@ -14,11 +14,11 @@ namespace Core::Timing { constexpr u64 MAX_VALUE_TO_MULTIPLY = std::numeric_limits<s64>::max() / BASE_CLOCK_RATE; s64 usToCycles(s64 us) { - if (us / 1000000 > MAX_VALUE_TO_MULTIPLY) { + if (static_cast<u64>(us / 1000000) > MAX_VALUE_TO_MULTIPLY) { LOG_ERROR(Core_Timing, "Integer overflow, use max value"); return std::numeric_limits<s64>::max(); } - if (us > MAX_VALUE_TO_MULTIPLY) { + if (static_cast<u64>(us) > MAX_VALUE_TO_MULTIPLY) { LOG_DEBUG(Core_Timing, "Time very big, do rounding"); return BASE_CLOCK_RATE * (us / 1000000); } @@ -38,11 +38,11 @@ s64 usToCycles(u64 us) { } s64 nsToCycles(s64 ns) { - if (ns / 1000000000 > MAX_VALUE_TO_MULTIPLY) { + if (static_cast<u64>(ns / 1000000000) > MAX_VALUE_TO_MULTIPLY) { LOG_ERROR(Core_Timing, "Integer overflow, use max value"); return std::numeric_limits<s64>::max(); } - if (ns > MAX_VALUE_TO_MULTIPLY) { + if (static_cast<u64>(ns) > MAX_VALUE_TO_MULTIPLY) { LOG_DEBUG(Core_Timing, "Time very big, do rounding"); return BASE_CLOCK_RATE * (ns / 1000000000); } diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index e2c290dc1..4a9912641 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -169,8 +169,7 @@ private: * For the request to be honored, EmuWindow implementations will usually reimplement this * function. */ - virtual void OnMinimalClientAreaChangeRequest( - const std::pair<unsigned, unsigned>& minimal_size) { + virtual void OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned>) { // By default, ignore this request and do nothing. } diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 42d9dd844..f3da525d6 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -43,7 +43,7 @@ void SessionRequestHandler::ClientDisconnected(const SharedPtr<ServerSession>& s } SharedPtr<WritableEvent> HLERequestContext::SleepClientThread( - SharedPtr<Thread> thread, const std::string& reason, u64 timeout, WakeupCallback&& callback, + const std::string& reason, u64 timeout, WakeupCallback&& callback, SharedPtr<WritableEvent> writable_event) { // Put the client thread to sleep until the wait event is signaled or the timeout expires. thread->SetWakeupCallback([context = *this, callback]( @@ -76,8 +76,9 @@ SharedPtr<WritableEvent> HLERequestContext::SleepClientThread( return writable_event; } -HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_session) - : server_session(std::move(server_session)) { +HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_session, + SharedPtr<Thread> thread) + : server_session(std::move(server_session)), thread(std::move(thread)) { cmd_buf[0] = 0; } diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 2bdd9f02c..ccf5e56aa 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -97,7 +97,7 @@ protected: */ class HLERequestContext { public: - explicit HLERequestContext(SharedPtr<ServerSession> session); + explicit HLERequestContext(SharedPtr<ServerSession> session, SharedPtr<Thread> thread); ~HLERequestContext(); /// Returns a pointer to the IPC command buffer for this request. @@ -119,7 +119,6 @@ public: /** * Puts the specified guest thread to sleep until the returned event is signaled or until the * specified timeout expires. - * @param thread Thread to be put to sleep. * @param reason Reason for pausing the thread, to be used for debugging purposes. * @param timeout Timeout in nanoseconds after which the thread will be awoken and the callback * invoked with a Timeout reason. @@ -130,8 +129,8 @@ public: * created. * @returns Event that when signaled will resume the thread and call the callback function. */ - SharedPtr<WritableEvent> SleepClientThread(SharedPtr<Thread> thread, const std::string& reason, - u64 timeout, WakeupCallback&& callback, + SharedPtr<WritableEvent> SleepClientThread(const std::string& reason, u64 timeout, + WakeupCallback&& callback, SharedPtr<WritableEvent> writable_event = nullptr); /// Populates this context with data from the requesting process/thread. @@ -268,6 +267,7 @@ private: std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; SharedPtr<Kernel::ServerSession> server_session; + SharedPtr<Thread> thread; // TODO(yuriks): Check common usage of this and optimize size accordingly boost::container::small_vector<SharedPtr<Object>, 8> move_objects; boost::container::small_vector<SharedPtr<Object>, 8> copy_objects; diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 696a82cd9..30b2bfb5a 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -130,7 +130,7 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) { // The ServerSession received a sync request, this means that there's new data available // from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or // similar. - Kernel::HLERequestContext context(this); + Kernel::HLERequestContext context(this, thread); u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress()); context.PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf); diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index bd4e38461..d3e97776b 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp @@ -9,7 +9,6 @@ #include "core/file_sys/content_archive.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/nca_metadata.h" -#include "core/file_sys/partition_filesystem.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" #include "core/hle/ipc_helpers.h" @@ -18,7 +17,6 @@ #include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/writable_event.h" #include "core/hle/service/aoc/aoc_u.h" -#include "core/hle/service/filesystem/filesystem.h" #include "core/loader/loader.h" #include "core/settings.h" @@ -75,7 +73,15 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs AOC_U::~AOC_U() = default; void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_AOC, "called"); + struct Parameters { + u64 process_id; + }; + static_assert(sizeof(Parameters) == 8); + + IPC::RequestParser rp{ctx}; + const auto params = rp.PopRaw<Parameters>(); + + LOG_DEBUG(Service_AOC, "called. process_id={}", params.process_id); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); @@ -94,23 +100,32 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) { } void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { + struct Parameters { + u32 offset; + u32 count; + u64 process_id; + }; + static_assert(sizeof(Parameters) == 16); + IPC::RequestParser rp{ctx}; + const auto [offset, count, process_id] = rp.PopRaw<Parameters>(); - const auto offset = rp.PopRaw<u32>(); - auto count = rp.PopRaw<u32>(); - LOG_DEBUG(Service_AOC, "called with offset={}, count={}", offset, count); + LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count, + process_id); const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); std::vector<u32> out; - for (size_t i = 0; i < add_on_content.size(); ++i) { - if ((add_on_content[i] & DLC_BASE_TITLE_ID_MASK) == current) - out.push_back(static_cast<u32>(add_on_content[i] & 0x7FF)); - } - const auto& disabled = Settings::values.disabled_addons[current]; - if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) - out = {}; + if (std::find(disabled.begin(), disabled.end(), "DLC") == disabled.end()) { + for (u64 content_id : add_on_content) { + if ((content_id & DLC_BASE_TITLE_ID_MASK) != current) { + continue; + } + + out.push_back(static_cast<u32>(content_id & 0x7FF)); + } + } if (out.size() < offset) { IPC::ResponseBuilder rb{ctx, 2}; @@ -119,22 +134,31 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { return; } - count = static_cast<u32>(std::min<size_t>(out.size() - offset, count)); + const auto out_count = static_cast<u32>(std::min<size_t>(out.size() - offset, count)); std::rotate(out.begin(), out.begin() + offset, out.end()); - out.resize(count); + out.resize(out_count); ctx.WriteBuffer(out); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push(count); + rb.Push(out_count); } void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_AOC, "called"); + struct Parameters { + u64 process_id; + }; + static_assert(sizeof(Parameters) == 8); + + IPC::RequestParser rp{ctx}; + const auto params = rp.PopRaw<Parameters>(); + + LOG_DEBUG(Service_AOC, "called. process_id={}", params.process_id); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); + const auto title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); FileSys::PatchManager pm{title_id}; @@ -148,10 +172,17 @@ void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) { } void AOC_U::PrepareAddOnContent(Kernel::HLERequestContext& ctx) { + struct Parameters { + s32 addon_index; + u64 process_id; + }; + static_assert(sizeof(Parameters) == 16); + IPC::RequestParser rp{ctx}; + const auto [addon_index, process_id] = rp.PopRaw<Parameters>(); - const auto aoc_id = rp.PopRaw<u32>(); - LOG_WARNING(Service_AOC, "(STUBBED) called with aoc_id={:08X}", aoc_id); + LOG_WARNING(Service_AOC, "(STUBBED) called with addon_index={}, process_id={}", addon_index, + process_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 4e17249a9..f1fa6ccd1 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -556,7 +556,7 @@ private: } else { // Wait the current thread until a buffer becomes available ctx.SleepClientThread( - Kernel::GetCurrentThread(), "IHOSBinderDriver::DequeueBuffer", -1, + "IHOSBinderDriver::DequeueBuffer", -1, [=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx, Kernel::ThreadWakeupReason reason) { // Repeat TransactParcel DequeueBuffer when a buffer is available diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 8592b1f44..62c090353 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -39,7 +39,7 @@ std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data, const std::vector<u8> uncompressed_data = Common::Compression::DecompressDataLZ4(compressed_data, header.size); - ASSERT_MSG(uncompressed_data.size() == static_cast<int>(header.size), "{} != {}", header.size, + ASSERT_MSG(uncompressed_data.size() == header.size, "{} != {}", header.size, uncompressed_data.size()); return uncompressed_data; diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 7bbc556da..e83f25fa1 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -530,6 +530,11 @@ union Instruction { BitField<48, 16, u64> opcode; union { + BitField<8, 8, Register> gpr; + BitField<20, 24, s64> offset; + } gmem; + + union { BitField<20, 16, u64> imm20_16; BitField<20, 19, u64> imm20_19; BitField<20, 32, s64> imm20_32; @@ -812,13 +817,11 @@ union Instruction { union { BitField<48, 3, UniformType> type; BitField<46, 2, u64> cache_mode; - BitField<20, 24, s64> immediate_offset; } ldg; union { BitField<48, 3, UniformType> type; BitField<46, 2, u64> cache_mode; - BitField<20, 24, s64> immediate_offset; } stg; union { @@ -828,6 +831,11 @@ union Instruction { } al2p; union { + BitField<53, 3, UniformType> type; + BitField<52, 1, u64> extended; + } generic; + + union { BitField<0, 3, u64> pred0; BitField<3, 3, u64> pred3; BitField<7, 1, u64> abs_a; @@ -1387,10 +1395,12 @@ public: LD_L, LD_S, LD_C, + LD, // Load from generic memory + LDG, // Load from global memory ST_A, ST_L, ST_S, - LDG, // Load from global memory + ST, // Store in generic memory STG, // Store in global memory AL2P, // Transforms attribute memory into physical memory TEX, @@ -1658,10 +1668,12 @@ private: INST("1110111101001---", Id::LD_S, Type::Memory, "LD_S"), INST("1110111101000---", Id::LD_L, Type::Memory, "LD_L"), INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"), + INST("100-------------", Id::LD, Type::Memory, "LD"), + INST("1110111011010---", Id::LDG, Type::Memory, "LDG"), INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"), INST("1110111101011---", Id::ST_S, Type::Memory, "ST_S"), INST("1110111101010---", Id::ST_L, Type::Memory, "ST_L"), - INST("1110111011010---", Id::LDG, Type::Memory, "LDG"), + INST("101-------------", Id::ST, Type::Memory, "ST"), INST("1110111011011---", Id::STG, Type::Memory, "STG"), INST("1110111110100---", Id::AL2P, Type::Memory, "AL2P"), INST("110000----111---", Id::TEX, Type::Texture, "TEX"), diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index 38497678a..1d1581f49 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp @@ -35,6 +35,7 @@ Device::Device(std::nullptr_t) { bool Device::TestVariableAoffi() { const GLchar* AOFFI_TEST = R"(#version 430 core +// This is a unit test, please ignore me on apitrace bug reports. uniform sampler2D tex; uniform ivec2 variable_offset; void main() { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index dbd8049f5..f9b6dfeea 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -98,9 +98,11 @@ struct FramebufferCacheKey { } }; -RasterizerOpenGL::RasterizerOpenGL(Core::System& system, ScreenInfo& info) - : res_cache{*this}, shader_cache{*this, system, device}, global_cache{*this}, system{system}, - screen_info{info}, buffer_cache(*this, STREAM_BUFFER_SIZE) { +RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window, + ScreenInfo& info) + : res_cache{*this}, shader_cache{*this, system, emu_window, device}, + global_cache{*this}, system{system}, screen_info{info}, + buffer_cache(*this, STREAM_BUFFER_SIZE) { OpenGLState::ApplyDefaultState(); shader_program_manager = std::make_unique<GLShader::ProgramManager>(); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 71b9c5ead..d78094138 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -48,7 +48,8 @@ struct FramebufferCacheKey; class RasterizerOpenGL : public VideoCore::RasterizerInterface { public: - explicit RasterizerOpenGL(Core::System& system, ScreenInfo& info); + explicit RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window, + ScreenInfo& info); ~RasterizerOpenGL() override; void DrawArrays() override; diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index f700dc89a..d66252224 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -2,10 +2,14 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <mutex> +#include <thread> #include <boost/functional/hash.hpp> #include "common/assert.h" #include "common/hash.h" +#include "common/scope_exit.h" #include "core/core.h" +#include "core/frontend/emu_window.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/memory_manager.h" #include "video_core/renderer_opengl/gl_rasterizer.h" @@ -166,7 +170,8 @@ GLShader::ProgramResult CreateProgram(const Device& device, Maxwell::ShaderProgr CachedProgram SpecializeShader(const std::string& code, const GLShader::ShaderEntries& entries, Maxwell::ShaderProgram program_type, BaseBindings base_bindings, GLenum primitive_mode, bool hint_retrievable = false) { - std::string source = "#version 430 core\n"; + std::string source = "#version 430 core\n" + "#extension GL_ARB_separate_shader_objects : enable\n\n"; source += fmt::format("#define EMULATION_UBO_BINDING {}\n", base_bindings.cbuf++); for (const auto& cbuf : entries.const_buffers) { @@ -344,8 +349,8 @@ ShaderDiskCacheUsage CachedShader::GetUsage(GLenum primitive_mode, } ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system, - const Device& device) - : RasterizerCache{rasterizer}, device{device}, disk_cache{system} {} + Core::Frontend::EmuWindow& emu_window, const Device& device) + : RasterizerCache{rasterizer}, emu_window{emu_window}, device{device}, disk_cache{system} {} void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading, const VideoCore::DiskResourceLoadCallback& callback) { @@ -353,62 +358,107 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading, if (!transferable) { return; } - const auto [raws, usages] = *transferable; + const auto [raws, shader_usages] = *transferable; auto [decompiled, dumps] = disk_cache.LoadPrecompiled(); const auto supported_formats{GetSupportedFormats()}; - const auto unspecialized{ + const auto unspecialized_shaders{ GenerateUnspecializedShaders(stop_loading, callback, raws, decompiled)}; - if (stop_loading) + if (stop_loading) { return; + } // Track if precompiled cache was altered during loading to know if we have to serialize the // virtual precompiled cache file back to the hard drive bool precompiled_cache_altered = false; - // Build shaders - if (callback) - callback(VideoCore::LoadCallbackStage::Build, 0, usages.size()); - for (std::size_t i = 0; i < usages.size(); ++i) { - if (stop_loading) - return; + // Inform the frontend about shader build initialization + if (callback) { + callback(VideoCore::LoadCallbackStage::Build, 0, shader_usages.size()); + } - const auto& usage{usages[i]}; - LOG_INFO(Render_OpenGL, "Building shader {:016x} ({} of {})", usage.unique_identifier, - i + 1, usages.size()); + std::mutex mutex; + std::size_t built_shaders = 0; // It doesn't have be atomic since it's used behind a mutex + std::atomic_bool compilation_failed = false; - const auto& unspec{unspecialized.at(usage.unique_identifier)}; - const auto dump_it = dumps.find(usage); + const auto Worker = [&](Core::Frontend::GraphicsContext* context, std::size_t begin, + std::size_t end, const std::vector<ShaderDiskCacheUsage>& shader_usages, + const ShaderDumpsMap& dumps) { + context->MakeCurrent(); + SCOPE_EXIT({ return context->DoneCurrent(); }); - CachedProgram shader; - if (dump_it != dumps.end()) { - // If the shader is dumped, attempt to load it with - shader = GeneratePrecompiledProgram(dump_it->second, supported_formats); + for (std::size_t i = begin; i < end; ++i) { + if (stop_loading || compilation_failed) { + return; + } + const auto& usage{shader_usages[i]}; + LOG_INFO(Render_OpenGL, "Building shader {:016x} (index {} of {})", + usage.unique_identifier, i, shader_usages.size()); + + const auto& unspecialized{unspecialized_shaders.at(usage.unique_identifier)}; + const auto dump{dumps.find(usage)}; + + CachedProgram shader; + if (dump != dumps.end()) { + // If the shader is dumped, attempt to load it with + shader = GeneratePrecompiledProgram(dump->second, supported_formats); + if (!shader) { + compilation_failed = true; + return; + } + } if (!shader) { - // Invalidate the precompiled cache if a shader dumped shader was rejected - disk_cache.InvalidatePrecompiled(); - precompiled_cache_altered = true; - dumps.clear(); + shader = SpecializeShader(unspecialized.code, unspecialized.entries, + unspecialized.program_type, usage.bindings, + usage.primitive, true); } + + std::scoped_lock lock(mutex); + if (callback) { + callback(VideoCore::LoadCallbackStage::Build, ++built_shaders, + shader_usages.size()); + } + + precompiled_programs.emplace(usage, std::move(shader)); } - if (!shader) { - shader = SpecializeShader(unspec.code, unspec.entries, unspec.program_type, - usage.bindings, usage.primitive, true); - } - precompiled_programs.insert({usage, std::move(shader)}); + }; + + const auto num_workers{static_cast<std::size_t>(std::thread::hardware_concurrency() + 1)}; + const std::size_t bucket_size{shader_usages.size() / num_workers}; + std::vector<std::unique_ptr<Core::Frontend::GraphicsContext>> contexts(num_workers); + std::vector<std::thread> threads(num_workers); + for (std::size_t i = 0; i < num_workers; ++i) { + const bool is_last_worker = i + 1 == num_workers; + const std::size_t start{bucket_size * i}; + const std::size_t end{is_last_worker ? shader_usages.size() : start + bucket_size}; + + // On some platforms the shared context has to be created from the GUI thread + contexts[i] = emu_window.CreateSharedContext(); + threads[i] = std::thread(Worker, contexts[i].get(), start, end, shader_usages, dumps); + } + for (auto& thread : threads) { + thread.join(); + } - if (callback) - callback(VideoCore::LoadCallbackStage::Build, i + 1, usages.size()); + if (compilation_failed) { + // Invalidate the precompiled cache if a shader dumped shader was rejected + disk_cache.InvalidatePrecompiled(); + dumps.clear(); + precompiled_cache_altered = true; + return; + } + if (stop_loading) { + return; } // TODO(Rodrigo): Do state tracking for transferable shaders and do a dummy draw before // precompiling them - for (std::size_t i = 0; i < usages.size(); ++i) { - const auto& usage{usages[i]}; + for (std::size_t i = 0; i < shader_usages.size(); ++i) { + const auto& usage{shader_usages[i]}; if (dumps.find(usage) == dumps.end()) { - const auto& program = precompiled_programs.at(usage); + const auto& program{precompiled_programs.at(usage)}; disk_cache.SaveDump(usage, program->handle); precompiled_cache_altered = true; } diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h index 31b979987..64e5a5594 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_cache.h @@ -22,7 +22,11 @@ namespace Core { class System; -} // namespace Core +} + +namespace Core::Frontend { +class EmuWindow; +} namespace OpenGL { @@ -111,7 +115,7 @@ private: class ShaderCacheOpenGL final : public RasterizerCache<Shader> { public: explicit ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system, - const Device& device); + Core::Frontend::EmuWindow& emu_window, const Device& device); /// Loads disk cache for the current game void LoadDiskCache(const std::atomic_bool& stop_loading, @@ -133,13 +137,13 @@ private: CachedProgram GeneratePrecompiledProgram(const ShaderDiskCacheDump& dump, const std::set<GLenum>& supported_formats); + Core::Frontend::EmuWindow& emu_window; const Device& device; - - std::array<Shader, Maxwell::MaxShaderProgram> last_shaders; - ShaderDiskCacheOpenGL disk_cache; + PrecompiledShaders precompiled_shaders; PrecompiledPrograms precompiled_programs; + std::array<Shader, Maxwell::MaxShaderProgram> last_shaders; }; } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index d437afad1..e9f8d40db 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -59,15 +59,14 @@ public: shader_source += text; } - void AddLine(std::string_view text) { - AddExpression(text); - AddNewLine(); - } - - void AddLine(char character) { - DEBUG_ASSERT(scope >= 0); - AppendIndentation(); - shader_source += character; + // Forwards all arguments directly to libfmt. + // Note that all formatting requirements for fmt must be + // obeyed when using this function. (e.g. {{ must be used + // printing the character '{' is desirable. Ditto for }} and '}', + // etc). + template <typename... Args> + void AddLine(std::string_view text, Args&&... args) { + AddExpression(fmt::format(text, std::forward<Args>(args)...)); AddNewLine(); } @@ -77,9 +76,7 @@ public: } std::string GenerateTemporary() { - std::string temporary = "tmp"; - temporary += std::to_string(temporary_index++); - return temporary; + return fmt::format("tmp{}", temporary_index++); } std::string GetResult() { @@ -167,41 +164,41 @@ public: DeclareSamplers(); DeclarePhysicalAttributeReader(); - code.AddLine("void execute_" + suffix + "() {"); + code.AddLine("void execute_{}() {{", suffix); ++code.scope; // VM's program counter const auto first_address = ir.GetBasicBlocks().begin()->first; - code.AddLine("uint jmp_to = " + std::to_string(first_address) + "u;"); + code.AddLine("uint jmp_to = {}u;", first_address); // TODO(Subv): Figure out the actual depth of the flow stack, for now it seems // unlikely that shaders will use 20 nested SSYs and PBKs. constexpr u32 FLOW_STACK_SIZE = 20; - code.AddLine(fmt::format("uint flow_stack[{}];", FLOW_STACK_SIZE)); + code.AddLine("uint flow_stack[{}];", FLOW_STACK_SIZE); code.AddLine("uint flow_stack_top = 0u;"); - code.AddLine("while (true) {"); + code.AddLine("while (true) {{"); ++code.scope; - code.AddLine("switch (jmp_to) {"); + code.AddLine("switch (jmp_to) {{"); for (const auto& pair : ir.GetBasicBlocks()) { const auto [address, bb] = pair; - code.AddLine(fmt::format("case 0x{:x}u: {{", address)); + code.AddLine("case 0x{:x}u: {{", address); ++code.scope; VisitBlock(bb); --code.scope; - code.AddLine('}'); + code.AddLine("}}"); } code.AddLine("default: return;"); - code.AddLine('}'); + code.AddLine("}}"); for (std::size_t i = 0; i < 2; ++i) { --code.scope; - code.AddLine('}'); + code.AddLine("}}"); } } @@ -241,12 +238,13 @@ private: } void DeclareGeometry() { - if (stage != ShaderStage::Geometry) + if (stage != ShaderStage::Geometry) { return; + } const auto topology = GetTopologyName(header.common3.output_topology); - const auto max_vertices = std::to_string(header.common4.max_output_vertices); - code.AddLine("layout (" + topology + ", max_vertices = " + max_vertices + ") out;"); + const auto max_vertices = header.common4.max_output_vertices.Value(); + code.AddLine("layout ({}, max_vertices = {}) out;", topology, max_vertices); code.AddNewLine(); DeclareVertexRedeclarations(); @@ -255,7 +253,7 @@ private: void DeclareVertexRedeclarations() { bool clip_distances_declared = false; - code.AddLine("out gl_PerVertex {"); + code.AddLine("out gl_PerVertex {{"); ++code.scope; code.AddLine("vec4 gl_Position;"); @@ -271,40 +269,42 @@ private: } --code.scope; - code.AddLine("};"); + code.AddLine("}};"); code.AddNewLine(); } void DeclareRegisters() { const auto& registers = ir.GetRegisters(); for (const u32 gpr : registers) { - code.AddLine("float " + GetRegister(gpr) + " = 0;"); + code.AddLine("float {} = 0;", GetRegister(gpr)); } - if (!registers.empty()) + if (!registers.empty()) { code.AddNewLine(); + } } void DeclarePredicates() { const auto& predicates = ir.GetPredicates(); for (const auto pred : predicates) { - code.AddLine("bool " + GetPredicate(pred) + " = false;"); + code.AddLine("bool {} = false;", GetPredicate(pred)); } - if (!predicates.empty()) + if (!predicates.empty()) { code.AddNewLine(); + } } void DeclareLocalMemory() { if (const u64 local_memory_size = header.GetLocalMemorySize(); local_memory_size > 0) { const auto element_count = Common::AlignUp(local_memory_size, 4) / 4; - code.AddLine("float " + GetLocalMemory() + '[' + std::to_string(element_count) + "];"); + code.AddLine("float {}[{}];", GetLocalMemory(), element_count); code.AddNewLine(); } } void DeclareInternalFlags() { for (u32 flag = 0; flag < static_cast<u32>(InternalFlag::Amount); flag++) { - const InternalFlag flag_code = static_cast<InternalFlag>(flag); - code.AddLine("bool " + GetInternalFlag(flag_code) + " = false;"); + const auto flag_code = static_cast<InternalFlag>(flag); + code.AddLine("bool {} = false;", GetInternalFlag(flag_code)); } code.AddNewLine(); } @@ -343,8 +343,9 @@ private: DeclareInputAttribute(index, false); } } - if (!attributes.empty()) + if (!attributes.empty()) { code.AddNewLine(); + } } void DeclareInputAttribute(Attribute::Index index, bool skip_unused) { @@ -370,8 +371,7 @@ private: location += GENERIC_VARYING_START_LOCATION; } - code.AddLine("layout (location = " + std::to_string(location) + ") " + suffix + "in vec4 " + - name + ';'); + code.AddLine("layout (location = {}) {} in vec4 {};", location, suffix, name); } void DeclareOutputAttributes() { @@ -389,23 +389,23 @@ private: DeclareOutputAttribute(index); } } - if (!attributes.empty()) + if (!attributes.empty()) { code.AddNewLine(); + } } void DeclareOutputAttribute(Attribute::Index index) { const u32 location{GetGenericAttributeIndex(index) + GENERIC_VARYING_START_LOCATION}; - code.AddLine("layout (location = " + std::to_string(location) + ") out vec4 " + - GetOutputAttribute(index) + ';'); + code.AddLine("layout (location = {}) out vec4 {};", location, GetOutputAttribute(index)); } void DeclareConstantBuffers() { for (const auto& entry : ir.GetConstantBuffers()) { const auto [index, size] = entry; - code.AddLine("layout (std140, binding = CBUF_BINDING_" + std::to_string(index) + - ") uniform " + GetConstBufferBlock(index) + " {"); - code.AddLine(" vec4 " + GetConstBuffer(index) + "[MAX_CONSTBUFFER_ELEMENTS];"); - code.AddLine("};"); + code.AddLine("layout (std140, binding = CBUF_BINDING_{}) uniform {} {{", index, + GetConstBufferBlock(index)); + code.AddLine(" vec4 {}[MAX_CONSTBUFFER_ELEMENTS];", GetConstBuffer(index)); + code.AddLine("}};"); code.AddNewLine(); } } @@ -417,17 +417,16 @@ private: // Since we don't know how the shader will use the shader, hint the driver to disable as // much optimizations as possible std::string qualifier = "coherent volatile"; - if (usage.is_read && !usage.is_written) + if (usage.is_read && !usage.is_written) { qualifier += " readonly"; - else if (usage.is_written && !usage.is_read) + } else if (usage.is_written && !usage.is_read) { qualifier += " writeonly"; + } - const std::string binding = - fmt::format("GMEM_BINDING_{}_{}", base.cbuf_index, base.cbuf_offset); - code.AddLine("layout (std430, binding = " + binding + ") " + qualifier + " buffer " + - GetGlobalMemoryBlock(base) + " {"); - code.AddLine(" float " + GetGlobalMemory(base) + "[];"); - code.AddLine("};"); + code.AddLine("layout (std430, binding = GMEM_BINDING_{}_{}) {} buffer {} {{", + base.cbuf_index, base.cbuf_offset, qualifier, GetGlobalMemoryBlock(base)); + code.AddLine(" float {}[];", GetGlobalMemory(base)); + code.AddLine("}};"); code.AddNewLine(); } } @@ -435,7 +434,7 @@ private: void DeclareSamplers() { const auto& samplers = ir.GetSamplers(); for (const auto& sampler : samplers) { - std::string sampler_type = [&]() { + std::string sampler_type = [&sampler] { switch (sampler.GetType()) { case Tegra::Shader::TextureType::Texture1D: return "sampler1D"; @@ -450,25 +449,28 @@ private: return "sampler2D"; } }(); - if (sampler.IsArray()) + if (sampler.IsArray()) { sampler_type += "Array"; - if (sampler.IsShadow()) + } + if (sampler.IsShadow()) { sampler_type += "Shadow"; + } - code.AddLine("layout (binding = SAMPLER_BINDING_" + std::to_string(sampler.GetIndex()) + - ") uniform " + sampler_type + ' ' + GetSampler(sampler) + ';'); + code.AddLine("layout (binding = SAMPLER_BINDING_{}) uniform {} {};", sampler.GetIndex(), + sampler_type, GetSampler(sampler)); } - if (!samplers.empty()) + if (!samplers.empty()) { code.AddNewLine(); + } } void DeclarePhysicalAttributeReader() { if (!ir.HasPhysicalAttributes()) { return; } - code.AddLine("float readPhysicalAttribute(uint physical_address) {"); + code.AddLine("float readPhysicalAttribute(uint physical_address) {{"); ++code.scope; - code.AddLine("switch (physical_address) {"); + code.AddLine("switch (physical_address) {{"); // Just declare generic attributes for now. const auto num_attributes{static_cast<u32>(GetNumPhysicalInputAttributes())}; @@ -483,15 +485,15 @@ private: const bool declared{stage != ShaderStage::Fragment || header.ps.GetAttributeUse(index) != AttributeUse::Unused}; const std::string value{declared ? ReadAttribute(attribute, element) : "0"}; - code.AddLine(fmt::format("case 0x{:x}: return {};", address, value)); + code.AddLine("case 0x{:x}: return {};", address, value); } } code.AddLine("default: return 0;"); - code.AddLine('}'); + code.AddLine("}}"); --code.scope; - code.AddLine('}'); + code.AddLine("}}"); code.AddNewLine(); } @@ -516,23 +518,26 @@ private: return {}; } return (this->*decompiler)(*operation); + } - } else if (const auto gpr = std::get_if<GprNode>(node)) { + if (const auto gpr = std::get_if<GprNode>(node)) { const u32 index = gpr->GetIndex(); if (index == Register::ZeroIndex) { return "0"; } return GetRegister(index); + } - } else if (const auto immediate = std::get_if<ImmediateNode>(node)) { + if (const auto immediate = std::get_if<ImmediateNode>(node)) { const u32 value = immediate->GetValue(); if (value < 10) { // For eyecandy avoid using hex numbers on single digits return fmt::format("utof({}u)", immediate->GetValue()); } return fmt::format("utof(0x{:x}u)", immediate->GetValue()); + } - } else if (const auto predicate = std::get_if<PredicateNode>(node)) { + if (const auto predicate = std::get_if<PredicateNode>(node)) { const auto value = [&]() -> std::string { switch (const auto index = predicate->GetIndex(); index) { case Tegra::Shader::Pred::UnusedIndex: @@ -544,19 +549,22 @@ private: } }(); if (predicate->IsNegated()) { - return "!(" + value + ')'; + return fmt::format("!({})", value); } return value; + } - } else if (const auto abuf = std::get_if<AbufNode>(node)) { + if (const auto abuf = std::get_if<AbufNode>(node)) { UNIMPLEMENTED_IF_MSG(abuf->IsPhysicalBuffer() && stage == ShaderStage::Geometry, "Physical attributes in geometry shaders are not implemented"); if (abuf->IsPhysicalBuffer()) { - return "readPhysicalAttribute(ftou(" + Visit(abuf->GetPhysicalAddress()) + "))"; + return fmt::format("readPhysicalAttribute(ftou({}))", + Visit(abuf->GetPhysicalAddress())); } return ReadAttribute(abuf->GetIndex(), abuf->GetElement(), abuf->GetBuffer()); + } - } else if (const auto cbuf = std::get_if<CbufNode>(node)) { + if (const auto cbuf = std::get_if<CbufNode>(node)) { const Node offset = cbuf->GetOffset(); if (const auto immediate = std::get_if<ImmediateNode>(offset)) { // Direct access @@ -564,57 +572,63 @@ private: ASSERT_MSG(offset_imm % 4 == 0, "Unaligned cbuf direct access"); return fmt::format("{}[{}][{}]", GetConstBuffer(cbuf->GetIndex()), offset_imm / (4 * 4), (offset_imm / 4) % 4); + } - } else if (std::holds_alternative<OperationNode>(*offset)) { + if (std::holds_alternative<OperationNode>(*offset)) { // Indirect access const std::string final_offset = code.GenerateTemporary(); - code.AddLine("uint " + final_offset + " = (ftou(" + Visit(offset) + ") / 4);"); + code.AddLine("uint {} = (ftou({}) / 4);", final_offset, Visit(offset)); return fmt::format("{}[{} / 4][{} % 4]", GetConstBuffer(cbuf->GetIndex()), final_offset, final_offset); - - } else { - UNREACHABLE_MSG("Unmanaged offset node type"); } - } else if (const auto gmem = std::get_if<GmemNode>(node)) { + UNREACHABLE_MSG("Unmanaged offset node type"); + } + + if (const auto gmem = std::get_if<GmemNode>(node)) { const std::string real = Visit(gmem->GetRealAddress()); const std::string base = Visit(gmem->GetBaseAddress()); - const std::string final_offset = "(ftou(" + real + ") - ftou(" + base + ")) / 4"; + const std::string final_offset = fmt::format("(ftou({}) - ftou({})) / 4", real, base); return fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset); + } - } else if (const auto lmem = std::get_if<LmemNode>(node)) { + if (const auto lmem = std::get_if<LmemNode>(node)) { return fmt::format("{}[ftou({}) / 4]", GetLocalMemory(), Visit(lmem->GetAddress())); + } - } else if (const auto internal_flag = std::get_if<InternalFlagNode>(node)) { + if (const auto internal_flag = std::get_if<InternalFlagNode>(node)) { return GetInternalFlag(internal_flag->GetFlag()); + } - } else if (const auto conditional = std::get_if<ConditionalNode>(node)) { + if (const auto conditional = std::get_if<ConditionalNode>(node)) { // It's invalid to call conditional on nested nodes, use an operation instead - code.AddLine("if (" + Visit(conditional->GetCondition()) + ") {"); + code.AddLine("if ({}) {{", Visit(conditional->GetCondition())); ++code.scope; VisitBlock(conditional->GetCode()); --code.scope; - code.AddLine('}'); + code.AddLine("}}"); return {}; + } - } else if (const auto comment = std::get_if<CommentNode>(node)) { + if (const auto comment = std::get_if<CommentNode>(node)) { return "// " + comment->GetText(); } + UNREACHABLE(); return {}; } std::string ReadAttribute(Attribute::Index attribute, u32 element, Node buffer = {}) { - const auto GeometryPass = [&](std::string name) { + const auto GeometryPass = [&](std::string_view name) { if (stage == ShaderStage::Geometry && buffer) { // TODO(Rodrigo): Guard geometry inputs against out of bound reads. Some games // set an 0x80000000 index for those and the shader fails to build. Find out why // this happens and what's its intent. - return "gs_" + std::move(name) + "[ftou(" + Visit(buffer) + ") % MAX_VERTEX_INPUT]"; + return fmt::format("gs_{}[ftou({}) % MAX_VERTEX_INPUT]", name, Visit(buffer)); } - return name; + return std::string(name); }; switch (attribute) { @@ -677,7 +691,7 @@ private: const std::string precise = stage != ShaderStage::Fragment ? "precise " : ""; const std::string temporary = code.GenerateTemporary(); - code.AddLine(precise + "float " + temporary + " = " + value + ';'); + code.AddLine("{}float {} = {};", precise, temporary, value); return temporary; } @@ -691,7 +705,7 @@ private: } const std::string temporary = code.GenerateTemporary(); - code.AddLine("float " + temporary + " = " + Visit(operand) + ';'); + code.AddLine("float {} = {};", temporary, Visit(operand)); return temporary; } @@ -706,31 +720,32 @@ private: case Type::Float: return value; case Type::Int: - return "ftoi(" + value + ')'; + return fmt::format("ftoi({})", value); case Type::Uint: - return "ftou(" + value + ')'; + return fmt::format("ftou({})", value); case Type::HalfFloat: - return "toHalf2(" + value + ')'; + return fmt::format("toHalf2({})", value); } UNREACHABLE(); return value; } - std::string BitwiseCastResult(std::string value, Type type, bool needs_parenthesis = false) { + std::string BitwiseCastResult(const std::string& value, Type type, + bool needs_parenthesis = false) { switch (type) { case Type::Bool: case Type::Bool2: case Type::Float: if (needs_parenthesis) { - return '(' + value + ')'; + return fmt::format("({})", value); } return value; case Type::Int: - return "itof(" + value + ')'; + return fmt::format("itof({})", value); case Type::Uint: - return "utof(" + value + ')'; + return fmt::format("utof({})", value); case Type::HalfFloat: - return "fromHalf2(" + value + ')'; + return fmt::format("fromHalf2({})", value); } UNREACHABLE(); return value; @@ -738,27 +753,27 @@ private: std::string GenerateUnary(Operation operation, const std::string& func, Type result_type, Type type_a, bool needs_parenthesis = true) { - return ApplyPrecise(operation, - BitwiseCastResult(func + '(' + VisitOperand(operation, 0, type_a) + ')', - result_type, needs_parenthesis)); + const std::string op_str = fmt::format("{}({})", func, VisitOperand(operation, 0, type_a)); + + return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type, needs_parenthesis)); } std::string GenerateBinaryInfix(Operation operation, const std::string& func, Type result_type, Type type_a, Type type_b) { const std::string op_a = VisitOperand(operation, 0, type_a); const std::string op_b = VisitOperand(operation, 1, type_b); + const std::string op_str = fmt::format("({} {} {})", op_a, func, op_b); - return ApplyPrecise( - operation, BitwiseCastResult('(' + op_a + ' ' + func + ' ' + op_b + ')', result_type)); + return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type)); } std::string GenerateBinaryCall(Operation operation, const std::string& func, Type result_type, Type type_a, Type type_b) { const std::string op_a = VisitOperand(operation, 0, type_a); const std::string op_b = VisitOperand(operation, 1, type_b); + const std::string op_str = fmt::format("{}({}, {})", func, op_a, op_b); - return ApplyPrecise(operation, - BitwiseCastResult(func + '(' + op_a + ", " + op_b + ')', result_type)); + return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type)); } std::string GenerateTernary(Operation operation, const std::string& func, Type result_type, @@ -766,10 +781,9 @@ private: const std::string op_a = VisitOperand(operation, 0, type_a); const std::string op_b = VisitOperand(operation, 1, type_b); const std::string op_c = VisitOperand(operation, 2, type_c); + const std::string op_str = fmt::format("{}({}, {}, {})", func, op_a, op_b, op_c); - return ApplyPrecise( - operation, - BitwiseCastResult(func + '(' + op_a + ", " + op_b + ", " + op_c + ')', result_type)); + return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type)); } std::string GenerateQuaternary(Operation operation, const std::string& func, Type result_type, @@ -778,10 +792,9 @@ private: const std::string op_b = VisitOperand(operation, 1, type_b); const std::string op_c = VisitOperand(operation, 2, type_c); const std::string op_d = VisitOperand(operation, 3, type_d); + const std::string op_str = fmt::format("{}({}, {}, {}, {})", func, op_a, op_b, op_c, op_d); - return ApplyPrecise(operation, BitwiseCastResult(func + '(' + op_a + ", " + op_b + ", " + - op_c + ", " + op_d + ')', - result_type)); + return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type)); } std::string GenerateTexture(Operation operation, const std::string& function_suffix, @@ -844,7 +857,7 @@ private: // required to be constant) expr += std::to_string(static_cast<s32>(immediate->GetValue())); } else { - expr += "ftoi(" + Visit(operand) + ')'; + expr += fmt::format("ftoi({})", Visit(operand)); } break; case Type::Float: @@ -877,7 +890,7 @@ private: expr += std::to_string(static_cast<s32>(immediate->GetValue())); } else if (device.HasVariableAoffi()) { // Avoid using variable AOFFI on unsupported devices. - expr += "ftoi(" + Visit(operand) + ')'; + expr += fmt::format("ftoi({})", Visit(operand)); } else { // Insert 0 on devices not supporting variable AOFFI. expr += '0'; @@ -902,7 +915,6 @@ private: return {}; } target = GetRegister(gpr->GetIndex()); - } else if (const auto abuf = std::get_if<AbufNode>(dest)) { UNIMPLEMENTED_IF(abuf->IsPhysicalBuffer()); @@ -913,9 +925,9 @@ private: case Attribute::Index::PointSize: return "gl_PointSize"; case Attribute::Index::ClipDistances0123: - return "gl_ClipDistance[" + std::to_string(abuf->GetElement()) + ']'; + return fmt::format("gl_ClipDistance[{}]", abuf->GetElement()); case Attribute::Index::ClipDistances4567: - return "gl_ClipDistance[" + std::to_string(abuf->GetElement() + 4) + ']'; + return fmt::format("gl_ClipDistance[{}]", abuf->GetElement() + 4); default: if (IsGenericAttribute(attribute)) { return GetOutputAttribute(attribute) + GetSwizzle(abuf->GetElement()); @@ -925,21 +937,18 @@ private: return "0"; } }(); - } else if (const auto lmem = std::get_if<LmemNode>(dest)) { - target = GetLocalMemory() + "[ftou(" + Visit(lmem->GetAddress()) + ") / 4]"; - + target = fmt::format("{}[ftou({}) / 4]", GetLocalMemory(), Visit(lmem->GetAddress())); } else if (const auto gmem = std::get_if<GmemNode>(dest)) { const std::string real = Visit(gmem->GetRealAddress()); const std::string base = Visit(gmem->GetBaseAddress()); - const std::string final_offset = "(ftou(" + real + ") - ftou(" + base + ")) / 4"; + const std::string final_offset = fmt::format("(ftou({}) - ftou({})) / 4", real, base); target = fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset); - } else { UNREACHABLE_MSG("Assign called without a proper target"); } - code.AddLine(target + " = " + Visit(src) + ';'); + code.AddLine("{} = {};", target, Visit(src)); return {}; } @@ -992,8 +1001,9 @@ private: const std::string condition = Visit(operation[0]); const std::string true_case = Visit(operation[1]); const std::string false_case = Visit(operation[2]); - return ApplyPrecise(operation, - '(' + condition + " ? " + true_case + " : " + false_case + ')'); + const std::string op_str = fmt::format("({} ? {} : {})", condition, true_case, false_case); + + return ApplyPrecise(operation, op_str); } std::string FCos(Operation operation) { @@ -1057,9 +1067,9 @@ private: std::string ILogicalShiftRight(Operation operation) { const std::string op_a = VisitOperand(operation, 0, Type::Uint); const std::string op_b = VisitOperand(operation, 1, Type::Uint); + const std::string op_str = fmt::format("int({} >> {})", op_a, op_b); - return ApplyPrecise(operation, - BitwiseCastResult("int(" + op_a + " >> " + op_b + ')', Type::Int)); + return ApplyPrecise(operation, BitwiseCastResult(op_str, Type::Int)); } std::string IArithmeticShiftRight(Operation operation) { @@ -1115,11 +1125,12 @@ private: } std::string HNegate(Operation operation) { - const auto GetNegate = [&](std::size_t index) -> std::string { + const auto GetNegate = [&](std::size_t index) { return VisitOperand(operation, index, Type::Bool) + " ? -1 : 1"; }; - const std::string value = '(' + VisitOperand(operation, 0, Type::HalfFloat) + " * vec2(" + - GetNegate(1) + ", " + GetNegate(2) + "))"; + const std::string value = + fmt::format("({} * vec2({}, {}))", VisitOperand(operation, 0, Type::HalfFloat), + GetNegate(1), GetNegate(2)); return BitwiseCastResult(value, Type::HalfFloat); } @@ -1127,7 +1138,8 @@ private: const std::string value = VisitOperand(operation, 0, Type::HalfFloat); const std::string min = VisitOperand(operation, 1, Type::Float); const std::string max = VisitOperand(operation, 2, Type::Float); - const std::string clamped = "clamp(" + value + ", vec2(" + min + "), vec2(" + max + "))"; + const std::string clamped = fmt::format("clamp({}, vec2({}), vec2({}))", value, min, max); + return ApplyPrecise(operation, BitwiseCastResult(clamped, Type::HalfFloat)); } @@ -1138,34 +1150,35 @@ private: case Tegra::Shader::HalfType::H0_H1: return operand; case Tegra::Shader::HalfType::F32: - return "vec2(fromHalf2(" + operand + "))"; + return fmt::format("vec2(fromHalf2({}))", operand); case Tegra::Shader::HalfType::H0_H0: - return "vec2(" + operand + "[0])"; + return fmt::format("vec2({}[0])", operand); case Tegra::Shader::HalfType::H1_H1: - return "vec2(" + operand + "[1])"; + return fmt::format("vec2({}[1])", operand); } UNREACHABLE(); return "0"; }(); - return "fromHalf2(" + value + ')'; + return fmt::format("fromHalf2({})", value); } std::string HMergeF32(Operation operation) { - return "float(toHalf2(" + Visit(operation[0]) + ")[0])"; + return fmt::format("float(toHalf2({})[0])", Visit(operation[0])); } std::string HMergeH0(Operation operation) { - return "fromHalf2(vec2(toHalf2(" + Visit(operation[1]) + ")[0], toHalf2(" + - Visit(operation[0]) + ")[1]))"; + return fmt::format("fromHalf2(vec2(toHalf2({})[0], toHalf2({})[1]))", Visit(operation[1]), + Visit(operation[0])); } std::string HMergeH1(Operation operation) { - return "fromHalf2(vec2(toHalf2(" + Visit(operation[0]) + ")[0], toHalf2(" + - Visit(operation[1]) + ")[1]))"; + return fmt::format("fromHalf2(vec2(toHalf2({})[0], toHalf2({})[1]))", Visit(operation[0]), + Visit(operation[1])); } std::string HPack2(Operation operation) { - return "utof(packHalf2x16(vec2(" + Visit(operation[0]) + ", " + Visit(operation[1]) + ")))"; + return fmt::format("utof(packHalf2x16(vec2({}, {})))", Visit(operation[0]), + Visit(operation[1])); } template <Type type> @@ -1223,7 +1236,7 @@ private: target = GetInternalFlag(flag->GetFlag()); } - code.AddLine(target + " = " + Visit(src) + ';'); + code.AddLine("{} = {};", target, Visit(src)); return {}; } @@ -1245,7 +1258,7 @@ private: std::string LogicalPick2(Operation operation) { const std::string pair = VisitOperand(operation, 0, Type::Bool2); - return pair + '[' + VisitOperand(operation, 1, Type::Uint) + ']'; + return fmt::format("{}[{}]", pair, VisitOperand(operation, 1, Type::Uint)); } std::string LogicalAll2(Operation operation) { @@ -1257,15 +1270,15 @@ private: } template <bool with_nan> - std::string GenerateHalfComparison(Operation operation, std::string compare_op) { - std::string comparison{GenerateBinaryCall(operation, compare_op, Type::Bool2, - Type::HalfFloat, Type::HalfFloat)}; + std::string GenerateHalfComparison(Operation operation, const std::string& compare_op) { + const std::string comparison{GenerateBinaryCall(operation, compare_op, Type::Bool2, + Type::HalfFloat, Type::HalfFloat)}; if constexpr (!with_nan) { return comparison; } - return "halfFloatNanComparison(" + comparison + ", " + - VisitOperand(operation, 0, Type::HalfFloat) + ", " + - VisitOperand(operation, 1, Type::HalfFloat) + ')'; + return fmt::format("halfFloatNanComparison({}, {}, {})", comparison, + VisitOperand(operation, 0, Type::HalfFloat), + VisitOperand(operation, 1, Type::HalfFloat)); } template <bool with_nan> @@ -1342,12 +1355,12 @@ private: switch (meta->element) { case 0: case 1: - return "itof(int(textureSize(" + sampler + ", " + lod + ')' + - GetSwizzle(meta->element) + "))"; + return fmt::format("itof(int(textureSize({}, {}){}))", sampler, lod, + GetSwizzle(meta->element)); case 2: return "0"; case 3: - return "itof(textureQueryLevels(" + sampler + "))"; + return fmt::format("itof(textureQueryLevels({}))", sampler); } UNREACHABLE(); return "0"; @@ -1358,8 +1371,9 @@ private: ASSERT(meta); if (meta->element < 2) { - return "itof(int((" + GenerateTexture(operation, "QueryLod", {}) + " * vec2(256))" + - GetSwizzle(meta->element) + "))"; + return fmt::format("itof(int(({} * vec2(256)){}))", + GenerateTexture(operation, "QueryLod", {}), + GetSwizzle(meta->element)); } return "0"; } @@ -1398,7 +1412,7 @@ private: const auto target = std::get_if<ImmediateNode>(operation[0]); UNIMPLEMENTED_IF(!target); - code.AddLine(fmt::format("jmp_to = 0x{:x}u;", target->GetValue())); + code.AddLine("jmp_to = 0x{:x}u;", target->GetValue()); code.AddLine("break;"); return {}; } @@ -1407,7 +1421,7 @@ private: const auto target = std::get_if<ImmediateNode>(operation[0]); UNIMPLEMENTED_IF(!target); - code.AddLine(fmt::format("flow_stack[flow_stack_top++] = 0x{:x}u;", target->GetValue())); + code.AddLine("flow_stack[flow_stack_top++] = 0x{:x}u;", target->GetValue()); return {}; } @@ -1433,7 +1447,7 @@ private: UNIMPLEMENTED_IF_MSG(header.ps.omap.sample_mask != 0, "Sample mask write is unimplemented"); - code.AddLine("if (alpha_test[0] != 0) {"); + code.AddLine("if (alpha_test[0] != 0) {{"); ++code.scope; // We start on the register containing the alpha value in the first RT. u32 current_reg = 3; @@ -1444,13 +1458,12 @@ private: header.ps.IsColorComponentOutputEnabled(render_target, 1) || header.ps.IsColorComponentOutputEnabled(render_target, 2) || header.ps.IsColorComponentOutputEnabled(render_target, 3)) { - code.AddLine( - fmt::format("if (!AlphaFunc({})) discard;", SafeGetRegister(current_reg))); + code.AddLine("if (!AlphaFunc({})) discard;", SafeGetRegister(current_reg)); current_reg += 4; } } --code.scope; - code.AddLine('}'); + code.AddLine("}}"); // Write the color outputs using the data in the shader registers, disabled // rendertargets/components are skipped in the register assignment. @@ -1459,8 +1472,8 @@ private: // TODO(Subv): Figure out how dual-source blending is configured in the Switch. for (u32 component = 0; component < 4; ++component) { if (header.ps.IsColorComponentOutputEnabled(render_target, component)) { - code.AddLine(fmt::format("FragColor{}[{}] = {};", render_target, component, - SafeGetRegister(current_reg))); + code.AddLine("FragColor{}[{}] = {};", render_target, component, + SafeGetRegister(current_reg)); ++current_reg; } } @@ -1469,7 +1482,7 @@ private: if (header.ps.omap.depth) { // The depth output is always 2 registers after the last color output, and current_reg // already contains one past the last color register. - code.AddLine("gl_FragDepth = " + SafeGetRegister(current_reg + 1) + ';'); + code.AddLine("gl_FragDepth = {};", SafeGetRegister(current_reg + 1)); } code.AddLine("return;"); @@ -1479,11 +1492,11 @@ private: std::string Discard(Operation operation) { // Enclose "discard" in a conditional, so that GLSL compilation does not complain // about unexecuted instructions that may follow this. - code.AddLine("if (true) {"); + code.AddLine("if (true) {{"); ++code.scope; code.AddLine("discard;"); --code.scope; - code.AddLine("}"); + code.AddLine("}}"); return {}; } @@ -1713,7 +1726,7 @@ private: const auto index = static_cast<u32>(flag); ASSERT(index < static_cast<u32>(InternalFlag::Amount)); - return std::string(InternalFlagNames[index]) + '_' + suffix; + return fmt::format("{}_{}", InternalFlagNames[index], suffix); } std::string GetSampler(const Sampler& sampler) const { @@ -1721,7 +1734,7 @@ private: } std::string GetDeclarationWithSuffix(u32 index, const std::string& name) const { - return name + '_' + std::to_string(index) + '_' + suffix; + return fmt::format("{}_{}_{}", name, index, suffix); } u32 GetNumPhysicalInputAttributes() const { @@ -1749,24 +1762,25 @@ private: } // Anonymous namespace std::string GetCommonDeclarations() { - const auto cbuf = std::to_string(MAX_CONSTBUFFER_ELEMENTS); - return "#define MAX_CONSTBUFFER_ELEMENTS " + cbuf + "\n" + - "#define ftoi floatBitsToInt\n" - "#define ftou floatBitsToUint\n" - "#define itof intBitsToFloat\n" - "#define utof uintBitsToFloat\n\n" - "float fromHalf2(vec2 pair) {\n" - " return utof(packHalf2x16(pair));\n" - "}\n\n" - "vec2 toHalf2(float value) {\n" - " return unpackHalf2x16(ftou(value));\n" - "}\n\n" - "bvec2 halfFloatNanComparison(bvec2 comparison, vec2 pair1, vec2 pair2) {\n" - " bvec2 is_nan1 = isnan(pair1);\n" - " bvec2 is_nan2 = isnan(pair2);\n" - " return bvec2(comparison.x || is_nan1.x || is_nan2.x, comparison.y || is_nan1.y || " - "is_nan2.y);\n" - "}\n"; + return fmt::format( + "#define MAX_CONSTBUFFER_ELEMENTS {}\n" + "#define ftoi floatBitsToInt\n" + "#define ftou floatBitsToUint\n" + "#define itof intBitsToFloat\n" + "#define utof uintBitsToFloat\n\n" + "float fromHalf2(vec2 pair) {{\n" + " return utof(packHalf2x16(pair));\n" + "}}\n\n" + "vec2 toHalf2(float value) {{\n" + " return unpackHalf2x16(ftou(value));\n" + "}}\n\n" + "bvec2 halfFloatNanComparison(bvec2 comparison, vec2 pair1, vec2 pair2) {{\n" + " bvec2 is_nan1 = isnan(pair1);\n" + " bvec2 is_nan2 = isnan(pair2);\n" + " return bvec2(comparison.x || is_nan1.x || is_nan2.x, comparison.y || is_nan1.y || " + "is_nan2.y);\n" + "}}\n", + MAX_CONSTBUFFER_ELEMENTS); } ProgramResult Decompile(const Device& device, const ShaderIR& ir, Maxwell::ShaderStage stage, diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp index fba9c594a..ee4a45ca2 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp @@ -183,8 +183,7 @@ ShaderDiskCacheOpenGL::LoadTransferable() { return {{raws, usages}}; } -std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, - std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>> +std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, ShaderDumpsMap> ShaderDiskCacheOpenGL::LoadPrecompiled() { if (!IsUsable()) return {}; @@ -208,8 +207,7 @@ ShaderDiskCacheOpenGL::LoadPrecompiled() { return *result; } -std::optional<std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, - std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>> +std::optional<std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, ShaderDumpsMap>> ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) { // Read compressed file from disk and decompress to virtual precompiled cache file std::vector<u8> compressed(file.GetSize()); @@ -230,7 +228,7 @@ ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) { } std::unordered_map<u64, ShaderDiskCacheDecompiled> decompiled; - std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump> dumps; + ShaderDumpsMap dumps; while (precompiled_cache_virtual_file_offset < precompiled_cache_virtual_file.GetSize()) { PrecompiledEntryKind kind{}; if (!LoadObjectFromPrecompiled(kind)) { diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.h b/src/video_core/renderer_opengl/gl_shader_disk_cache.h index 2da0a4a23..ecd72ba58 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.h @@ -33,6 +33,11 @@ namespace OpenGL { using ProgramCode = std::vector<u64>; using Maxwell = Tegra::Engines::Maxwell3D::Regs; +struct ShaderDiskCacheUsage; +struct ShaderDiskCacheDump; + +using ShaderDumpsMap = std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>; + /// Allocated bindings used by an OpenGL shader program struct BaseBindings { u32 cbuf{}; @@ -294,4 +299,4 @@ private: bool tried_to_load{}; }; -} // namespace OpenGL
\ No newline at end of file +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 7ab0b4553..d2bb705a9 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -19,8 +19,7 @@ static constexpr u32 PROGRAM_OFFSET{10}; ProgramResult GenerateVertexShader(const Device& device, const ShaderSetup& setup) { const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); - std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n"; - out += "// Shader Unique Id: VS" + id + "\n\n"; + std::string out = "// Shader Unique Id: VS" + id + "\n\n"; out += GetCommonDeclarations(); out += R"( @@ -82,8 +81,7 @@ void main() { ProgramResult GenerateGeometryShader(const Device& device, const ShaderSetup& setup) { const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); - std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n"; - out += "// Shader Unique Id: GS" + id + "\n\n"; + std::string out = "// Shader Unique Id: GS" + id + "\n\n"; out += GetCommonDeclarations(); out += R"( @@ -113,8 +111,7 @@ void main() { ProgramResult GenerateFragmentShader(const Device& device, const ShaderSetup& setup) { const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); - std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n"; - out += "// Shader Unique Id: FS" + id + "\n\n"; + std::string out = "// Shader Unique Id: FS" + id + "\n\n"; out += GetCommonDeclarations(); out += R"( diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index d69cba9c3..3451d321d 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -97,8 +97,8 @@ static std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(const float width, cons return matrix; } -RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& window, Core::System& system) - : VideoCore::RendererBase{window}, system{system} {} +RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system) + : VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system} {} RendererOpenGL::~RendererOpenGL() = default; @@ -265,7 +265,7 @@ void RendererOpenGL::CreateRasterizer() { } // Initialize sRGB Usage OpenGLState::ClearsRGBUsed(); - rasterizer = std::make_unique<RasterizerOpenGL>(system, screen_info); + rasterizer = std::make_unique<RasterizerOpenGL>(system, emu_window, screen_info); } void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 6cbf9d2cb..4aebf2321 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -45,7 +45,7 @@ struct ScreenInfo { class RendererOpenGL : public VideoCore::RendererBase { public: - explicit RendererOpenGL(Core::Frontend::EmuWindow& window, Core::System& system); + explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system); ~RendererOpenGL() override; /// Swap buffers (render frame) @@ -77,6 +77,7 @@ private: void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a, const TextureInfo& texture); + Core::Frontend::EmuWindow& emu_window; Core::System& system; OpenGLState state; diff --git a/src/video_core/renderer_opengl/utils.cpp b/src/video_core/renderer_opengl/utils.cpp index 84a987371..f23fc9f9d 100644 --- a/src/video_core/renderer_opengl/utils.cpp +++ b/src/video_core/renderer_opengl/utils.cpp @@ -38,27 +38,27 @@ void BindBuffersRangePushBuffer::Bind() const { sizes.data()); } -void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string extra_info) { +void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string_view extra_info) { if (!GLAD_GL_KHR_debug) { - return; // We don't need to throw an error as this is just for debugging + // We don't need to throw an error as this is just for debugging + return; } - const std::string nice_addr = fmt::format("0x{:016x}", addr); - std::string object_label; + std::string object_label; if (extra_info.empty()) { switch (identifier) { case GL_TEXTURE: - object_label = "Texture@" + nice_addr; + object_label = fmt::format("Texture@0x{:016X}", addr); break; case GL_PROGRAM: - object_label = "Shader@" + nice_addr; + object_label = fmt::format("Shader@0x{:016X}", addr); break; default: - object_label = fmt::format("Object(0x{:x})@{}", identifier, nice_addr); + object_label = fmt::format("Object(0x{:X})@0x{:016X}", identifier, addr); break; } } else { - object_label = extra_info + '@' + nice_addr; + object_label = fmt::format("{}@0x{:016X}", extra_info, addr); } glObjectLabel(identifier, handle, -1, static_cast<const GLchar*>(object_label.c_str())); } diff --git a/src/video_core/renderer_opengl/utils.h b/src/video_core/renderer_opengl/utils.h index aef45c9dc..b3e9fc499 100644 --- a/src/video_core/renderer_opengl/utils.h +++ b/src/video_core/renderer_opengl/utils.h @@ -4,7 +4,7 @@ #pragma once -#include <string> +#include <string_view> #include <vector> #include <glad/glad.h> #include "common/common_types.h" @@ -30,6 +30,6 @@ private: std::vector<GLsizeiptr> sizes; }; -void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string extra_info = ""); +void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string_view extra_info = {}); } // namespace OpenGL
\ No newline at end of file diff --git a/src/video_core/shader/decode/arithmetic.cpp b/src/video_core/shader/decode/arithmetic.cpp index 3190e2d7c..b4859bc1e 100644 --- a/src/video_core/shader/decode/arithmetic.cpp +++ b/src/video_core/shader/decode/arithmetic.cpp @@ -4,6 +4,7 @@ #include "common/assert.h" #include "common/common_types.h" +#include "common/logging/log.h" #include "video_core/engines/shader_bytecode.h" #include "video_core/shader/shader_ir.h" @@ -152,4 +153,4 @@ u32 ShaderIR::DecodeArithmetic(NodeBlock& bb, u32 pc) { return pc; } -} // namespace VideoCommon::Shader
\ No newline at end of file +} // namespace VideoCommon::Shader diff --git a/src/video_core/shader/decode/arithmetic_half.cpp b/src/video_core/shader/decode/arithmetic_half.cpp index 2098c1170..3a29c4a46 100644 --- a/src/video_core/shader/decode/arithmetic_half.cpp +++ b/src/video_core/shader/decode/arithmetic_half.cpp @@ -4,6 +4,7 @@ #include "common/assert.h" #include "common/common_types.h" +#include "common/logging/log.h" #include "video_core/engines/shader_bytecode.h" #include "video_core/shader/shader_ir.h" diff --git a/src/video_core/shader/decode/arithmetic_half_immediate.cpp b/src/video_core/shader/decode/arithmetic_half_immediate.cpp index fbcd35b18..5341e460f 100644 --- a/src/video_core/shader/decode/arithmetic_half_immediate.cpp +++ b/src/video_core/shader/decode/arithmetic_half_immediate.cpp @@ -4,6 +4,7 @@ #include "common/assert.h" #include "common/common_types.h" +#include "common/logging/log.h" #include "video_core/engines/shader_bytecode.h" #include "video_core/shader/shader_ir.h" @@ -47,4 +48,4 @@ u32 ShaderIR::DecodeArithmeticHalfImmediate(NodeBlock& bb, u32 pc) { return pc; } -} // namespace VideoCommon::Shader
\ No newline at end of file +} // namespace VideoCommon::Shader diff --git a/src/video_core/shader/decode/arithmetic_immediate.cpp b/src/video_core/shader/decode/arithmetic_immediate.cpp index 0d139c0d2..3095f2fd4 100644 --- a/src/video_core/shader/decode/arithmetic_immediate.cpp +++ b/src/video_core/shader/decode/arithmetic_immediate.cpp @@ -49,4 +49,4 @@ u32 ShaderIR::DecodeArithmeticImmediate(NodeBlock& bb, u32 pc) { return pc; } -} // namespace VideoCommon::Shader
\ No newline at end of file +} // namespace VideoCommon::Shader diff --git a/src/video_core/shader/decode/arithmetic_integer_immediate.cpp b/src/video_core/shader/decode/arithmetic_integer_immediate.cpp index 3ed5ccc5a..679ac0d4e 100644 --- a/src/video_core/shader/decode/arithmetic_integer_immediate.cpp +++ b/src/video_core/shader/decode/arithmetic_integer_immediate.cpp @@ -93,4 +93,4 @@ void ShaderIR::WriteLogicOperation(NodeBlock& bb, Register dest, LogicOperation } } -} // namespace VideoCommon::Shader
\ No newline at end of file +} // namespace VideoCommon::Shader diff --git a/src/video_core/shader/decode/bfe.cpp b/src/video_core/shader/decode/bfe.cpp index 6a95dc928..1ae192c6a 100644 --- a/src/video_core/shader/decode/bfe.cpp +++ b/src/video_core/shader/decode/bfe.cpp @@ -46,4 +46,4 @@ u32 ShaderIR::DecodeBfe(NodeBlock& bb, u32 pc) { return pc; } -} // namespace VideoCommon::Shader
\ No newline at end of file +} // namespace VideoCommon::Shader diff --git a/src/video_core/shader/decode/bfi.cpp b/src/video_core/shader/decode/bfi.cpp index 601d66f1f..0b12a0d08 100644 --- a/src/video_core/shader/decode/bfi.cpp +++ b/src/video_core/shader/decode/bfi.cpp @@ -38,4 +38,4 @@ u32 ShaderIR::DecodeBfi(NodeBlock& bb, u32 pc) { return pc; } -} // namespace VideoCommon::Shader
\ No newline at end of file +} // namespace VideoCommon::Shader diff --git a/src/video_core/shader/decode/ffma.cpp b/src/video_core/shader/decode/ffma.cpp index 0559cc8de..a1d04c6e5 100644 --- a/src/video_core/shader/decode/ffma.cpp +++ b/src/video_core/shader/decode/ffma.cpp @@ -56,4 +56,4 @@ u32 ShaderIR::DecodeFfma(NodeBlock& bb, u32 pc) { return pc; } -} // namespace VideoCommon::Shader
\ No newline at end of file +} // namespace VideoCommon::Shader diff --git a/src/video_core/shader/decode/float_set.cpp b/src/video_core/shader/decode/float_set.cpp index 1bd6755dd..cc522f1de 100644 --- a/src/video_core/shader/decode/float_set.cpp +++ b/src/video_core/shader/decode/float_set.cpp @@ -55,4 +55,4 @@ u32 ShaderIR::DecodeFloatSet(NodeBlock& bb, u32 pc) { return pc; } -} // namespace VideoCommon::Shader
\ No newline at end of file +} // namespace VideoCommon::Shader diff --git a/src/video_core/shader/decode/float_set_predicate.cpp b/src/video_core/shader/decode/float_set_predicate.cpp index 9285b8d05..9d2322a1d 100644 --- a/src/video_core/shader/decode/float_set_predicate.cpp +++ b/src/video_core/shader/decode/float_set_predicate.cpp @@ -53,4 +53,4 @@ u32 ShaderIR::DecodeFloatSetPredicate(NodeBlock& bb, u32 pc) { return pc; } -} // namespace VideoCommon::Shader
\ No newline at end of file +} // namespace VideoCommon::Shader diff --git a/src/video_core/shader/decode/half_set.cpp b/src/video_core/shader/decode/half_set.cpp index 1dd94bf9d..755f2ec44 100644 --- a/src/video_core/shader/decode/half_set.cpp +++ b/src/video_core/shader/decode/half_set.cpp @@ -6,6 +6,7 @@ #include "common/assert.h" #include "common/common_types.h" +#include "common/logging/log.h" #include "video_core/engines/shader_bytecode.h" #include "video_core/shader/shader_ir.h" @@ -64,4 +65,4 @@ u32 ShaderIR::DecodeHalfSet(NodeBlock& bb, u32 pc) { return pc; } -} // namespace VideoCommon::Shader
\ No newline at end of file +} // namespace VideoCommon::Shader diff --git a/src/video_core/shader/decode/half_set_predicate.cpp b/src/video_core/shader/decode/half_set_predicate.cpp index 6e59eb650..fba44d714 100644 --- a/src/video_core/shader/decode/half_set_predicate.cpp +++ b/src/video_core/shader/decode/half_set_predicate.cpp @@ -59,4 +59,4 @@ u32 ShaderIR::DecodeHalfSetPredicate(NodeBlock& bb, u32 pc) { return pc; } -} // namespace VideoCommon::Shader
\ No newline at end of file +} // namespace VideoCommon::Shader diff --git a/src/video_core/shader/decode/integer_set.cpp b/src/video_core/shader/decode/integer_set.cpp index a3bf17eba..a4cdaf74d 100644 --- a/src/video_core/shader/decode/integer_set.cpp +++ b/src/video_core/shader/decode/integer_set.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "common/assert.h" #include "common/common_types.h" #include "video_core/engines/shader_bytecode.h" #include "video_core/shader/shader_ir.h" @@ -47,4 +46,4 @@ u32 ShaderIR::DecodeIntegerSet(NodeBlock& bb, u32 pc) { return pc; } -} // namespace VideoCommon::Shader
\ No newline at end of file +} // namespace VideoCommon::Shader diff --git a/src/video_core/shader/decode/integer_set_predicate.cpp b/src/video_core/shader/decode/integer_set_predicate.cpp index aad836d24..a6a1fb632 100644 --- a/src/video_core/shader/decode/integer_set_predicate.cpp +++ b/src/video_core/shader/decode/integer_set_predicate.cpp @@ -50,4 +50,4 @@ u32 ShaderIR::DecodeIntegerSetPredicate(NodeBlock& bb, u32 pc) { return pc; } -} // namespace VideoCommon::Shader
\ No newline at end of file +} // namespace VideoCommon::Shader diff --git a/src/video_core/shader/decode/memory.cpp b/src/video_core/shader/decode/memory.cpp index 6a992c543..e6a010a7d 100644 --- a/src/video_core/shader/decode/memory.cpp +++ b/src/video_core/shader/decode/memory.cpp @@ -12,8 +12,6 @@ #include "video_core/engines/shader_bytecode.h" #include "video_core/shader/shader_ir.h" -#pragma optimize("", off) - namespace VideoCommon::Shader { using Tegra::Shader::Attribute; @@ -148,12 +146,25 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { } break; } + case OpCode::Id::LD: case OpCode::Id::LDG: { + const auto type = [instr, &opcode]() -> Tegra::Shader::UniformType { + switch (opcode->get().GetId()) { + case OpCode::Id::LD: + UNIMPLEMENTED_IF_MSG(!instr.generic.extended, "Unextended LD is not implemented"); + return instr.generic.type; + case OpCode::Id::LDG: + return instr.ldg.type; + default: + UNREACHABLE(); + return {}; + } + }(); + const auto [real_address_base, base_address, descriptor] = - TrackAndGetGlobalMemory(bb, GetRegister(instr.gpr8), - static_cast<u32>(instr.ldg.immediate_offset.Value()), false); + TrackAndGetGlobalMemory(bb, instr, false); - const u32 count = GetUniformTypeElementsCount(instr.ldg.type); + const u32 count = GetUniformTypeElementsCount(type); for (u32 i = 0; i < count; ++i) { const Node it_offset = Immediate(i * 4); const Node real_address = @@ -167,28 +178,6 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { } break; } - case OpCode::Id::STG: { - const auto [real_address_base, base_address, descriptor] = - TrackAndGetGlobalMemory(bb, GetRegister(instr.gpr8), - static_cast<u32>(instr.stg.immediate_offset.Value()), true); - - // Encode in temporary registers like this: real_base_address, {registers_to_be_written...} - SetTemporal(bb, 0, real_address_base); - - const u32 count = GetUniformTypeElementsCount(instr.stg.type); - for (u32 i = 0; i < count; ++i) { - SetTemporal(bb, i + 1, GetRegister(instr.gpr0.Value() + i)); - } - for (u32 i = 0; i < count; ++i) { - const Node it_offset = Immediate(i * 4); - const Node real_address = - Operation(OperationCode::UAdd, NO_PRECISE, real_address_base, it_offset); - const Node gmem = StoreNode(GmemNode(real_address, base_address, descriptor)); - - bb.push_back(Operation(OperationCode::Assign, gmem, GetTemporal(i + 1))); - } - break; - } case OpCode::Id::ST_A: { UNIMPLEMENTED_IF_MSG(instr.gpr8.Value() != Register::ZeroIndex, "Indirect attribute loads are not supported"); @@ -244,6 +233,41 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { } break; } + case OpCode::Id::ST: + case OpCode::Id::STG: { + const auto type = [instr, &opcode]() -> Tegra::Shader::UniformType { + switch (opcode->get().GetId()) { + case OpCode::Id::ST: + UNIMPLEMENTED_IF_MSG(!instr.generic.extended, "Unextended ST is not implemented"); + return instr.generic.type; + case OpCode::Id::STG: + return instr.stg.type; + default: + UNREACHABLE(); + return {}; + } + }(); + + const auto [real_address_base, base_address, descriptor] = + TrackAndGetGlobalMemory(bb, instr, true); + + // Encode in temporary registers like this: real_base_address, {registers_to_be_written...} + SetTemporal(bb, 0, real_address_base); + + const u32 count = GetUniformTypeElementsCount(type); + for (u32 i = 0; i < count; ++i) { + SetTemporal(bb, i + 1, GetRegister(instr.gpr0.Value() + i)); + } + for (u32 i = 0; i < count; ++i) { + const Node it_offset = Immediate(i * 4); + const Node real_address = + Operation(OperationCode::UAdd, NO_PRECISE, real_address_base, it_offset); + const Node gmem = StoreNode(GmemNode(real_address, base_address, descriptor)); + + bb.push_back(Operation(OperationCode::Assign, gmem, GetTemporal(i + 1))); + } + break; + } case OpCode::Id::AL2P: { // Ignore al2p.direction since we don't care about it. @@ -267,9 +291,11 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { } std::tuple<Node, Node, GlobalMemoryBase> ShaderIR::TrackAndGetGlobalMemory(NodeBlock& bb, - Node addr_register, - u32 immediate_offset, + Instruction instr, bool is_write) { + const auto addr_register{GetRegister(instr.gmem.gpr)}; + const auto immediate_offset{static_cast<u32>(instr.gmem.offset)}; + const Node base_address{ TrackCbuf(addr_register, global_code, static_cast<s64>(global_code.size()))}; const auto cbuf = std::get_if<CbufNode>(base_address); diff --git a/src/video_core/shader/decode/other.cpp b/src/video_core/shader/decode/other.cpp index 77c6f9951..a6c123573 100644 --- a/src/video_core/shader/decode/other.cpp +++ b/src/video_core/shader/decode/other.cpp @@ -4,6 +4,7 @@ #include "common/assert.h" #include "common/common_types.h" +#include "common/logging/log.h" #include "video_core/engines/shader_bytecode.h" #include "video_core/shader/shader_ir.h" diff --git a/src/video_core/shader/decode/predicate_set_predicate.cpp b/src/video_core/shader/decode/predicate_set_predicate.cpp index 83c61680e..71844c42b 100644 --- a/src/video_core/shader/decode/predicate_set_predicate.cpp +++ b/src/video_core/shader/decode/predicate_set_predicate.cpp @@ -64,4 +64,4 @@ u32 ShaderIR::DecodePredicateSetPredicate(NodeBlock& bb, u32 pc) { return pc; } -} // namespace VideoCommon::Shader
\ No newline at end of file +} // namespace VideoCommon::Shader diff --git a/src/video_core/shader/decode/predicate_set_register.cpp b/src/video_core/shader/decode/predicate_set_register.cpp index d0495995d..387491bd3 100644 --- a/src/video_core/shader/decode/predicate_set_register.cpp +++ b/src/video_core/shader/decode/predicate_set_register.cpp @@ -43,4 +43,4 @@ u32 ShaderIR::DecodePredicateSetRegister(NodeBlock& bb, u32 pc) { return pc; } -} // namespace VideoCommon::Shader
\ No newline at end of file +} // namespace VideoCommon::Shader diff --git a/src/video_core/shader/decode/register_set_predicate.cpp b/src/video_core/shader/decode/register_set_predicate.cpp index f070e8912..f8659e48e 100644 --- a/src/video_core/shader/decode/register_set_predicate.cpp +++ b/src/video_core/shader/decode/register_set_predicate.cpp @@ -48,4 +48,4 @@ u32 ShaderIR::DecodeRegisterSetPredicate(NodeBlock& bb, u32 pc) { return pc; } -} // namespace VideoCommon::Shader
\ No newline at end of file +} // namespace VideoCommon::Shader diff --git a/src/video_core/shader/decode/shift.cpp b/src/video_core/shader/decode/shift.cpp index 951e85f44..44ae87ece 100644 --- a/src/video_core/shader/decode/shift.cpp +++ b/src/video_core/shader/decode/shift.cpp @@ -52,4 +52,4 @@ u32 ShaderIR::DecodeShift(NodeBlock& bb, u32 pc) { return pc; } -} // namespace VideoCommon::Shader
\ No newline at end of file +} // namespace VideoCommon::Shader diff --git a/src/video_core/shader/decode/video.cpp b/src/video_core/shader/decode/video.cpp index 956c01d9b..cb9ab72b1 100644 --- a/src/video_core/shader/decode/video.cpp +++ b/src/video_core/shader/decode/video.cpp @@ -108,4 +108,4 @@ Node ShaderIR::GetVideoOperand(Node op, bool is_chunk, bool is_signed, } } -} // namespace VideoCommon::Shader
\ No newline at end of file +} // namespace VideoCommon::Shader diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp index 153ad1fd0..8a6ee5cf5 100644 --- a/src/video_core/shader/shader_ir.cpp +++ b/src/video_core/shader/shader_ir.cpp @@ -39,8 +39,8 @@ Node ShaderIR::Conditional(Node condition, std::vector<Node>&& code) { return StoreNode(ConditionalNode(condition, std::move(code))); } -Node ShaderIR::Comment(const std::string& text) { - return StoreNode(CommentNode(text)); +Node ShaderIR::Comment(std::string text) { + return StoreNode(CommentNode(std::move(text))); } Node ShaderIR::Immediate(u32 value) { diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h index f99300c1c..ff7472e30 100644 --- a/src/video_core/shader/shader_ir.h +++ b/src/video_core/shader/shader_ir.h @@ -669,7 +669,7 @@ private: /// Creates a conditional node Node Conditional(Node condition, std::vector<Node>&& code); /// Creates a commentary - Node Comment(const std::string& text); + Node Comment(std::string text); /// Creates an u32 immediate Node Immediate(u32 value); /// Creates a s32 immediate @@ -824,10 +824,8 @@ private: std::pair<Node, s64> TrackRegister(const GprNode* tracked, const NodeBlock& code, s64 cursor) const; - std::tuple<Node, Node, GlobalMemoryBase> TrackAndGetGlobalMemory(NodeBlock& bb, - Node addr_register, - u32 immediate_offset, - bool is_write); + std::tuple<Node, Node, GlobalMemoryBase> TrackAndGetGlobalMemory( + NodeBlock& bb, Tegra::Shader::Instruction instr, bool is_write); template <typename... T> Node Operation(OperationCode code, const T*... operands) { diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 7e883991a..3ea7b55d0 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -155,6 +155,10 @@ target_compile_definitions(yuzu PRIVATE # Use QStringBuilder for string concatenation to reduce # the overall number of temporary strings created. -DQT_USE_QSTRINGBUILDER + + # Disable implicit conversions from/to C strings + -DQT_NO_CAST_FROM_ASCII + -DQT_NO_CAST_TO_ASCII ) if (YUZU_ENABLE_COMPATIBILITY_REPORTING) diff --git a/src/yuzu/applets/error.cpp b/src/yuzu/applets/error.cpp index 106dde9e2..08ed57355 100644 --- a/src/yuzu/applets/error.cpp +++ b/src/yuzu/applets/error.cpp @@ -29,11 +29,13 @@ void QtErrorDisplay::ShowError(ResultCode error, std::function<void()> finished) void QtErrorDisplay::ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time, std::function<void()> finished) const { this->callback = std::move(finished); + + const QDateTime date_time = QDateTime::fromSecsSinceEpoch(time.count()); emit MainWindowDisplayError( tr("An error occured on %1 at %2.\nPlease try again or contact the " "developer of the software.\n\nError Code: %3-%4 (0x%5)") - .arg(QDateTime::fromSecsSinceEpoch(time.count()).toString("dddd, MMMM d, yyyy")) - .arg(QDateTime::fromSecsSinceEpoch(time.count()).toString("h:mm:ss A")) + .arg(date_time.toString(QStringLiteral("dddd, MMMM d, yyyy"))) + .arg(date_time.toString(QStringLiteral("h:mm:ss A"))) .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) .arg(error.description, 4, 10, QChar::fromLatin1('0')) .arg(error.raw, 8, 16, QChar::fromLatin1('0'))); diff --git a/src/yuzu/applets/software_keyboard.cpp b/src/yuzu/applets/software_keyboard.cpp index f3eb29b25..5223ec977 100644 --- a/src/yuzu/applets/software_keyboard.cpp +++ b/src/yuzu/applets/software_keyboard.cpp @@ -18,23 +18,30 @@ QtSoftwareKeyboardValidator::QtSoftwareKeyboardValidator( : parameters(std::move(parameters)) {} QValidator::State QtSoftwareKeyboardValidator::validate(QString& input, int& pos) const { - if (input.size() > parameters.max_length) + if (input.size() > static_cast<s64>(parameters.max_length)) { return Invalid; - if (parameters.disable_space && input.contains(' ')) + } + if (parameters.disable_space && input.contains(QLatin1Char{' '})) { return Invalid; - if (parameters.disable_address && input.contains('@')) + } + if (parameters.disable_address && input.contains(QLatin1Char{'@'})) { return Invalid; - if (parameters.disable_percent && input.contains('%')) + } + if (parameters.disable_percent && input.contains(QLatin1Char{'%'})) { return Invalid; - if (parameters.disable_slash && (input.contains('/') || input.contains('\\'))) + } + if (parameters.disable_slash && + (input.contains(QLatin1Char{'/'}) || input.contains(QLatin1Char{'\\'}))) { return Invalid; + } if (parameters.disable_number && std::any_of(input.begin(), input.end(), [](QChar c) { return c.isDigit(); })) { return Invalid; } - if (parameters.disable_download_code && - std::any_of(input.begin(), input.end(), [](QChar c) { return c == 'O' || c == 'I'; })) { + if (parameters.disable_download_code && std::any_of(input.begin(), input.end(), [](QChar c) { + return c == QLatin1Char{'O'} || c == QLatin1Char{'I'}; + })) { return Invalid; } @@ -142,7 +149,7 @@ void QtSoftwareKeyboard::SendTextCheckDialog(std::u16string error_message, void QtSoftwareKeyboard::MainWindowFinishedText(std::optional<std::u16string> text) { // Acquire the HLE mutex std::lock_guard lock{HLE::g_hle_lock}; - text_output(text); + text_output(std::move(text)); } void QtSoftwareKeyboard::MainWindowFinishedCheckDialog() { diff --git a/src/yuzu/applets/software_keyboard.h b/src/yuzu/applets/software_keyboard.h index c63720ba4..78c5a042b 100644 --- a/src/yuzu/applets/software_keyboard.h +++ b/src/yuzu/applets/software_keyboard.h @@ -6,7 +6,6 @@ #include <QDialog> #include <QValidator> -#include "common/assert.h" #include "core/frontend/applets/software_keyboard.h" class GMainWindow; diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 810954b36..9e420b359 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -91,25 +91,25 @@ void EmuThread::run() { class GGLContext : public Core::Frontend::GraphicsContext { public: - explicit GGLContext(QOpenGLContext* shared_context) - : context{std::make_unique<QOpenGLContext>(shared_context)} { - surface.setFormat(shared_context->format()); - surface.create(); + explicit GGLContext(QOpenGLContext* shared_context) : shared_context{shared_context} { + context.setFormat(shared_context->format()); + context.setShareContext(shared_context); + context.create(); } void MakeCurrent() override { - context->makeCurrent(&surface); + context.makeCurrent(shared_context->surface()); } void DoneCurrent() override { - context->doneCurrent(); + context.doneCurrent(); } void SwapBuffers() override {} private: - std::unique_ptr<QOpenGLContext> context; - QOffscreenSurface surface; + QOpenGLContext* shared_context; + QOpenGLContext context; }; // This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL @@ -188,7 +188,9 @@ private: GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) : QWidget(parent), emu_thread(emu_thread) { setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") - .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); + .arg(QString::fromUtf8(Common::g_build_name), + QString::fromUtf8(Common::g_scm_branch), + QString::fromUtf8(Common::g_scm_desc))); setAttribute(Qt::WA_AcceptTouchEvents); InputCommon::Init(); @@ -217,7 +219,7 @@ void GRenderWindow::SwapBuffers() { // However: // - The Qt debug runtime prints a bogus warning on the console if `makeCurrent` wasn't called // since the last time `swapBuffers` was executed; - // - On macOS, if `makeCurrent` isn't called explicitely, resizing the buffer breaks. + // - On macOS, if `makeCurrent` isn't called explicitly, resizing the buffer breaks. context->makeCurrent(child); context->swapBuffers(child); @@ -356,7 +358,7 @@ void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) { } std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { - return std::make_unique<GGLContext>(shared_context.get()); + return std::make_unique<GGLContext>(context.get()); } void GRenderWindow::InitRenderTarget() { @@ -438,8 +440,7 @@ void GRenderWindow::CaptureScreenshot(u16 res_scale, const QString& screenshot_p layout); } -void GRenderWindow::OnMinimalClientAreaChangeRequest( - const std::pair<unsigned, unsigned>& minimal_size) { +void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) { setMinimumSize(minimal_size.first, minimal_size.second); } diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 3df33aca1..7f9f8e8e3 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -162,8 +162,7 @@ private: void TouchUpdateEvent(const QTouchEvent* event); void TouchEndEvent(); - void OnMinimalClientAreaChangeRequest( - const std::pair<unsigned, unsigned>& minimal_size) override; + void OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) override; QWidget* container = nullptr; GGLWidgetInternal* child = nullptr; diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 6c6f047d8..db27da23e 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -11,6 +11,7 @@ #include "core/hle/service/hid/controllers/npad.h" #include "input_common/main.h" #include "yuzu/configuration/config.h" +#include "yuzu/ui_settings.h" Config::Config() { // TODO: Don't hardcode the path; let the frontend decide where to put the config files. @@ -206,79 +207,91 @@ const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default }; // This shouldn't have anything except static initializers (no functions). So -// QKeySequnce(...).toString() is NOT ALLOWED HERE. +// QKeySequence(...).toString() is NOT ALLOWED HERE. // This must be in alphabetical order according to action name as it must have the same order as // UISetting::values.shortcuts, which is alphabetically ordered. -const std::array<UISettings::Shortcut, 15> Config::default_hotkeys{ - {{"Capture Screenshot", "Main Window", {"Ctrl+P", Qt::ApplicationShortcut}}, - {"Continue/Pause Emulation", "Main Window", {"F4", Qt::WindowShortcut}}, - {"Decrease Speed Limit", "Main Window", {"-", Qt::ApplicationShortcut}}, - {"Exit yuzu", "Main Window", {"Ctrl+Q", Qt::WindowShortcut}}, - {"Exit Fullscreen", "Main Window", {"Esc", Qt::WindowShortcut}}, - {"Fullscreen", "Main Window", {"F11", Qt::WindowShortcut}}, - {"Increase Speed Limit", "Main Window", {"+", Qt::ApplicationShortcut}}, - {"Load Amiibo", "Main Window", {"F2", Qt::ApplicationShortcut}}, - {"Load File", "Main Window", {"Ctrl+O", Qt::WindowShortcut}}, - {"Restart Emulation", "Main Window", {"F6", Qt::WindowShortcut}}, - {"Stop Emulation", "Main Window", {"F5", Qt::WindowShortcut}}, - {"Toggle Filter Bar", "Main Window", {"Ctrl+F", Qt::WindowShortcut}}, - {"Toggle Speed Limit", "Main Window", {"Ctrl+Z", Qt::ApplicationShortcut}}, - {"Toggle Status Bar", "Main Window", {"Ctrl+S", Qt::WindowShortcut}}, - {"Change Docked Mode", "Main Window", {"F10", Qt::ApplicationShortcut}}}}; +// clang-format off +const std::array<UISettings::Shortcut, 15> default_hotkeys{{ + {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::ApplicationShortcut}}, + {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}}, + {QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}}, + {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}}, + {QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), Qt::WindowShortcut}}, + {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}}, + {QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}}, + {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::ApplicationShortcut}}, + {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WindowShortcut}}, + {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}}, + {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}}, + {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}}, + {QStringLiteral("Toggle Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}}, + {QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}}, + {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}}, +}}; +// clang-format on void Config::ReadPlayerValues() { for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { auto& player = Settings::values.players[p]; - player.connected = ReadSetting(QString("player_%1_connected").arg(p), false).toBool(); + player.connected = + ReadSetting(QStringLiteral("player_%1_connected").arg(p), false).toBool(); player.type = static_cast<Settings::ControllerType>( qt_config - ->value(QString("player_%1_type").arg(p), + ->value(QStringLiteral("player_%1_type").arg(p), static_cast<u8>(Settings::ControllerType::DualJoycon)) .toUInt()); player.body_color_left = qt_config - ->value(QString("player_%1_body_color_left").arg(p), + ->value(QStringLiteral("player_%1_body_color_left").arg(p), Settings::JOYCON_BODY_NEON_BLUE) .toUInt(); player.body_color_right = qt_config - ->value(QString("player_%1_body_color_right").arg(p), + ->value(QStringLiteral("player_%1_body_color_right").arg(p), Settings::JOYCON_BODY_NEON_RED) .toUInt(); player.button_color_left = qt_config - ->value(QString("player_%1_button_color_left").arg(p), + ->value(QStringLiteral("player_%1_button_color_left").arg(p), Settings::JOYCON_BUTTONS_NEON_BLUE) .toUInt(); - player.button_color_right = qt_config - ->value(QString("player_%1_button_color_right").arg(p), - Settings::JOYCON_BUTTONS_NEON_RED) - .toUInt(); + player.button_color_right = + qt_config + ->value(QStringLiteral("player_%1_button_color_right").arg(p), + Settings::JOYCON_BUTTONS_NEON_RED) + .toUInt(); for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { - std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); - player.buttons[i] = - qt_config - ->value(QString("player_%1_").arg(p) + Settings::NativeButton::mapping[i], - QString::fromStdString(default_param)) - .toString() - .toStdString(); - if (player.buttons[i].empty()) - player.buttons[i] = default_param; + const std::string default_param = + InputCommon::GenerateKeyboardParam(default_buttons[i]); + auto& player_buttons = player.buttons[i]; + + player_buttons = qt_config + ->value(QStringLiteral("player_%1_").arg(p) + + QString::fromUtf8(Settings::NativeButton::mapping[i]), + QString::fromStdString(default_param)) + .toString() + .toStdString(); + if (player_buttons.empty()) { + player_buttons = default_param; + } } for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { - std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], default_analogs[i][3], default_analogs[i][4], 0.5f); - player.analogs[i] = - qt_config - ->value(QString("player_%1_").arg(p) + Settings::NativeAnalog::mapping[i], - QString::fromStdString(default_param)) - .toString() - .toStdString(); - if (player.analogs[i].empty()) - player.analogs[i] = default_param; + auto& player_analogs = player.analogs[i]; + + player_analogs = qt_config + ->value(QStringLiteral("player_%1_").arg(p) + + QString::fromUtf8(Settings::NativeAnalog::mapping[i]), + QString::fromStdString(default_param)) + .toString() + .toStdString(); + if (player_analogs.empty()) { + player_analogs = default_param; + } } } @@ -290,36 +303,45 @@ void Config::ReadPlayerValues() { } void Config::ReadDebugValues() { - Settings::values.debug_pad_enabled = ReadSetting("debug_pad_enabled", false).toBool(); + Settings::values.debug_pad_enabled = + ReadSetting(QStringLiteral("debug_pad_enabled"), false).toBool(); + for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { - std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); - Settings::values.debug_pad_buttons[i] = - qt_config - ->value(QString("debug_pad_") + Settings::NativeButton::mapping[i], - QString::fromStdString(default_param)) - .toString() - .toStdString(); - if (Settings::values.debug_pad_buttons[i].empty()) - Settings::values.debug_pad_buttons[i] = default_param; + const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); + auto& debug_pad_buttons = Settings::values.debug_pad_buttons[i]; + + debug_pad_buttons = qt_config + ->value(QStringLiteral("debug_pad_") + + QString::fromUtf8(Settings::NativeButton::mapping[i]), + QString::fromStdString(default_param)) + .toString() + .toStdString(); + if (debug_pad_buttons.empty()) { + debug_pad_buttons = default_param; + } } for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { - std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], default_analogs[i][3], default_analogs[i][4], 0.5f); - Settings::values.debug_pad_analogs[i] = - qt_config - ->value(QString("debug_pad_") + Settings::NativeAnalog::mapping[i], - QString::fromStdString(default_param)) - .toString() - .toStdString(); - if (Settings::values.debug_pad_analogs[i].empty()) - Settings::values.debug_pad_analogs[i] = default_param; + auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i]; + + debug_pad_analogs = qt_config + ->value(QStringLiteral("debug_pad_") + + QString::fromUtf8(Settings::NativeAnalog::mapping[i]), + QString::fromStdString(default_param)) + .toString() + .toStdString(); + if (debug_pad_analogs.empty()) { + debug_pad_analogs = default_param; + } } } void Config::ReadKeyboardValues() { - Settings::values.keyboard_enabled = ReadSetting("keyboard_enabled", false).toBool(); + Settings::values.keyboard_enabled = + ReadSetting(QStringLiteral("keyboard_enabled"), false).toBool(); std::transform(default_keyboard_keys.begin(), default_keyboard_keys.end(), Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam); @@ -332,31 +354,41 @@ void Config::ReadKeyboardValues() { } void Config::ReadMouseValues() { - Settings::values.mouse_enabled = ReadSetting("mouse_enabled", false).toBool(); + Settings::values.mouse_enabled = ReadSetting(QStringLiteral("mouse_enabled"), false).toBool(); for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { - std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]); - Settings::values.mouse_buttons[i] = - qt_config - ->value(QString("mouse_") + Settings::NativeMouseButton::mapping[i], - QString::fromStdString(default_param)) - .toString() - .toStdString(); - if (Settings::values.mouse_buttons[i].empty()) - Settings::values.mouse_buttons[i] = default_param; + const std::string default_param = + InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]); + auto& mouse_buttons = Settings::values.mouse_buttons[i]; + + mouse_buttons = qt_config + ->value(QStringLiteral("mouse_") + + QString::fromUtf8(Settings::NativeMouseButton::mapping[i]), + QString::fromStdString(default_param)) + .toString() + .toStdString(); + if (mouse_buttons.empty()) { + mouse_buttons = default_param; + } } } void Config::ReadTouchscreenValues() { - Settings::values.touchscreen.enabled = ReadSetting("touchscreen_enabled", true).toBool(); + Settings::values.touchscreen.enabled = + ReadSetting(QStringLiteral("touchscreen_enabled"), true).toBool(); Settings::values.touchscreen.device = - ReadSetting("touchscreen_device", "engine:emu_window").toString().toStdString(); + ReadSetting(QStringLiteral("touchscreen_device"), QStringLiteral("engine:emu_window")) + .toString() + .toStdString(); - Settings::values.touchscreen.finger = ReadSetting("touchscreen_finger", 0).toUInt(); - Settings::values.touchscreen.rotation_angle = ReadSetting("touchscreen_angle", 0).toUInt(); - Settings::values.touchscreen.diameter_x = ReadSetting("touchscreen_diameter_x", 15).toUInt(); - Settings::values.touchscreen.diameter_y = ReadSetting("touchscreen_diameter_y", 15).toUInt(); - qt_config->endGroup(); + Settings::values.touchscreen.finger = + ReadSetting(QStringLiteral("touchscreen_finger"), 0).toUInt(); + Settings::values.touchscreen.rotation_angle = + ReadSetting(QStringLiteral("touchscreen_angle"), 0).toUInt(); + Settings::values.touchscreen.diameter_x = + ReadSetting(QStringLiteral("touchscreen_diameter_x"), 15).toUInt(); + Settings::values.touchscreen.diameter_y = + ReadSetting(QStringLiteral("touchscreen_diameter_y"), 15).toUInt(); } void Config::ApplyDefaultProfileIfInputInvalid() { @@ -366,8 +398,25 @@ void Config::ApplyDefaultProfileIfInputInvalid() { } } -void Config::ReadValues() { - qt_config->beginGroup("Controls"); +void Config::ReadAudioValues() { + qt_config->beginGroup(QStringLiteral("Audio")); + + Settings::values.sink_id = ReadSetting(QStringLiteral("output_engine"), QStringLiteral("auto")) + .toString() + .toStdString(); + Settings::values.enable_audio_stretching = + ReadSetting(QStringLiteral("enable_audio_stretching"), true).toBool(); + Settings::values.audio_device_id = + ReadSetting(QStringLiteral("output_device"), QStringLiteral("auto")) + .toString() + .toStdString(); + Settings::values.volume = ReadSetting(QStringLiteral("volume"), 1).toFloat(); + + qt_config->endGroup(); +} + +void Config::ReadControlValues() { + qt_config->beginGroup(QStringLiteral("Controls")); ReadPlayerValues(); ReadDebugValues(); @@ -376,220 +425,308 @@ void Config::ReadValues() { ReadTouchscreenValues(); Settings::values.motion_device = - ReadSetting("motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01") + ReadSetting(QStringLiteral("motion_device"), + QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01")) .toString() .toStdString(); - qt_config->beginGroup("Core"); - Settings::values.use_cpu_jit = ReadSetting("use_cpu_jit", true).toBool(); - Settings::values.use_multi_core = ReadSetting("use_multi_core", false).toBool(); qt_config->endGroup(); +} - qt_config->beginGroup("Renderer"); - Settings::values.resolution_factor = ReadSetting("resolution_factor", 1.0).toFloat(); - Settings::values.use_frame_limit = ReadSetting("use_frame_limit", true).toBool(); - Settings::values.frame_limit = ReadSetting("frame_limit", 100).toInt(); - Settings::values.use_compatibility_profile = - ReadSetting("use_compatibility_profile", true).toBool(); - Settings::values.use_disk_shader_cache = ReadSetting("use_disk_shader_cache", true).toBool(); - Settings::values.use_accurate_gpu_emulation = - ReadSetting("use_accurate_gpu_emulation", false).toBool(); - Settings::values.use_asynchronous_gpu_emulation = - ReadSetting("use_asynchronous_gpu_emulation", false).toBool(); - Settings::values.force_30fps_mode = ReadSetting("force_30fps_mode", false).toBool(); +void Config::ReadCoreValues() { + qt_config->beginGroup(QStringLiteral("Core")); - Settings::values.bg_red = ReadSetting("bg_red", 0.0).toFloat(); - Settings::values.bg_green = ReadSetting("bg_green", 0.0).toFloat(); - Settings::values.bg_blue = ReadSetting("bg_blue", 0.0).toFloat(); - qt_config->endGroup(); + Settings::values.use_cpu_jit = ReadSetting(QStringLiteral("use_cpu_jit"), true).toBool(); + Settings::values.use_multi_core = ReadSetting(QStringLiteral("use_multi_core"), false).toBool(); - qt_config->beginGroup("Audio"); - Settings::values.sink_id = ReadSetting("output_engine", "auto").toString().toStdString(); - Settings::values.enable_audio_stretching = - ReadSetting("enable_audio_stretching", true).toBool(); - Settings::values.audio_device_id = - ReadSetting("output_device", "auto").toString().toStdString(); - Settings::values.volume = ReadSetting("volume", 1).toFloat(); qt_config->endGroup(); +} + +void Config::ReadDataStorageValues() { + qt_config->beginGroup(QStringLiteral("Data Storage")); - qt_config->beginGroup("Data Storage"); - Settings::values.use_virtual_sd = ReadSetting("use_virtual_sd", true).toBool(); + Settings::values.use_virtual_sd = ReadSetting(QStringLiteral("use_virtual_sd"), true).toBool(); FileUtil::GetUserPath( FileUtil::UserPath::NANDDir, qt_config - ->value("nand_directory", + ->value(QStringLiteral("nand_directory"), QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))) .toString() .toStdString()); FileUtil::GetUserPath( FileUtil::UserPath::SDMCDir, qt_config - ->value("sdmc_directory", + ->value(QStringLiteral("sdmc_directory"), QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))) .toString() .toStdString()); - qt_config->endGroup(); - qt_config->beginGroup("Core"); - Settings::values.use_cpu_jit = ReadSetting("use_cpu_jit", true).toBool(); - Settings::values.use_multi_core = ReadSetting("use_multi_core", false).toBool(); qt_config->endGroup(); +} - qt_config->beginGroup("System"); - Settings::values.use_docked_mode = ReadSetting("use_docked_mode", false).toBool(); - - Settings::values.current_user = - std::clamp<int>(ReadSetting("current_user", 0).toInt(), 0, Service::Account::MAX_USERS - 1); - - Settings::values.language_index = ReadSetting("language_index", 1).toInt(); - - const auto rng_seed_enabled = ReadSetting("rng_seed_enabled", false).toBool(); - if (rng_seed_enabled) { - Settings::values.rng_seed = ReadSetting("rng_seed", 0).toULongLong(); - } else { - Settings::values.rng_seed = std::nullopt; - } - - const auto custom_rtc_enabled = ReadSetting("custom_rtc_enabled", false).toBool(); - if (custom_rtc_enabled) { - Settings::values.custom_rtc = - std::chrono::seconds(ReadSetting("custom_rtc", 0).toULongLong()); - } else { - Settings::values.custom_rtc = std::nullopt; - } - - qt_config->endGroup(); +void Config::ReadDebuggingValues() { + qt_config->beginGroup(QStringLiteral("Debugging")); - qt_config->beginGroup("Miscellaneous"); - Settings::values.log_filter = ReadSetting("log_filter", "*:Info").toString().toStdString(); - Settings::values.use_dev_keys = ReadSetting("use_dev_keys", false).toBool(); - qt_config->endGroup(); + Settings::values.use_gdbstub = ReadSetting(QStringLiteral("use_gdbstub"), false).toBool(); + Settings::values.gdbstub_port = ReadSetting(QStringLiteral("gdbstub_port"), 24689).toInt(); + Settings::values.program_args = + ReadSetting(QStringLiteral("program_args"), QStringLiteral("")).toString().toStdString(); + Settings::values.dump_exefs = ReadSetting(QStringLiteral("dump_exefs"), false).toBool(); + Settings::values.dump_nso = ReadSetting(QStringLiteral("dump_nso"), false).toBool(); - qt_config->beginGroup("Debugging"); - Settings::values.use_gdbstub = ReadSetting("use_gdbstub", false).toBool(); - Settings::values.gdbstub_port = ReadSetting("gdbstub_port", 24689).toInt(); - Settings::values.program_args = ReadSetting("program_args", "").toString().toStdString(); - Settings::values.dump_exefs = ReadSetting("dump_exefs", false).toBool(); - Settings::values.dump_nso = ReadSetting("dump_nso", false).toBool(); qt_config->endGroup(); +} - qt_config->beginGroup("WebService"); - Settings::values.enable_telemetry = ReadSetting("enable_telemetry", true).toBool(); - Settings::values.web_api_url = - ReadSetting("web_api_url", "https://api.yuzu-emu.org").toString().toStdString(); - Settings::values.yuzu_username = ReadSetting("yuzu_username").toString().toStdString(); - Settings::values.yuzu_token = ReadSetting("yuzu_token").toString().toStdString(); - qt_config->endGroup(); +void Config::ReadDisabledAddOnValues() { + const auto size = qt_config->beginReadArray(QStringLiteral("DisabledAddOns")); - const auto size = qt_config->beginReadArray("DisabledAddOns"); for (int i = 0; i < size; ++i) { qt_config->setArrayIndex(i); - const auto title_id = ReadSetting("title_id", 0).toULongLong(); + const auto title_id = ReadSetting(QStringLiteral("title_id"), 0).toULongLong(); std::vector<std::string> out; - const auto d_size = qt_config->beginReadArray("disabled"); + const auto d_size = qt_config->beginReadArray(QStringLiteral("disabled")); for (int j = 0; j < d_size; ++j) { qt_config->setArrayIndex(j); - out.push_back(ReadSetting("d", "").toString().toStdString()); + out.push_back( + ReadSetting(QStringLiteral("d"), QStringLiteral("")).toString().toStdString()); } qt_config->endArray(); Settings::values.disabled_addons.insert_or_assign(title_id, out); } + qt_config->endArray(); +} + +void Config::ReadMiscellaneousValues() { + qt_config->beginGroup(QStringLiteral("Miscellaneous")); + + Settings::values.log_filter = + ReadSetting(QStringLiteral("log_filter"), QStringLiteral("*:Info")) + .toString() + .toStdString(); + Settings::values.use_dev_keys = ReadSetting(QStringLiteral("use_dev_keys"), false).toBool(); - qt_config->beginGroup("UI"); - UISettings::values.theme = ReadSetting("theme", UISettings::themes[0].second).toString(); - UISettings::values.enable_discord_presence = - ReadSetting("enable_discord_presence", true).toBool(); - UISettings::values.screenshot_resolution_factor = - static_cast<u16>(ReadSetting("screenshot_resolution_factor", 0).toUInt()); - UISettings::values.select_user_on_boot = ReadSetting("select_user_on_boot", false).toBool(); - - qt_config->beginGroup("UIGameList"); - UISettings::values.show_unknown = ReadSetting("show_unknown", true).toBool(); - UISettings::values.show_add_ons = ReadSetting("show_add_ons", true).toBool(); - UISettings::values.icon_size = ReadSetting("icon_size", 64).toUInt(); - UISettings::values.row_1_text_id = ReadSetting("row_1_text_id", 3).toUInt(); - UISettings::values.row_2_text_id = ReadSetting("row_2_text_id", 2).toUInt(); qt_config->endGroup(); +} + +void Config::ReadPathValues() { + qt_config->beginGroup(QStringLiteral("Paths")); + + UISettings::values.roms_path = ReadSetting(QStringLiteral("romsPath")).toString(); + UISettings::values.symbols_path = ReadSetting(QStringLiteral("symbolsPath")).toString(); + UISettings::values.game_directory_path = + ReadSetting(QStringLiteral("gameListRootDir"), QStringLiteral(".")).toString(); + UISettings::values.game_directory_deepscan = + ReadSetting(QStringLiteral("gameListDeepScan"), false).toBool(); + UISettings::values.recent_files = ReadSetting(QStringLiteral("recentFiles")).toStringList(); - qt_config->beginGroup("UILayout"); - UISettings::values.geometry = ReadSetting("geometry").toByteArray(); - UISettings::values.state = ReadSetting("state").toByteArray(); - UISettings::values.renderwindow_geometry = ReadSetting("geometryRenderWindow").toByteArray(); - UISettings::values.gamelist_header_state = ReadSetting("gameListHeaderState").toByteArray(); - UISettings::values.microprofile_geometry = - ReadSetting("microProfileDialogGeometry").toByteArray(); - UISettings::values.microprofile_visible = - ReadSetting("microProfileDialogVisible", false).toBool(); qt_config->endGroup(); +} + +void Config::ReadRendererValues() { + qt_config->beginGroup(QStringLiteral("Renderer")); + + Settings::values.resolution_factor = + ReadSetting(QStringLiteral("resolution_factor"), 1.0).toFloat(); + Settings::values.use_frame_limit = + ReadSetting(QStringLiteral("use_frame_limit"), true).toBool(); + Settings::values.frame_limit = ReadSetting(QStringLiteral("frame_limit"), 100).toInt(); + Settings::values.use_compatibility_profile = + ReadSetting(QStringLiteral("use_compatibility_profile"), true).toBool(); + Settings::values.use_disk_shader_cache = + ReadSetting(QStringLiteral("use_disk_shader_cache"), true).toBool(); + Settings::values.use_accurate_gpu_emulation = + ReadSetting(QStringLiteral("use_accurate_gpu_emulation"), false).toBool(); + Settings::values.use_asynchronous_gpu_emulation = + ReadSetting(QStringLiteral("use_asynchronous_gpu_emulation"), false).toBool(); + Settings::values.force_30fps_mode = + ReadSetting(QStringLiteral("force_30fps_mode"), false).toBool(); + + Settings::values.bg_red = ReadSetting(QStringLiteral("bg_red"), 0.0).toFloat(); + Settings::values.bg_green = ReadSetting(QStringLiteral("bg_green"), 0.0).toFloat(); + Settings::values.bg_blue = ReadSetting(QStringLiteral("bg_blue"), 0.0).toFloat(); - qt_config->beginGroup("Paths"); - UISettings::values.roms_path = ReadSetting("romsPath").toString(); - UISettings::values.symbols_path = ReadSetting("symbolsPath").toString(); - UISettings::values.game_directory_path = ReadSetting("gameListRootDir", ".").toString(); - UISettings::values.game_directory_deepscan = ReadSetting("gameListDeepScan", false).toBool(); - UISettings::values.recent_files = ReadSetting("recentFiles").toStringList(); qt_config->endGroup(); +} + +void Config::ReadShortcutValues() { + qt_config->beginGroup(QStringLiteral("Shortcuts")); - qt_config->beginGroup("Shortcuts"); - for (auto [name, group, shortcut] : default_hotkeys) { - auto [keyseq, context] = shortcut; + for (const auto& [name, group, shortcut] : default_hotkeys) { + const auto& [keyseq, context] = shortcut; qt_config->beginGroup(group); qt_config->beginGroup(name); UISettings::values.shortcuts.push_back( {name, group, - {ReadSetting("KeySeq", keyseq).toString(), ReadSetting("Context", context).toInt()}}); + {ReadSetting(QStringLiteral("KeySeq"), keyseq).toString(), + ReadSetting(QStringLiteral("Context"), context).toInt()}}); qt_config->endGroup(); qt_config->endGroup(); } + + qt_config->endGroup(); +} + +void Config::ReadSystemValues() { + qt_config->beginGroup(QStringLiteral("System")); + + Settings::values.use_docked_mode = + ReadSetting(QStringLiteral("use_docked_mode"), false).toBool(); + + Settings::values.current_user = std::clamp<int>( + ReadSetting(QStringLiteral("current_user"), 0).toInt(), 0, Service::Account::MAX_USERS - 1); + + Settings::values.language_index = ReadSetting(QStringLiteral("language_index"), 1).toInt(); + + const auto rng_seed_enabled = ReadSetting(QStringLiteral("rng_seed_enabled"), false).toBool(); + if (rng_seed_enabled) { + Settings::values.rng_seed = ReadSetting(QStringLiteral("rng_seed"), 0).toULongLong(); + } else { + Settings::values.rng_seed = std::nullopt; + } + + const auto custom_rtc_enabled = + ReadSetting(QStringLiteral("custom_rtc_enabled"), false).toBool(); + if (custom_rtc_enabled) { + Settings::values.custom_rtc = + std::chrono::seconds(ReadSetting(QStringLiteral("custom_rtc"), 0).toULongLong()); + } else { + Settings::values.custom_rtc = std::nullopt; + } + qt_config->endGroup(); +} + +void Config::ReadUIValues() { + qt_config->beginGroup(QStringLiteral("UI")); - UISettings::values.single_window_mode = ReadSetting("singleWindowMode", true).toBool(); - UISettings::values.fullscreen = ReadSetting("fullscreen", false).toBool(); - UISettings::values.display_titlebar = ReadSetting("displayTitleBars", true).toBool(); - UISettings::values.show_filter_bar = ReadSetting("showFilterBar", true).toBool(); - UISettings::values.show_status_bar = ReadSetting("showStatusBar", true).toBool(); - UISettings::values.confirm_before_closing = ReadSetting("confirmClose", true).toBool(); - UISettings::values.first_start = ReadSetting("firstStart", true).toBool(); - UISettings::values.callout_flags = ReadSetting("calloutFlags", 0).toUInt(); - UISettings::values.show_console = ReadSetting("showConsole", false).toBool(); - UISettings::values.profile_index = ReadSetting("profileIndex", 0).toUInt(); + UISettings::values.theme = + ReadSetting(QStringLiteral("theme"), QString::fromUtf8(UISettings::themes[0].second)) + .toString(); + UISettings::values.enable_discord_presence = + ReadSetting(QStringLiteral("enable_discord_presence"), true).toBool(); + UISettings::values.screenshot_resolution_factor = + static_cast<u16>(ReadSetting(QStringLiteral("screenshot_resolution_factor"), 0).toUInt()); + UISettings::values.select_user_on_boot = + ReadSetting(QStringLiteral("select_user_on_boot"), false).toBool(); + + ReadUIGamelistValues(); + ReadUILayoutValues(); + ReadPathValues(); + ReadShortcutValues(); + + UISettings::values.single_window_mode = + ReadSetting(QStringLiteral("singleWindowMode"), true).toBool(); + UISettings::values.fullscreen = ReadSetting(QStringLiteral("fullscreen"), false).toBool(); + UISettings::values.display_titlebar = + ReadSetting(QStringLiteral("displayTitleBars"), true).toBool(); + UISettings::values.show_filter_bar = + ReadSetting(QStringLiteral("showFilterBar"), true).toBool(); + UISettings::values.show_status_bar = + ReadSetting(QStringLiteral("showStatusBar"), true).toBool(); + UISettings::values.confirm_before_closing = + ReadSetting(QStringLiteral("confirmClose"), true).toBool(); + UISettings::values.first_start = ReadSetting(QStringLiteral("firstStart"), true).toBool(); + UISettings::values.callout_flags = ReadSetting(QStringLiteral("calloutFlags"), 0).toUInt(); + UISettings::values.show_console = ReadSetting(QStringLiteral("showConsole"), false).toBool(); + UISettings::values.profile_index = ReadSetting(QStringLiteral("profileIndex"), 0).toUInt(); ApplyDefaultProfileIfInputInvalid(); qt_config->endGroup(); } +void Config::ReadUIGamelistValues() { + qt_config->beginGroup(QStringLiteral("UIGameList")); + + UISettings::values.show_unknown = ReadSetting(QStringLiteral("show_unknown"), true).toBool(); + UISettings::values.show_add_ons = ReadSetting(QStringLiteral("show_add_ons"), true).toBool(); + UISettings::values.icon_size = ReadSetting(QStringLiteral("icon_size"), 64).toUInt(); + UISettings::values.row_1_text_id = ReadSetting(QStringLiteral("row_1_text_id"), 3).toUInt(); + UISettings::values.row_2_text_id = ReadSetting(QStringLiteral("row_2_text_id"), 2).toUInt(); + + qt_config->endGroup(); +} + +void Config::ReadUILayoutValues() { + qt_config->beginGroup(QStringLiteral("UILayout")); + + UISettings::values.geometry = ReadSetting(QStringLiteral("geometry")).toByteArray(); + UISettings::values.state = ReadSetting(QStringLiteral("state")).toByteArray(); + UISettings::values.renderwindow_geometry = + ReadSetting(QStringLiteral("geometryRenderWindow")).toByteArray(); + UISettings::values.gamelist_header_state = + ReadSetting(QStringLiteral("gameListHeaderState")).toByteArray(); + UISettings::values.microprofile_geometry = + ReadSetting(QStringLiteral("microProfileDialogGeometry")).toByteArray(); + UISettings::values.microprofile_visible = + ReadSetting(QStringLiteral("microProfileDialogVisible"), false).toBool(); + + qt_config->endGroup(); +} + +void Config::ReadWebServiceValues() { + qt_config->beginGroup(QStringLiteral("WebService")); + + Settings::values.enable_telemetry = + ReadSetting(QStringLiteral("enable_telemetry"), true).toBool(); + Settings::values.web_api_url = + ReadSetting(QStringLiteral("web_api_url"), QStringLiteral("https://api.yuzu-emu.org")) + .toString() + .toStdString(); + Settings::values.yuzu_username = + ReadSetting(QStringLiteral("yuzu_username")).toString().toStdString(); + Settings::values.yuzu_token = + ReadSetting(QStringLiteral("yuzu_token")).toString().toStdString(); + + qt_config->endGroup(); +} + +void Config::ReadValues() { + ReadControlValues(); + ReadCoreValues(); + ReadRendererValues(); + ReadAudioValues(); + ReadDataStorageValues(); + ReadSystemValues(); + ReadMiscellaneousValues(); + ReadDebugValues(); + ReadWebServiceValues(); + ReadDisabledAddOnValues(); + ReadUIValues(); +} + void Config::SavePlayerValues() { for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { const auto& player = Settings::values.players[p]; - WriteSetting(QString("player_%1_connected").arg(p), player.connected, false); - WriteSetting(QString("player_%1_type").arg(p), static_cast<u8>(player.type), + WriteSetting(QStringLiteral("player_%1_connected").arg(p), player.connected, false); + WriteSetting(QStringLiteral("player_%1_type").arg(p), static_cast<u8>(player.type), static_cast<u8>(Settings::ControllerType::DualJoycon)); - WriteSetting(QString("player_%1_body_color_left").arg(p), player.body_color_left, + WriteSetting(QStringLiteral("player_%1_body_color_left").arg(p), player.body_color_left, Settings::JOYCON_BODY_NEON_BLUE); - WriteSetting(QString("player_%1_body_color_right").arg(p), player.body_color_right, + WriteSetting(QStringLiteral("player_%1_body_color_right").arg(p), player.body_color_right, Settings::JOYCON_BODY_NEON_RED); - WriteSetting(QString("player_%1_button_color_left").arg(p), player.button_color_left, + WriteSetting(QStringLiteral("player_%1_button_color_left").arg(p), player.button_color_left, Settings::JOYCON_BUTTONS_NEON_BLUE); - WriteSetting(QString("player_%1_button_color_right").arg(p), player.button_color_right, - Settings::JOYCON_BUTTONS_NEON_RED); + WriteSetting(QStringLiteral("player_%1_button_color_right").arg(p), + player.button_color_right, Settings::JOYCON_BUTTONS_NEON_RED); for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { - std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); - WriteSetting(QString("player_%1_").arg(p) + + const std::string default_param = + InputCommon::GenerateKeyboardParam(default_buttons[i]); + WriteSetting(QStringLiteral("player_%1_").arg(p) + QString::fromStdString(Settings::NativeButton::mapping[i]), QString::fromStdString(player.buttons[i]), QString::fromStdString(default_param)); } for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { - std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], default_analogs[i][3], default_analogs[i][4], 0.5f); - WriteSetting(QString("player_%1_").arg(p) + + WriteSetting(QStringLiteral("player_%1_").arg(p) + QString::fromStdString(Settings::NativeAnalog::mapping[i]), QString::fromStdString(player.analogs[i]), QString::fromStdString(default_param)); @@ -598,19 +735,19 @@ void Config::SavePlayerValues() { } void Config::SaveDebugValues() { - WriteSetting("debug_pad_enabled", Settings::values.debug_pad_enabled, false); + WriteSetting(QStringLiteral("debug_pad_enabled"), Settings::values.debug_pad_enabled, false); for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { - std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); - WriteSetting(QString("debug_pad_") + + const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); + WriteSetting(QStringLiteral("debug_pad_") + QString::fromStdString(Settings::NativeButton::mapping[i]), QString::fromStdString(Settings::values.debug_pad_buttons[i]), QString::fromStdString(default_param)); } for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { - std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], default_analogs[i][3], default_analogs[i][4], 0.5f); - WriteSetting(QString("debug_pad_") + + WriteSetting(QStringLiteral("debug_pad_") + QString::fromStdString(Settings::NativeAnalog::mapping[i]), QString::fromStdString(Settings::values.debug_pad_analogs[i]), QString::fromStdString(default_param)); @@ -618,11 +755,12 @@ void Config::SaveDebugValues() { } void Config::SaveMouseValues() { - WriteSetting("mouse_enabled", Settings::values.mouse_enabled, false); + WriteSetting(QStringLiteral("mouse_enabled"), Settings::values.mouse_enabled, false); for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { - std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]); - WriteSetting(QString("mouse_") + + const std::string default_param = + InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]); + WriteSetting(QStringLiteral("mouse_") + QString::fromStdString(Settings::NativeMouseButton::mapping[i]), QString::fromStdString(Settings::values.mouse_buttons[i]), QString::fromStdString(default_param)); @@ -630,178 +768,277 @@ void Config::SaveMouseValues() { } void Config::SaveTouchscreenValues() { - WriteSetting("touchscreen_enabled", Settings::values.touchscreen.enabled, true); - WriteSetting("touchscreen_device", QString::fromStdString(Settings::values.touchscreen.device), - "engine:emu_window"); + const auto& touchscreen = Settings::values.touchscreen; + + WriteSetting(QStringLiteral("touchscreen_enabled"), touchscreen.enabled, true); + WriteSetting(QStringLiteral("touchscreen_device"), QString::fromStdString(touchscreen.device), + QStringLiteral("engine:emu_window")); - WriteSetting("touchscreen_finger", Settings::values.touchscreen.finger, 0); - WriteSetting("touchscreen_angle", Settings::values.touchscreen.rotation_angle, 0); - WriteSetting("touchscreen_diameter_x", Settings::values.touchscreen.diameter_x, 15); - WriteSetting("touchscreen_diameter_y", Settings::values.touchscreen.diameter_y, 15); + WriteSetting(QStringLiteral("touchscreen_finger"), touchscreen.finger, 0); + WriteSetting(QStringLiteral("touchscreen_angle"), touchscreen.rotation_angle, 0); + WriteSetting(QStringLiteral("touchscreen_diameter_x"), touchscreen.diameter_x, 15); + WriteSetting(QStringLiteral("touchscreen_diameter_y"), touchscreen.diameter_y, 15); } void Config::SaveValues() { - qt_config->beginGroup("Controls"); + SaveControlValues(); + SaveCoreValues(); + SaveRendererValues(); + SaveAudioValues(); + SaveDataStorageValues(); + SaveSystemValues(); + SaveMiscellaneousValues(); + SaveDebuggingValues(); + SaveWebServiceValues(); + SaveDisabledAddOnValues(); + SaveUIValues(); +} + +void Config::SaveAudioValues() { + qt_config->beginGroup(QStringLiteral("Audio")); + + WriteSetting(QStringLiteral("output_engine"), QString::fromStdString(Settings::values.sink_id), + QStringLiteral("auto")); + WriteSetting(QStringLiteral("enable_audio_stretching"), + Settings::values.enable_audio_stretching, true); + WriteSetting(QStringLiteral("output_device"), + QString::fromStdString(Settings::values.audio_device_id), QStringLiteral("auto")); + WriteSetting(QStringLiteral("volume"), Settings::values.volume, 1.0f); + + qt_config->endGroup(); +} + +void Config::SaveControlValues() { + qt_config->beginGroup(QStringLiteral("Controls")); SavePlayerValues(); SaveDebugValues(); SaveMouseValues(); SaveTouchscreenValues(); - WriteSetting("motion_device", QString::fromStdString(Settings::values.motion_device), - "engine:motion_emu,update_period:100,sensitivity:0.01"); - WriteSetting("keyboard_enabled", Settings::values.keyboard_enabled, false); + WriteSetting(QStringLiteral("motion_device"), + QString::fromStdString(Settings::values.motion_device), + QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01")); + WriteSetting(QStringLiteral("keyboard_enabled"), Settings::values.keyboard_enabled, false); qt_config->endGroup(); +} - qt_config->beginGroup("Core"); - WriteSetting("use_cpu_jit", Settings::values.use_cpu_jit, true); - WriteSetting("use_multi_core", Settings::values.use_multi_core, false); - qt_config->endGroup(); +void Config::SaveCoreValues() { + qt_config->beginGroup(QStringLiteral("Core")); - qt_config->beginGroup("Renderer"); - WriteSetting("resolution_factor", (double)Settings::values.resolution_factor, 1.0); - WriteSetting("use_frame_limit", Settings::values.use_frame_limit, true); - WriteSetting("frame_limit", Settings::values.frame_limit, 100); - WriteSetting("use_compatibility_profile", Settings::values.use_compatibility_profile, true); - WriteSetting("use_disk_shader_cache", Settings::values.use_disk_shader_cache, true); - WriteSetting("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation, false); - WriteSetting("use_asynchronous_gpu_emulation", Settings::values.use_asynchronous_gpu_emulation, - false); - WriteSetting("force_30fps_mode", Settings::values.force_30fps_mode, false); + WriteSetting(QStringLiteral("use_cpu_jit"), Settings::values.use_cpu_jit, true); + WriteSetting(QStringLiteral("use_multi_core"), Settings::values.use_multi_core, false); - // Cast to double because Qt's written float values are not human-readable - WriteSetting("bg_red", (double)Settings::values.bg_red, 0.0); - WriteSetting("bg_green", (double)Settings::values.bg_green, 0.0); - WriteSetting("bg_blue", (double)Settings::values.bg_blue, 0.0); qt_config->endGroup(); +} - qt_config->beginGroup("Audio"); - WriteSetting("output_engine", QString::fromStdString(Settings::values.sink_id), "auto"); - WriteSetting("enable_audio_stretching", Settings::values.enable_audio_stretching, true); - WriteSetting("output_device", QString::fromStdString(Settings::values.audio_device_id), "auto"); - WriteSetting("volume", Settings::values.volume, 1.0f); - qt_config->endGroup(); +void Config::SaveDataStorageValues() { + qt_config->beginGroup(QStringLiteral("Data Storage")); - qt_config->beginGroup("Data Storage"); - WriteSetting("use_virtual_sd", Settings::values.use_virtual_sd, true); - WriteSetting("nand_directory", + WriteSetting(QStringLiteral("use_virtual_sd"), Settings::values.use_virtual_sd, true); + WriteSetting(QStringLiteral("nand_directory"), QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)), QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))); - WriteSetting("sdmc_directory", + WriteSetting(QStringLiteral("sdmc_directory"), QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)), QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); - qt_config->endGroup(); - - qt_config->beginGroup("System"); - WriteSetting("use_docked_mode", Settings::values.use_docked_mode, false); - WriteSetting("current_user", Settings::values.current_user, 0); - WriteSetting("language_index", Settings::values.language_index, 1); - - WriteSetting("rng_seed_enabled", Settings::values.rng_seed.has_value(), false); - WriteSetting("rng_seed", Settings::values.rng_seed.value_or(0), 0); - - WriteSetting("custom_rtc_enabled", Settings::values.custom_rtc.has_value(), false); - WriteSetting("custom_rtc", - QVariant::fromValue<long long>( - Settings::values.custom_rtc.value_or(std::chrono::seconds{}).count()), - 0); qt_config->endGroup(); +} - qt_config->beginGroup("Miscellaneous"); - WriteSetting("log_filter", QString::fromStdString(Settings::values.log_filter), "*:Info"); - WriteSetting("use_dev_keys", Settings::values.use_dev_keys, false); - qt_config->endGroup(); +void Config::SaveDebuggingValues() { + qt_config->beginGroup(QStringLiteral("Debugging")); - qt_config->beginGroup("Debugging"); - WriteSetting("use_gdbstub", Settings::values.use_gdbstub, false); - WriteSetting("gdbstub_port", Settings::values.gdbstub_port, 24689); - WriteSetting("program_args", QString::fromStdString(Settings::values.program_args), ""); - WriteSetting("dump_exefs", Settings::values.dump_exefs, false); - WriteSetting("dump_nso", Settings::values.dump_nso, false); - qt_config->endGroup(); + WriteSetting(QStringLiteral("use_gdbstub"), Settings::values.use_gdbstub, false); + WriteSetting(QStringLiteral("gdbstub_port"), Settings::values.gdbstub_port, 24689); + WriteSetting(QStringLiteral("program_args"), + QString::fromStdString(Settings::values.program_args), QStringLiteral("")); + WriteSetting(QStringLiteral("dump_exefs"), Settings::values.dump_exefs, false); + WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false); - qt_config->beginGroup("WebService"); - WriteSetting("enable_telemetry", Settings::values.enable_telemetry, true); - WriteSetting("web_api_url", QString::fromStdString(Settings::values.web_api_url), - "https://api.yuzu-emu.org"); - WriteSetting("yuzu_username", QString::fromStdString(Settings::values.yuzu_username)); - WriteSetting("yuzu_token", QString::fromStdString(Settings::values.yuzu_token)); qt_config->endGroup(); +} + +void Config::SaveDisabledAddOnValues() { + qt_config->beginWriteArray(QStringLiteral("DisabledAddOns")); - qt_config->beginWriteArray("DisabledAddOns"); int i = 0; for (const auto& elem : Settings::values.disabled_addons) { qt_config->setArrayIndex(i); - WriteSetting("title_id", QVariant::fromValue<u64>(elem.first), 0); - qt_config->beginWriteArray("disabled"); + WriteSetting(QStringLiteral("title_id"), QVariant::fromValue<u64>(elem.first), 0); + qt_config->beginWriteArray(QStringLiteral("disabled")); for (std::size_t j = 0; j < elem.second.size(); ++j) { qt_config->setArrayIndex(static_cast<int>(j)); - WriteSetting("d", QString::fromStdString(elem.second[j]), ""); + WriteSetting(QStringLiteral("d"), QString::fromStdString(elem.second[j]), + QStringLiteral("")); } qt_config->endArray(); ++i; } + qt_config->endArray(); +} + +void Config::SaveMiscellaneousValues() { + qt_config->beginGroup(QStringLiteral("Miscellaneous")); + + WriteSetting(QStringLiteral("log_filter"), QString::fromStdString(Settings::values.log_filter), + QStringLiteral("*:Info")); + WriteSetting(QStringLiteral("use_dev_keys"), Settings::values.use_dev_keys, false); - qt_config->beginGroup("UI"); - WriteSetting("theme", UISettings::values.theme, UISettings::themes[0].second); - WriteSetting("enable_discord_presence", UISettings::values.enable_discord_presence, true); - WriteSetting("screenshot_resolution_factor", UISettings::values.screenshot_resolution_factor, - 0); - WriteSetting("select_user_on_boot", UISettings::values.select_user_on_boot, false); - - qt_config->beginGroup("UIGameList"); - WriteSetting("show_unknown", UISettings::values.show_unknown, true); - WriteSetting("show_add_ons", UISettings::values.show_add_ons, true); - WriteSetting("icon_size", UISettings::values.icon_size, 64); - WriteSetting("row_1_text_id", UISettings::values.row_1_text_id, 3); - WriteSetting("row_2_text_id", UISettings::values.row_2_text_id, 2); qt_config->endGroup(); +} + +void Config::SavePathValues() { + qt_config->beginGroup(QStringLiteral("Paths")); + + WriteSetting(QStringLiteral("romsPath"), UISettings::values.roms_path); + WriteSetting(QStringLiteral("symbolsPath"), UISettings::values.symbols_path); + WriteSetting(QStringLiteral("screenshotPath"), UISettings::values.screenshot_path); + WriteSetting(QStringLiteral("gameListRootDir"), UISettings::values.game_directory_path, + QStringLiteral(".")); + WriteSetting(QStringLiteral("gameListDeepScan"), UISettings::values.game_directory_deepscan, + false); + WriteSetting(QStringLiteral("recentFiles"), UISettings::values.recent_files); - qt_config->beginGroup("UILayout"); - WriteSetting("geometry", UISettings::values.geometry); - WriteSetting("state", UISettings::values.state); - WriteSetting("geometryRenderWindow", UISettings::values.renderwindow_geometry); - WriteSetting("gameListHeaderState", UISettings::values.gamelist_header_state); - WriteSetting("microProfileDialogGeometry", UISettings::values.microprofile_geometry); - WriteSetting("microProfileDialogVisible", UISettings::values.microprofile_visible, false); qt_config->endGroup(); +} + +void Config::SaveRendererValues() { + qt_config->beginGroup(QStringLiteral("Renderer")); + + WriteSetting(QStringLiteral("resolution_factor"), + static_cast<double>(Settings::values.resolution_factor), 1.0); + WriteSetting(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true); + WriteSetting(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100); + WriteSetting(QStringLiteral("use_compatibility_profile"), + Settings::values.use_compatibility_profile, true); + WriteSetting(QStringLiteral("use_disk_shader_cache"), Settings::values.use_disk_shader_cache, + true); + WriteSetting(QStringLiteral("use_accurate_gpu_emulation"), + Settings::values.use_accurate_gpu_emulation, false); + WriteSetting(QStringLiteral("use_asynchronous_gpu_emulation"), + Settings::values.use_asynchronous_gpu_emulation, false); + WriteSetting(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode, false); + + // Cast to double because Qt's written float values are not human-readable + WriteSetting(QStringLiteral("bg_red"), static_cast<double>(Settings::values.bg_red), 0.0); + WriteSetting(QStringLiteral("bg_green"), static_cast<double>(Settings::values.bg_green), 0.0); + WriteSetting(QStringLiteral("bg_blue"), static_cast<double>(Settings::values.bg_blue), 0.0); - qt_config->beginGroup("Paths"); - WriteSetting("romsPath", UISettings::values.roms_path); - WriteSetting("symbolsPath", UISettings::values.symbols_path); - WriteSetting("screenshotPath", UISettings::values.screenshot_path); - WriteSetting("gameListRootDir", UISettings::values.game_directory_path, "."); - WriteSetting("gameListDeepScan", UISettings::values.game_directory_deepscan, false); - WriteSetting("recentFiles", UISettings::values.recent_files); qt_config->endGroup(); +} + +void Config::SaveShortcutValues() { + qt_config->beginGroup(QStringLiteral("Shortcuts")); - qt_config->beginGroup("Shortcuts"); // Lengths of UISettings::values.shortcuts & default_hotkeys are same. // However, their ordering must also be the same. for (std::size_t i = 0; i < default_hotkeys.size(); i++) { - auto [name, group, shortcut] = UISettings::values.shortcuts[i]; + const auto& [name, group, shortcut] = UISettings::values.shortcuts[i]; + const auto& default_hotkey = default_hotkeys[i].shortcut; + qt_config->beginGroup(group); qt_config->beginGroup(name); - WriteSetting("KeySeq", shortcut.first, default_hotkeys[i].shortcut.first); - WriteSetting("Context", shortcut.second, default_hotkeys[i].shortcut.second); + WriteSetting(QStringLiteral("KeySeq"), shortcut.first, default_hotkey.first); + WriteSetting(QStringLiteral("Context"), shortcut.second, default_hotkey.second); qt_config->endGroup(); qt_config->endGroup(); } + + qt_config->endGroup(); +} + +void Config::SaveSystemValues() { + qt_config->beginGroup(QStringLiteral("System")); + + WriteSetting(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false); + WriteSetting(QStringLiteral("current_user"), Settings::values.current_user, 0); + WriteSetting(QStringLiteral("language_index"), Settings::values.language_index, 1); + + WriteSetting(QStringLiteral("rng_seed_enabled"), Settings::values.rng_seed.has_value(), false); + WriteSetting(QStringLiteral("rng_seed"), Settings::values.rng_seed.value_or(0), 0); + + WriteSetting(QStringLiteral("custom_rtc_enabled"), Settings::values.custom_rtc.has_value(), + false); + WriteSetting(QStringLiteral("custom_rtc"), + QVariant::fromValue<long long>( + Settings::values.custom_rtc.value_or(std::chrono::seconds{}).count()), + 0); + + qt_config->endGroup(); +} + +void Config::SaveUIValues() { + qt_config->beginGroup(QStringLiteral("UI")); + + WriteSetting(QStringLiteral("theme"), UISettings::values.theme, + QString::fromUtf8(UISettings::themes[0].second)); + WriteSetting(QStringLiteral("enable_discord_presence"), + UISettings::values.enable_discord_presence, true); + WriteSetting(QStringLiteral("screenshot_resolution_factor"), + UISettings::values.screenshot_resolution_factor, 0); + WriteSetting(QStringLiteral("select_user_on_boot"), UISettings::values.select_user_on_boot, + false); + + SaveUIGamelistValues(); + SaveUILayoutValues(); + SavePathValues(); + SaveShortcutValues(); + + WriteSetting(QStringLiteral("singleWindowMode"), UISettings::values.single_window_mode, true); + WriteSetting(QStringLiteral("fullscreen"), UISettings::values.fullscreen, false); + WriteSetting(QStringLiteral("displayTitleBars"), UISettings::values.display_titlebar, true); + WriteSetting(QStringLiteral("showFilterBar"), UISettings::values.show_filter_bar, true); + WriteSetting(QStringLiteral("showStatusBar"), UISettings::values.show_status_bar, true); + WriteSetting(QStringLiteral("confirmClose"), UISettings::values.confirm_before_closing, true); + WriteSetting(QStringLiteral("firstStart"), UISettings::values.first_start, true); + WriteSetting(QStringLiteral("calloutFlags"), UISettings::values.callout_flags, 0); + WriteSetting(QStringLiteral("showConsole"), UISettings::values.show_console, false); + WriteSetting(QStringLiteral("profileIndex"), UISettings::values.profile_index, 0); + + qt_config->endGroup(); +} + +void Config::SaveUIGamelistValues() { + qt_config->beginGroup(QStringLiteral("UIGameList")); + + WriteSetting(QStringLiteral("show_unknown"), UISettings::values.show_unknown, true); + WriteSetting(QStringLiteral("show_add_ons"), UISettings::values.show_add_ons, true); + WriteSetting(QStringLiteral("icon_size"), UISettings::values.icon_size, 64); + WriteSetting(QStringLiteral("row_1_text_id"), UISettings::values.row_1_text_id, 3); + WriteSetting(QStringLiteral("row_2_text_id"), UISettings::values.row_2_text_id, 2); + + qt_config->endGroup(); +} + +void Config::SaveUILayoutValues() { + qt_config->beginGroup(QStringLiteral("UILayout")); + + WriteSetting(QStringLiteral("geometry"), UISettings::values.geometry); + WriteSetting(QStringLiteral("state"), UISettings::values.state); + WriteSetting(QStringLiteral("geometryRenderWindow"), UISettings::values.renderwindow_geometry); + WriteSetting(QStringLiteral("gameListHeaderState"), UISettings::values.gamelist_header_state); + WriteSetting(QStringLiteral("microProfileDialogGeometry"), + UISettings::values.microprofile_geometry); + WriteSetting(QStringLiteral("microProfileDialogVisible"), + UISettings::values.microprofile_visible, false); + qt_config->endGroup(); +} + +void Config::SaveWebServiceValues() { + qt_config->beginGroup(QStringLiteral("WebService")); + + WriteSetting(QStringLiteral("enable_telemetry"), Settings::values.enable_telemetry, true); + WriteSetting(QStringLiteral("web_api_url"), + QString::fromStdString(Settings::values.web_api_url), + QStringLiteral("https://api.yuzu-emu.org")); + WriteSetting(QStringLiteral("yuzu_username"), + QString::fromStdString(Settings::values.yuzu_username)); + WriteSetting(QStringLiteral("yuzu_token"), QString::fromStdString(Settings::values.yuzu_token)); - WriteSetting("singleWindowMode", UISettings::values.single_window_mode, true); - WriteSetting("fullscreen", UISettings::values.fullscreen, false); - WriteSetting("displayTitleBars", UISettings::values.display_titlebar, true); - WriteSetting("showFilterBar", UISettings::values.show_filter_bar, true); - WriteSetting("showStatusBar", UISettings::values.show_status_bar, true); - WriteSetting("confirmClose", UISettings::values.confirm_before_closing, true); - WriteSetting("firstStart", UISettings::values.first_start, true); - WriteSetting("calloutFlags", UISettings::values.callout_flags, 0); - WriteSetting("showConsole", UISettings::values.show_console, false); - WriteSetting("profileIndex", UISettings::values.profile_index, 0); qt_config->endGroup(); } @@ -811,7 +1048,7 @@ QVariant Config::ReadSetting(const QString& name) const { QVariant Config::ReadSetting(const QString& name, const QVariant& default_value) const { QVariant result; - if (qt_config->value(name + "/default", false).toBool()) { + if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) { result = default_value; } else { result = qt_config->value(name, default_value); @@ -825,7 +1062,7 @@ void Config::WriteSetting(const QString& name, const QVariant& value) { void Config::WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value) { - qt_config->setValue(name + "/default", value == default_value); + qt_config->setValue(name + QStringLiteral("/default"), value == default_value); qt_config->setValue(name, value); } diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index 221d2364c..6b523ecdd 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -9,7 +9,6 @@ #include <string> #include <QVariant> #include "core/settings.h" -#include "yuzu/ui_settings.h" class QSettings; @@ -37,19 +36,51 @@ private: void ReadTouchscreenValues(); void ApplyDefaultProfileIfInputInvalid(); + // Read functions bases off the respective config section names. + void ReadAudioValues(); + void ReadControlValues(); + void ReadCoreValues(); + void ReadDataStorageValues(); + void ReadDebuggingValues(); + void ReadDisabledAddOnValues(); + void ReadMiscellaneousValues(); + void ReadPathValues(); + void ReadRendererValues(); + void ReadShortcutValues(); + void ReadSystemValues(); + void ReadUIValues(); + void ReadUIGamelistValues(); + void ReadUILayoutValues(); + void ReadWebServiceValues(); + void SaveValues(); void SavePlayerValues(); void SaveDebugValues(); void SaveMouseValues(); void SaveTouchscreenValues(); + // Save functions based off the respective config section names. + void SaveAudioValues(); + void SaveControlValues(); + void SaveCoreValues(); + void SaveDataStorageValues(); + void SaveDebuggingValues(); + void SaveDisabledAddOnValues(); + void SaveMiscellaneousValues(); + void SavePathValues(); + void SaveRendererValues(); + void SaveShortcutValues(); + void SaveSystemValues(); + void SaveUIValues(); + void SaveUIGamelistValues(); + void SaveUILayoutValues(); + void SaveWebServiceValues(); + QVariant ReadSetting(const QString& name) const; QVariant ReadSetting(const QString& name, const QVariant& default_value) const; void WriteSetting(const QString& name, const QVariant& value); void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value); - static const std::array<UISettings::Shortcut, 15> default_hotkeys; - std::unique_ptr<QSettings> qt_config; std::string qt_config_loc; }; diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index 32c05b797..8086f9d6b 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp @@ -25,9 +25,6 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry) adjustSize(); ui->selectorList->setCurrentRow(0); - - // Synchronise lists upon initialisation - ui->hotkeysTab->EmitHotkeysChanged(); } ConfigureDialog::~ConfigureDialog() = default; diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp index a7a8752e5..9fb970c21 100644 --- a/src/yuzu/configuration/configure_hotkeys.cpp +++ b/src/yuzu/configuration/configure_hotkeys.cpp @@ -31,22 +31,6 @@ ConfigureHotkeys::ConfigureHotkeys(QWidget* parent) ConfigureHotkeys::~ConfigureHotkeys() = default; -void ConfigureHotkeys::EmitHotkeysChanged() { - emit HotkeysChanged(GetUsedKeyList()); -} - -QList<QKeySequence> ConfigureHotkeys::GetUsedKeyList() const { - QList<QKeySequence> list; - for (int r = 0; r < model->rowCount(); r++) { - const QStandardItem* parent = model->item(r, 0); - for (int r2 = 0; r2 < parent->rowCount(); r2++) { - const QStandardItem* keyseq = parent->child(r2, 1); - list << QKeySequence::fromString(keyseq->text(), QKeySequence::NativeText); - } - } - return list; -} - void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) { for (const auto& group : registry.hotkey_groups) { auto* parent_item = new QStandardItem(group.first); @@ -83,16 +67,29 @@ void ConfigureHotkeys::Configure(QModelIndex index) { } if (IsUsedKey(key_sequence) && key_sequence != QKeySequence(previous_key.toString())) { - QMessageBox::critical(this, tr("Error in inputted key"), - tr("You're using a key that's already bound.")); + QMessageBox::warning(this, tr("Conflicting Key Sequence"), + tr("The entered key sequence is already assigned to another hotkey.")); } else { model->setData(index, key_sequence.toString(QKeySequence::NativeText)); - EmitHotkeysChanged(); } } bool ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence) const { - return GetUsedKeyList().contains(key_sequence); + for (int r = 0; r < model->rowCount(); r++) { + const QStandardItem* const parent = model->item(r, 0); + + for (int r2 = 0; r2 < parent->rowCount(); r2++) { + const QStandardItem* const key_seq_item = parent->child(r2, 1); + const auto key_seq_str = key_seq_item->text(); + const auto key_seq = QKeySequence::fromString(key_seq_str, QKeySequence::NativeText); + + if (key_sequence == key_seq) { + return true; + } + } + } + + return false; } void ConfigureHotkeys::applyConfiguration(HotkeyRegistry& registry) { @@ -114,7 +111,6 @@ void ConfigureHotkeys::applyConfiguration(HotkeyRegistry& registry) { } registry.SaveHotkeys(); - Settings::Apply(); } void ConfigureHotkeys::retranslateUi() { diff --git a/src/yuzu/configuration/configure_hotkeys.h b/src/yuzu/configuration/configure_hotkeys.h index 73fb8a175..e77d73c35 100644 --- a/src/yuzu/configuration/configure_hotkeys.h +++ b/src/yuzu/configuration/configure_hotkeys.h @@ -24,8 +24,6 @@ public: void applyConfiguration(HotkeyRegistry& registry); void retranslateUi(); - void EmitHotkeysChanged(); - /** * Populates the hotkey list widget using data from the provided registry. * Called everytime the Configure dialog is opened. @@ -33,13 +31,9 @@ public: */ void Populate(const HotkeyRegistry& registry); -signals: - void HotkeysChanged(QList<QKeySequence> new_key_list); - private: void Configure(QModelIndex index); bool IsUsedKey(QKeySequence key_sequence) const; - QList<QKeySequence> GetUsedKeyList() const; std::unique_ptr<Ui::ConfigureHotkeys> ui; diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index b0ca766ec..83d675773 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -14,7 +14,6 @@ #include <QMenu> #include <QThreadPool> #include <fmt/format.h> -#include "common/common_paths.h" #include "common/common_types.h" #include "common/logging/log.h" #include "core/file_sys/patch_manager.h" @@ -48,7 +47,7 @@ bool GameListSearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* eve return QObject::eventFilter(obj, event); } else { gamelist->search_field->edit_filter->clear(); - edit_filter_text = ""; + edit_filter_text.clear(); } break; } @@ -71,9 +70,9 @@ bool GameListSearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* eve } if (resultCount == 1) { // To avoid loading error dialog loops while confirming them using enter - // Also users usually want to run a diffrent game after closing one - gamelist->search_field->edit_filter->setText(""); - edit_filter_text = ""; + // Also users usually want to run a different game after closing one + gamelist->search_field->edit_filter->clear(); + edit_filter_text.clear(); emit gamelist->GameChosen(file_path); } else { return QObject::eventFilter(obj, event); @@ -93,7 +92,7 @@ void GameListSearchField::setFilterResult(int visible, int total) { } void GameListSearchField::clear() { - edit_filter->setText(""); + edit_filter->clear(); } void GameListSearchField::setFocus() { @@ -103,25 +102,26 @@ void GameListSearchField::setFocus() { } GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} { - KeyReleaseEater* keyReleaseEater = new KeyReleaseEater(parent); + auto* const key_release_eater = new KeyReleaseEater(parent); layout_filter = new QHBoxLayout; layout_filter->setMargin(8); label_filter = new QLabel; label_filter->setText(tr("Filter:")); edit_filter = new QLineEdit; - edit_filter->setText(""); + edit_filter->clear(); edit_filter->setPlaceholderText(tr("Enter pattern to filter")); - edit_filter->installEventFilter(keyReleaseEater); + edit_filter->installEventFilter(key_release_eater); edit_filter->setClearButtonEnabled(true); connect(edit_filter, &QLineEdit::textChanged, parent, &GameList::onTextChanged); label_filter_result = new QLabel; button_filter_close = new QToolButton(this); - button_filter_close->setText("X"); + button_filter_close->setText(QStringLiteral("X")); button_filter_close->setCursor(Qt::ArrowCursor); - button_filter_close->setStyleSheet("QToolButton{ border: none; padding: 0px; color: " - "#000000; font-weight: bold; background: #F0F0F0; }" - "QToolButton:hover{ border: none; padding: 0px; color: " - "#EEEEEE; font-weight: bold; background: #E81123}"); + button_filter_close->setStyleSheet( + QStringLiteral("QToolButton{ border: none; padding: 0px; color: " + "#000000; font-weight: bold; background: #F0F0F0; }" + "QToolButton:hover{ border: none; padding: 0px; color: " + "#EEEEEE; font-weight: bold; background: #E81123}")); connect(button_filter_close, &QToolButton::clicked, parent, &GameList::onFilterCloseClicked); layout_filter->setSpacing(10); layout_filter->addWidget(label_filter); @@ -141,36 +141,34 @@ GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} { */ static bool ContainsAllWords(const QString& haystack, const QString& userinput) { const QStringList userinput_split = - userinput.split(' ', QString::SplitBehavior::SkipEmptyParts); + userinput.split(QLatin1Char{' '}, QString::SplitBehavior::SkipEmptyParts); return std::all_of(userinput_split.begin(), userinput_split.end(), [&haystack](const QString& s) { return haystack.contains(s); }); } // Event in order to filter the gamelist after editing the searchfield -void GameList::onTextChanged(const QString& newText) { - int rowCount = tree_view->model()->rowCount(); - QString edit_filter_text = newText.toLower(); - - QModelIndex root_index = item_model->invisibleRootItem()->index(); +void GameList::onTextChanged(const QString& new_text) { + const int row_count = tree_view->model()->rowCount(); + const QString edit_filter_text = new_text.toLower(); + const QModelIndex root_index = item_model->invisibleRootItem()->index(); // If the searchfield is empty every item is visible // Otherwise the filter gets applied if (edit_filter_text.isEmpty()) { - for (int i = 0; i < rowCount; ++i) { + for (int i = 0; i < row_count; ++i) { tree_view->setRowHidden(i, root_index, false); } - search_field->setFilterResult(rowCount, rowCount); + search_field->setFilterResult(row_count, row_count); } else { int result_count = 0; - for (int i = 0; i < rowCount; ++i) { + for (int i = 0; i < row_count; ++i) { const QStandardItem* child_file = item_model->item(i, 0); const QString file_path = child_file->data(GameListItemPath::FullPathRole).toString().toLower(); - QString file_name = file_path.mid(file_path.lastIndexOf('/') + 1); const QString file_title = child_file->data(GameListItemPath::TitleRole).toString().toLower(); - const QString file_programmid = + const QString file_program_id = child_file->data(GameListItemPath::ProgramIdRole).toString().toLower(); // Only items which filename in combination with its title contains all words @@ -178,14 +176,16 @@ void GameList::onTextChanged(const QString& newText) { // The search is case insensitive because of toLower() // I decided not to use Qt::CaseInsensitive in containsAllWords to prevent // multiple conversions of edit_filter_text for each game in the gamelist - if (ContainsAllWords(file_name.append(' ').append(file_title), edit_filter_text) || - (file_programmid.count() == 16 && edit_filter_text.contains(file_programmid))) { + const QString file_name = file_path.mid(file_path.lastIndexOf(QLatin1Char{'/'}) + 1) + + QLatin1Char{' '} + file_title; + if (ContainsAllWords(file_name, edit_filter_text) || + (file_program_id.count() == 16 && edit_filter_text.contains(file_program_id))) { tree_view->setRowHidden(i, root_index, false); ++result_count; } else { tree_view->setRowHidden(i, root_index, true); } - search_field->setFilterResult(result_count, rowCount); + search_field->setFilterResult(result_count, row_count); } } } @@ -216,7 +216,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvide tree_view->setEditTriggers(QHeaderView::NoEditTriggers); tree_view->setUniformRowHeights(true); tree_view->setContextMenuPolicy(Qt::CustomContextMenu); - tree_view->setStyleSheet("QTreeView{ border: none; }"); + tree_view->setStyleSheet(QStringLiteral("QTreeView{ border: none; }")); item_model->insertColumns(0, UISettings::values.show_add_ons ? COLUMN_COUNT : COLUMN_COUNT - 1); item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name")); @@ -282,9 +282,9 @@ void GameList::ValidateEntry(const QModelIndex& item) { const QFileInfo file_info{file_path}; if (file_info.isDir()) { const QDir dir{file_path}; - const QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files); + const QStringList matching_main = dir.entryList({QStringLiteral("main")}, QDir::Files); if (matching_main.size() == 1) { - emit GameChosen(dir.path() + DIR_SEP + matching_main[0]); + emit GameChosen(dir.path() + QDir::separator() + matching_main[0]); } return; } @@ -360,7 +360,7 @@ void GameList::PopupContextMenu(const QPoint& menu_location) { } void GameList::LoadCompatibilityList() { - QFile compat_list{":compatibility_list/compatibility_list.json"}; + QFile compat_list{QStringLiteral(":compatibility_list/compatibility_list.json")}; if (!compat_list.open(QFile::ReadOnly | QFile::Text)) { LOG_ERROR(Frontend, "Unable to open game compatibility list"); @@ -378,25 +378,27 @@ void GameList::LoadCompatibilityList() { return; } - const QString string_content = content; - QJsonDocument json = QJsonDocument::fromJson(string_content.toUtf8()); - QJsonArray arr = json.array(); + const QJsonDocument json = QJsonDocument::fromJson(content); + const QJsonArray arr = json.array(); - for (const QJsonValueRef value : arr) { - QJsonObject game = value.toObject(); + for (const QJsonValue value : arr) { + const QJsonObject game = value.toObject(); + const QString compatibility_key = QStringLiteral("compatibility"); - if (game.contains("compatibility") && game["compatibility"].isDouble()) { - int compatibility = game["compatibility"].toInt(); - QString directory = game["directory"].toString(); - QJsonArray ids = game["releases"].toArray(); + if (!game.contains(compatibility_key) || !game[compatibility_key].isDouble()) { + continue; + } - for (const QJsonValueRef id_ref : ids) { - QJsonObject id_object = id_ref.toObject(); - QString id = id_object["id"].toString(); - compatibility_list.emplace( - id.toUpper().toStdString(), - std::make_pair(QString::number(compatibility), directory)); - } + const int compatibility = game[compatibility_key].toInt(); + const QString directory = game[QStringLiteral("directory")].toString(); + const QJsonArray ids = game[QStringLiteral("releases")].toArray(); + + for (const QJsonValue id_ref : ids) { + const QJsonObject id_object = id_ref.toObject(); + const QString id = id_object[QStringLiteral("id")].toString(); + + compatibility_list.emplace(id.toUpper().toStdString(), + std::make_pair(QString::number(compatibility), directory)); } } } @@ -464,7 +466,10 @@ void GameList::LoadInterfaceLayout() { item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder()); } -const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci", "nsp"}; +const QStringList GameList::supported_file_extensions = { + QStringLiteral("nso"), QStringLiteral("nro"), QStringLiteral("nca"), + QStringLiteral("xci"), QStringLiteral("nsp"), +}; void GameList::RefreshGameDirectory() { if (!UISettings::values.game_directory_path.isEmpty() && current_worker != nullptr) { diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index 56007eef8..f8f8bd6c5 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -76,7 +76,7 @@ signals: void OpenPerGameGeneralRequested(const std::string& file); private slots: - void onTextChanged(const QString& newText); + void onTextChanged(const QString& new_text); void onFilterCloseClicked(); private: diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index 2cf5c58a0..0b458ef48 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h @@ -4,11 +4,9 @@ #pragma once -#include <algorithm> #include <array> #include <map> #include <string> -#include <unordered_map> #include <utility> #include <QCoreApplication> @@ -25,8 +23,8 @@ #include "yuzu/util/util.h" /** - * Gets the default icon (for games without valid SMDH) - * @param large If true, returns large icon (48x48), otherwise returns small icon (24x24) + * Gets the default icon (for games without valid title metadata) + * @param size The desired width and height of the default icon. * @return QPixmap default icon */ static QPixmap GetDefaultIcon(u32 size) { @@ -46,7 +44,7 @@ public: * A specialization of GameListItem for path values. * This class ensures that for every full path value it holds, a correct string representation * of just the filename (with no extension) will be displayed to the user. - * If this class receives valid SMDH data, it will also display game icons and titles. + * If this class receives valid title metadata, it will also display game icons and titles. */ class GameListItemPath : public GameListItem { public: @@ -95,7 +93,7 @@ public: if (row2.isEmpty()) return row1; - return QString(row1 + "\n " + row2); + return QString(row1 + QStringLiteral("\n ") + row2); } return GameListItem::data(role); @@ -115,13 +113,14 @@ public: }; // clang-format off static const std::map<QString, CompatStatus> status_data = { - {"0", {"#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.")}}, - {"1", {"#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.")}}, - {"2", {"#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.")}}, - {"3", {"#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.")}}, - {"4", {"#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.")}}, - {"5", {"#828282", QT_TR_NOOP("Won't Boot"), QT_TR_NOOP("The game crashes when attempting to startup.")}}, - {"99", {"#000000", QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}}}; + {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("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.")}}, + }; // clang-format on auto iterator = status_data.find(compatibility); diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 8687e7c5a..82d2826ba 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp @@ -45,7 +45,7 @@ bool HasSupportedFileExtension(const std::string& file_name) { } bool IsExtractedNCAMain(const std::string& file_name) { - return QFileInfo(QString::fromStdString(file_name)).fileName() == "main"; + return QFileInfo(QString::fromStdString(file_name)).fileName() == QStringLiteral("main"); } QString FormatGameName(const std::string& physical_name) { @@ -97,7 +97,7 @@ QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::stri const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); // The game list uses this as compatibility number for untested games - QString compatibility{"99"}; + QString compatibility{QStringLiteral("99")}; if (it != compatibility_list.end()) { compatibility = it->second.first; } diff --git a/src/yuzu/loading_screen.cpp b/src/yuzu/loading_screen.cpp index 4e2d988cd..4f2bfab48 100644 --- a/src/yuzu/loading_screen.cpp +++ b/src/yuzu/loading_screen.cpp @@ -30,11 +30,11 @@ #include <QMovie> #endif -constexpr const char PROGRESSBAR_STYLE_PREPARE[] = R"( +constexpr char PROGRESSBAR_STYLE_PREPARE[] = R"( QProgressBar {} QProgressBar::chunk {})"; -constexpr const char PROGRESSBAR_STYLE_DECOMPILE[] = R"( +constexpr char PROGRESSBAR_STYLE_DECOMPILE[] = R"( QProgressBar { background-color: black; border: 2px solid white; @@ -46,7 +46,7 @@ QProgressBar::chunk { width: 1px; })"; -constexpr const char PROGRESSBAR_STYLE_BUILD[] = R"( +constexpr char PROGRESSBAR_STYLE_BUILD[] = R"( QProgressBar { background-color: black; border: 2px solid white; @@ -58,7 +58,7 @@ QProgressBar::chunk { width: 1px; })"; -constexpr const char PROGRESSBAR_STYLE_COMPLETE[] = R"( +constexpr char PROGRESSBAR_STYLE_COMPLETE[] = R"( QProgressBar { background-color: #0ab9e6; border: 2px solid white; @@ -149,10 +149,10 @@ void LoadingScreen::OnLoadComplete() { void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) { using namespace std::chrono; - auto now = high_resolution_clock::now(); + const auto now = high_resolution_clock::now(); // reset the timer if the stage changes if (stage != previous_stage) { - ui->progress_bar->setStyleSheet(progressbar_style[stage]); + ui->progress_bar->setStyleSheet(QString::fromUtf8(progressbar_style[stage])); // Hide the progress bar during the prepare stage if (stage == VideoCore::LoadCallbackStage::Prepare) { ui->progress_bar->hide(); @@ -178,16 +178,16 @@ void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size slow_shader_first_value = value; } // only calculate an estimate time after a second has passed since stage change - auto diff = duration_cast<milliseconds>(now - slow_shader_start); + const auto diff = duration_cast<milliseconds>(now - slow_shader_start); if (diff > seconds{1}) { - auto eta_mseconds = + const auto eta_mseconds = static_cast<long>(static_cast<double>(total - slow_shader_first_value) / (value - slow_shader_first_value) * diff.count()); estimate = tr("Estimated Time %1") .arg(QTime(0, 0, 0, 0) .addMSecs(std::max<long>(eta_mseconds - diff.count() + 1000, 1000)) - .toString("mm:ss")); + .toString(QStringLiteral("mm:ss"))); } } diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index a59abf6e8..cef2cc1ae 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -281,7 +281,7 @@ void GMainWindow::SoftwareKeyboardInvokeCheckDialog(std::u16string error_message void GMainWindow::WebBrowserOpenPage(std::string_view filename, std::string_view additional_args) { NXInputWebEngineView web_browser_view(this); - // Scope to contain the QProgressDialog for initalization + // Scope to contain the QProgressDialog for initialization { QProgressDialog progress(this); progress.setMinimumDuration(200); @@ -301,7 +301,7 @@ void GMainWindow::WebBrowserOpenPage(std::string_view filename, std::string_view QWebEngineScript nx_shim; nx_shim.setSourceCode(GetNXShimInjectionScript()); nx_shim.setWorldId(QWebEngineScript::MainWorld); - nx_shim.setName("nx_inject.js"); + nx_shim.setName(QStringLiteral("nx_inject.js")); nx_shim.setInjectionPoint(QWebEngineScript::DocumentCreation); nx_shim.setRunsOnSubFrames(true); web_browser_view.page()->profile()->scripts()->insert(nx_shim); @@ -347,7 +347,7 @@ void GMainWindow::WebBrowserOpenPage(std::string_view filename, std::string_view const auto fire_js_keypress = [&web_browser_view](u32 key_code) { web_browser_view.page()->runJavaScript( QStringLiteral("document.dispatchEvent(new KeyboardEvent('keydown', {'key': %1}));") - .arg(QString::fromStdString(std::to_string(key_code)))); + .arg(key_code)); }; QMessageBox::information( @@ -468,7 +468,7 @@ void GMainWindow::InitializeWidgets() { statusBar()->addPermanentWidget(label, 0); } statusBar()->setVisible(true); - setStyleSheet("QStatusBar::item{border: none;}"); + setStyleSheet(QStringLiteral("QStatusBar::item{border: none;}")); } void GMainWindow::InitializeDebugWidgets() { @@ -518,58 +518,67 @@ void GMainWindow::InitializeRecentFileMenuActions() { void GMainWindow::InitializeHotkeys() { hotkey_registry.LoadHotkeys(); - ui.action_Load_File->setShortcut(hotkey_registry.GetKeySequence("Main Window", "Load File")); + const QString main_window = QStringLiteral("Main Window"); + const QString load_file = QStringLiteral("Load File"); + const QString exit_yuzu = QStringLiteral("Exit yuzu"); + const QString stop_emulation = QStringLiteral("Stop Emulation"); + const QString toggle_filter_bar = QStringLiteral("Toggle Filter Bar"); + const QString toggle_status_bar = QStringLiteral("Toggle Status Bar"); + const QString fullscreen = QStringLiteral("Fullscreen"); + + ui.action_Load_File->setShortcut(hotkey_registry.GetKeySequence(main_window, load_file)); ui.action_Load_File->setShortcutContext( - hotkey_registry.GetShortcutContext("Main Window", "Load File")); + hotkey_registry.GetShortcutContext(main_window, load_file)); - ui.action_Exit->setShortcut(hotkey_registry.GetKeySequence("Main Window", "Exit yuzu")); - ui.action_Exit->setShortcutContext( - hotkey_registry.GetShortcutContext("Main Window", "Exit yuzu")); + ui.action_Exit->setShortcut(hotkey_registry.GetKeySequence(main_window, exit_yuzu)); + ui.action_Exit->setShortcutContext(hotkey_registry.GetShortcutContext(main_window, exit_yuzu)); - ui.action_Stop->setShortcut(hotkey_registry.GetKeySequence("Main Window", "Stop Emulation")); + ui.action_Stop->setShortcut(hotkey_registry.GetKeySequence(main_window, stop_emulation)); ui.action_Stop->setShortcutContext( - hotkey_registry.GetShortcutContext("Main Window", "Stop Emulation")); + hotkey_registry.GetShortcutContext(main_window, stop_emulation)); ui.action_Show_Filter_Bar->setShortcut( - hotkey_registry.GetKeySequence("Main Window", "Toggle Filter Bar")); + hotkey_registry.GetKeySequence(main_window, toggle_filter_bar)); ui.action_Show_Filter_Bar->setShortcutContext( - hotkey_registry.GetShortcutContext("Main Window", "Toggle Filter Bar")); + hotkey_registry.GetShortcutContext(main_window, toggle_filter_bar)); ui.action_Show_Status_Bar->setShortcut( - hotkey_registry.GetKeySequence("Main Window", "Toggle Status Bar")); + hotkey_registry.GetKeySequence(main_window, toggle_status_bar)); ui.action_Show_Status_Bar->setShortcutContext( - hotkey_registry.GetShortcutContext("Main Window", "Toggle Status Bar")); - - connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated, - this, &GMainWindow::OnMenuLoadFile); - connect(hotkey_registry.GetHotkey("Main Window", "Continue/Pause Emulation", this), - &QShortcut::activated, this, [&] { - if (emulation_running) { - if (emu_thread->IsRunning()) { - OnPauseGame(); - } else { - OnStartGame(); - } + hotkey_registry.GetShortcutContext(main_window, toggle_status_bar)); + + connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Load File"), this), + &QShortcut::activated, this, &GMainWindow::OnMenuLoadFile); + connect( + hotkey_registry.GetHotkey(main_window, QStringLiteral("Continue/Pause Emulation"), this), + &QShortcut::activated, this, [&] { + if (emulation_running) { + if (emu_thread->IsRunning()) { + OnPauseGame(); + } else { + OnStartGame(); } - }); - connect(hotkey_registry.GetHotkey("Main Window", "Restart Emulation", this), + } + }); + connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Restart Emulation"), this), &QShortcut::activated, this, [this] { - if (!Core::System::GetInstance().IsPoweredOn()) + if (!Core::System::GetInstance().IsPoweredOn()) { return; - BootGame(QString(game_path)); + } + BootGame(game_path); }); - connect(hotkey_registry.GetHotkey("Main Window", "Fullscreen", render_window), + connect(hotkey_registry.GetHotkey(main_window, fullscreen, render_window), &QShortcut::activated, ui.action_Fullscreen, &QAction::trigger); - connect(hotkey_registry.GetHotkey("Main Window", "Fullscreen", render_window), + connect(hotkey_registry.GetHotkey(main_window, fullscreen, render_window), &QShortcut::activatedAmbiguously, ui.action_Fullscreen, &QAction::trigger); - connect(hotkey_registry.GetHotkey("Main Window", "Exit Fullscreen", this), + connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Exit Fullscreen"), this), &QShortcut::activated, this, [&] { if (emulation_running) { ui.action_Fullscreen->setChecked(false); ToggleFullscreen(); } }); - connect(hotkey_registry.GetHotkey("Main Window", "Toggle Speed Limit", this), + connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Speed Limit"), this), &QShortcut::activated, this, [&] { Settings::values.use_frame_limit = !Settings::values.use_frame_limit; UpdateStatusBar(); @@ -578,33 +587,33 @@ void GMainWindow::InitializeHotkeys() { // MSVC occurs and we make it a requirement (see: // https://developercommunity.visualstudio.com/content/problem/93922/constexprs-are-trying-to-be-captured-in-lambda-fun.html) static constexpr u16 SPEED_LIMIT_STEP = 5; - connect(hotkey_registry.GetHotkey("Main Window", "Increase Speed Limit", this), + connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Increase Speed Limit"), this), &QShortcut::activated, this, [&] { if (Settings::values.frame_limit < 9999 - SPEED_LIMIT_STEP) { Settings::values.frame_limit += SPEED_LIMIT_STEP; UpdateStatusBar(); } }); - connect(hotkey_registry.GetHotkey("Main Window", "Decrease Speed Limit", this), + connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Decrease Speed Limit"), this), &QShortcut::activated, this, [&] { if (Settings::values.frame_limit > SPEED_LIMIT_STEP) { Settings::values.frame_limit -= SPEED_LIMIT_STEP; UpdateStatusBar(); } }); - connect(hotkey_registry.GetHotkey("Main Window", "Load Amiibo", this), &QShortcut::activated, - this, [&] { + connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Load Amiibo"), this), + &QShortcut::activated, this, [&] { if (ui.action_Load_Amiibo->isEnabled()) { OnLoadAmiibo(); } }); - connect(hotkey_registry.GetHotkey("Main Window", "Capture Screenshot", this), + connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Capture Screenshot"), this), &QShortcut::activated, this, [&] { if (emu_thread->IsRunning()) { OnCaptureScreenshot(); } }); - connect(hotkey_registry.GetHotkey("Main Window", "Change Docked Mode", this), + connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Change Docked Mode"), this), &QShortcut::activated, this, [&] { Settings::values.use_docked_mode = !Settings::values.use_docked_mode; OnDockedModeChanged(!Settings::values.use_docked_mode, @@ -705,7 +714,9 @@ void GMainWindow::ConnectMenuEvents() { // Fullscreen ui.action_Fullscreen->setShortcut( - hotkey_registry.GetHotkey("Main Window", "Fullscreen", this)->key()); + hotkey_registry + .GetHotkey(QStringLiteral("Main Window"), QStringLiteral("Fullscreen"), this) + ->key()); connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen); // Movie @@ -742,25 +753,33 @@ void GMainWindow::OnDisplayTitleBars(bool show) { QStringList GMainWindow::GetUnsupportedGLExtensions() { QStringList unsupported_ext; - if (!GLAD_GL_ARB_direct_state_access) - unsupported_ext.append("ARB_direct_state_access"); - if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) - unsupported_ext.append("ARB_vertex_type_10f_11f_11f_rev"); - if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) - unsupported_ext.append("ARB_texture_mirror_clamp_to_edge"); - if (!GLAD_GL_ARB_multi_bind) - unsupported_ext.append("ARB_multi_bind"); + if (!GLAD_GL_ARB_direct_state_access) { + unsupported_ext.append(QStringLiteral("ARB_direct_state_access")); + } + if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) { + unsupported_ext.append(QStringLiteral("ARB_vertex_type_10f_11f_11f_rev")); + } + if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) { + unsupported_ext.append(QStringLiteral("ARB_texture_mirror_clamp_to_edge")); + } + if (!GLAD_GL_ARB_multi_bind) { + unsupported_ext.append(QStringLiteral("ARB_multi_bind")); + } // Extensions required to support some texture formats. - if (!GLAD_GL_EXT_texture_compression_s3tc) - unsupported_ext.append("EXT_texture_compression_s3tc"); - if (!GLAD_GL_ARB_texture_compression_rgtc) - unsupported_ext.append("ARB_texture_compression_rgtc"); - if (!GLAD_GL_ARB_depth_buffer_float) - unsupported_ext.append("ARB_depth_buffer_float"); - - for (const QString& ext : unsupported_ext) + if (!GLAD_GL_EXT_texture_compression_s3tc) { + unsupported_ext.append(QStringLiteral("EXT_texture_compression_s3tc")); + } + if (!GLAD_GL_ARB_texture_compression_rgtc) { + unsupported_ext.append(QStringLiteral("ARB_texture_compression_rgtc")); + } + if (!GLAD_GL_ARB_depth_buffer_float) { + unsupported_ext.append(QStringLiteral("ARB_depth_buffer_float")); + } + + for (const QString& ext : unsupported_ext) { LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString()); + } return unsupported_ext; } @@ -782,13 +801,13 @@ bool GMainWindow::LoadROM(const QString& filename) { } } - QStringList unsupported_gl_extensions = GetUnsupportedGLExtensions(); + const QStringList unsupported_gl_extensions = GetUnsupportedGLExtensions(); if (!unsupported_gl_extensions.empty()) { QMessageBox::critical(this, tr("Error while initializing OpenGL Core!"), tr("Your GPU may not support one or more required OpenGL" "extensions. Please ensure you have the latest graphics " "driver.<br><br>Unsupported extensions:<br>") + - unsupported_gl_extensions.join("<br>")); + unsupported_gl_extensions.join(QStringLiteral("<br>"))); return false; } @@ -1007,7 +1026,7 @@ void GMainWindow::UpdateRecentFiles() { std::min(UISettings::values.recent_files.size(), max_recent_files_item); for (int i = 0; i < num_recent_files; i++) { - const QString text = QString("&%1. %2").arg(i + 1).arg( + const QString text = QStringLiteral("&%1. %2").arg(i + 1).arg( QFileInfo(UISettings::values.recent_files[i]).fileName()); actions_recent_files[i]->setText(text); actions_recent_files[i]->setData(UISettings::values.recent_files[i]); @@ -1029,10 +1048,10 @@ void GMainWindow::OnGameListLoadFile(QString game_path) { void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target) { std::string path; - std::string open_target; + QString open_target; switch (target) { case GameListOpenTarget::SaveData: { - open_target = "Save Data"; + open_target = tr("Save Data"); const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir); ASSERT(program_id != 0); @@ -1069,7 +1088,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target break; } case GameListOpenTarget::ModData: { - open_target = "Mod Data"; + open_target = tr("Mod Data"); const auto load_dir = FileUtil::GetUserPath(FileUtil::UserPath::LoadDir); path = fmt::format("{}{:016X}", load_dir, program_id); break; @@ -1079,27 +1098,26 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target } const QString qpath = QString::fromStdString(path); - const QDir dir(qpath); if (!dir.exists()) { - QMessageBox::warning(this, - tr("Error Opening %1 Folder").arg(QString::fromStdString(open_target)), + QMessageBox::warning(this, tr("Error Opening %1 Folder").arg(open_target), tr("Folder does not exist!")); return; } - LOG_INFO(Frontend, "Opening {} path for program_id={:016x}", open_target, program_id); + LOG_INFO(Frontend, "Opening {} path for program_id={:016x}", open_target.toStdString(), + program_id); QDesktopServices::openUrl(QUrl::fromLocalFile(qpath)); } void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) { ASSERT(program_id != 0); + const QString shader_dir = + QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)); const QString tranferable_shader_cache_folder_path = - QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)) + "opengl" + - DIR_SEP + "transferable"; - + shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable"); const QString transferable_shader_cache_file_path = - tranferable_shader_cache_folder_path + DIR_SEP + + tranferable_shader_cache_folder_path + QDir::separator() + QString::fromStdString(fmt::format("{:016X}.bin", program_id)); if (!QFile::exists(transferable_shader_cache_file_path)) { @@ -1216,20 +1234,21 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa return; } - bool ok; + bool ok = false; + const QStringList selections{tr("Full"), tr("Skeleton")}; const auto res = QInputDialog::getItem( this, tr("Select RomFS Dump Mode"), tr("Please select the how you would like the RomFS dumped.<br>Full will copy all of the " "files into the new directory while <br>skeleton will only create the directory " "structure."), - {"Full", "Skeleton"}, 0, false, &ok); + selections, 0, false, &ok); if (!ok) { failed(); vfs->DeleteDirectory(path); return; } - const auto full = res == "Full"; + const auto full = res == selections.constFirst(); const auto entry_size = CalculateRomFSEntrySize(extracted, full); QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0, @@ -1259,10 +1278,11 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id, const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); QString directory; - if (it != compatibility_list.end()) + if (it != compatibility_list.end()) { directory = it->second.second; + } - QDesktopServices::openUrl(QUrl("https://yuzu-emu.org/game/" + directory)); + QDesktopServices::openUrl(QUrl(QStringLiteral("https://yuzu-emu.org/game/") + directory)); } void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) { @@ -1293,7 +1313,9 @@ void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) { void GMainWindow::OnMenuLoadFile() { const QString extensions = - QString("*.").append(GameList::supported_file_extensions.join(" *.")).append(" main"); + QStringLiteral("*.") + .append(GameList::supported_file_extensions.join(QStringLiteral(" *."))) + .append(QStringLiteral(" main")); const QString file_filter = tr("Switch Executable (%1);;All Files (*.*)", "%1 is an identifier for the Switch executable file extensions.") .arg(extensions); @@ -1317,9 +1339,9 @@ void GMainWindow::OnMenuLoadFolder() { } const QDir dir{dir_path}; - const QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files); + const QStringList matching_main = dir.entryList({QStringLiteral("main")}, QDir::Files); if (matching_main.size() == 1) { - BootGame(dir.path() + DIR_SEP + matching_main[0]); + BootGame(dir.path() + QDir::separator() + matching_main[0]); } else { QMessageBox::warning(this, tr("Invalid Directory Selected"), tr("The directory you have selected does not contain a 'main' file.")); @@ -1391,11 +1413,10 @@ void GMainWindow::OnMenuInstallToNAND() { QMessageBox::Yes; }; - if (filename.endsWith("xci", Qt::CaseInsensitive) || - filename.endsWith("nsp", Qt::CaseInsensitive)) { - + if (filename.endsWith(QStringLiteral("xci"), Qt::CaseInsensitive) || + filename.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) { std::shared_ptr<FileSys::NSP> nsp; - if (filename.endsWith("nsp", Qt::CaseInsensitive)) { + if (filename.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) { nsp = std::make_shared<FileSys::NSP>( vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); if (nsp->IsExtractedType()) @@ -1690,9 +1711,9 @@ void GMainWindow::OnConfigure() { } void GMainWindow::OnLoadAmiibo() { - const QString extensions{"*.bin"}; + const QString extensions{QStringLiteral("*.bin")}; const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions); - const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), "", file_filter); + const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), {}, file_filter); if (filename.isEmpty()) { return; @@ -1754,7 +1775,7 @@ void GMainWindow::OnCaptureScreenshot() { QFileDialog png_dialog(this, tr("Capture Screenshot"), UISettings::values.screenshot_path, tr("PNG Image (*.png)")); png_dialog.setAcceptMode(QFileDialog::AcceptSave); - png_dialog.setDefaultSuffix("png"); + png_dialog.setDefaultSuffix(QStringLiteral("png")); if (png_dialog.exec()) { const QString path = png_dialog.selectedFiles().first(); if (!path.isEmpty()) { @@ -1817,17 +1838,17 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det "data, or other bugs."); switch (result) { case Core::System::ResultStatus::ErrorSystemFiles: { - QString message = "yuzu was unable to locate a Switch system archive"; + QString message = tr("yuzu was unable to locate a Switch system archive"); if (!details.empty()) { - message.append(tr(": %1. ").arg(details.c_str())); + message.append(tr(": %1. ").arg(QString::fromStdString(details))); } else { - message.append(". "); + message.append(tr(". ")); } message.append(common_message); answer = QMessageBox::question(this, tr("System Archive Not Found"), message, QMessageBox::Yes | QMessageBox::No, QMessageBox::No); - status_message = "System Archive Missing"; + status_message = tr("System Archive Missing"); break; } @@ -1836,7 +1857,7 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det message.append(common_message); answer = QMessageBox::question(this, tr("Shared Fonts Not Found"), message, QMessageBox::Yes | QMessageBox::No, QMessageBox::No); - status_message = "Shared Font Missing"; + status_message = tr("Shared Font Missing"); break; } @@ -1852,7 +1873,7 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det "Continuing emulation may result in crashes, corrupted save data, or other " "bugs."), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); - status_message = "Fatal Error encountered"; + status_message = tr("Fatal Error encountered"); break; } @@ -1903,18 +1924,19 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { }; QString errors; - - if (!pdm.HasFuses()) + if (!pdm.HasFuses()) { errors += tr("- Missing fuses - Cannot derive SBK\n"); - if (!pdm.HasBoot0()) + } + if (!pdm.HasBoot0()) { errors += tr("- Missing BOOT0 - Cannot derive master keys\n"); - if (!pdm.HasPackage2()) + } + if (!pdm.HasPackage2()) { errors += tr("- Missing BCPKG2-1-Normal-Main - Cannot derive general keys\n"); - if (!pdm.HasProdInfo()) + } + if (!pdm.HasProdInfo()) { errors += tr("- Missing PRODINFO - Cannot derive title keys\n"); - + } if (!errors.isEmpty()) { - QMessageBox::warning( this, tr("Warning Missing Derivation Components"), tr("The following are missing from your configuration that may hinder key " @@ -1964,13 +1986,15 @@ std::optional<u64> GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProv std::vector<u64> romfs_tids; romfs_tids.push_back(program_id); - for (const auto& entry : dlc_match) + for (const auto& entry : dlc_match) { romfs_tids.push_back(entry.title_id); + } if (romfs_tids.size() > 1) { - QStringList list{"Base"}; - for (std::size_t i = 1; i < romfs_tids.size(); ++i) + QStringList list{QStringLiteral("Base")}; + for (std::size_t i = 1; i < romfs_tids.size(); ++i) { list.push_back(QStringLiteral("DLC %1").arg(romfs_tids[i] & 0x7FF)); + } bool ok; const auto res = QInputDialog::getItem( @@ -2082,26 +2106,32 @@ void GMainWindow::filterBarSetChecked(bool state) { } void GMainWindow::UpdateUITheme() { + const QString default_icons = QStringLiteral(":/icons/default"); + const QString& current_theme = UISettings::values.theme; + const bool is_default_theme = current_theme == QString::fromUtf8(UISettings::themes[0].second); QStringList theme_paths(default_theme_paths); - if (UISettings::values.theme != UISettings::themes[0].second && - !UISettings::values.theme.isEmpty()) { - const QString theme_uri(":" + UISettings::values.theme + "/style.qss"); + + if (is_default_theme || current_theme.isEmpty()) { + qApp->setStyleSheet({}); + setStyleSheet({}); + theme_paths.append(default_icons); + QIcon::setThemeName(default_icons); + } else { + const QString theme_uri(QLatin1Char{':'} + current_theme + QStringLiteral("/style.qss")); QFile f(theme_uri); if (f.open(QFile::ReadOnly | QFile::Text)) { QTextStream ts(&f); qApp->setStyleSheet(ts.readAll()); - GMainWindow::setStyleSheet(ts.readAll()); + setStyleSheet(ts.readAll()); } else { LOG_ERROR(Frontend, "Unable to set style, stylesheet file not found"); } - theme_paths.append(QStringList{":/icons/default", ":/icons/" + UISettings::values.theme}); - QIcon::setThemeName(":/icons/" + UISettings::values.theme); - } else { - qApp->setStyleSheet(""); - GMainWindow::setStyleSheet(""); - theme_paths.append(QStringList{":/icons/default"}); - QIcon::setThemeName(":/icons/default"); + + const QString theme_name = QStringLiteral(":/icons/") + current_theme; + theme_paths.append({default_icons, theme_name}); + QIcon::setThemeName(theme_name); } + QIcon::setThemeSearchPaths(theme_paths); emit UpdateThemedIcons(); } @@ -2129,8 +2159,8 @@ int main(int argc, char* argv[]) { SCOPE_EXIT({ MicroProfileShutdown(); }); // Init settings params - QCoreApplication::setOrganizationName("yuzu team"); - QCoreApplication::setApplicationName("yuzu"); + QCoreApplication::setOrganizationName(QStringLiteral("yuzu team")); + QCoreApplication::setApplicationName(QStringLiteral("yuzu")); // Enables the core to make the qt created contexts current on std::threads QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); diff --git a/src/yuzu/util/sequence_dialog/sequence_dialog.cpp b/src/yuzu/util/sequence_dialog/sequence_dialog.cpp index d3edf6ec3..bb5f74ec4 100644 --- a/src/yuzu/util/sequence_dialog/sequence_dialog.cpp +++ b/src/yuzu/util/sequence_dialog/sequence_dialog.cpp @@ -9,16 +9,19 @@ SequenceDialog::SequenceDialog(QWidget* parent) : QDialog(parent) { setWindowTitle(tr("Enter a hotkey")); - auto* layout = new QVBoxLayout(this); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + key_sequence = new QKeySequenceEdit; - layout->addWidget(key_sequence); - auto* buttons = - new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); + + auto* const buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); buttons->setCenterButtons(true); + + auto* const layout = new QVBoxLayout(this); + layout->addWidget(key_sequence); layout->addWidget(buttons); + connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); - setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); } SequenceDialog::~SequenceDialog() = default; diff --git a/src/yuzu/util/util.cpp b/src/yuzu/util/util.cpp index 62c080aff..ef31bc2d2 100644 --- a/src/yuzu/util/util.cpp +++ b/src/yuzu/util/util.cpp @@ -8,7 +8,7 @@ #include "yuzu/util/util.h" QFont GetMonospaceFont() { - QFont font("monospace"); + QFont font(QStringLiteral("monospace")); // Automatic fallback to a monospace font on on platforms without a font called "monospace" font.setStyleHint(QFont::Monospace); font.setFixedPitch(true); @@ -16,14 +16,16 @@ QFont GetMonospaceFont() { } QString ReadableByteSize(qulonglong size) { - static const std::array<const char*, 6> units = {"B", "KiB", "MiB", "GiB", "TiB", "PiB"}; - if (size == 0) - return "0"; - int digit_groups = std::min<int>(static_cast<int>(std::log10(size) / std::log10(1024)), - static_cast<int>(units.size())); - return QString("%L1 %2") + static constexpr std::array units{"B", "KiB", "MiB", "GiB", "TiB", "PiB"}; + if (size == 0) { + return QStringLiteral("0"); + } + + const int digit_groups = std::min(static_cast<int>(std::log10(size) / std::log10(1024)), + static_cast<int>(units.size())); + return QStringLiteral("%L1 %2") .arg(size / std::pow(1024, digit_groups), 0, 'f', 1) - .arg(units[digit_groups]); + .arg(QString::fromUtf8(units[digit_groups])); } QPixmap CreateCirclePixmapFromColor(const QColor& color) { diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt index 297dab653..b5f06ab9e 100644 --- a/src/yuzu_cmd/CMakeLists.txt +++ b/src/yuzu_cmd/CMakeLists.txt @@ -4,6 +4,8 @@ add_executable(yuzu-cmd config.cpp config.h default_ini.h + emu_window/emu_window_sdl2_gl.cpp + emu_window/emu_window_sdl2_gl.h emu_window/emu_window_sdl2.cpp emu_window/emu_window_sdl2.h resource.h diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index d0ae058fd..730956427 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -26,12 +26,12 @@ Config::Config() { Config::~Config() = default; bool Config::LoadINI(const std::string& default_contents, bool retry) { - const char* location = this->sdl2_config_loc.c_str(); + const std::string& location = this->sdl2_config_loc; if (sdl2_config->ParseError() < 0) { if (retry) { LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", location); FileUtil::CreateFullPath(location); - FileUtil::WriteStringToFile(true, default_contents, location); + FileUtil::WriteStringToFile(true, location, default_contents); sdl2_config = std::make_unique<INIReader>(location); // Reopen file return LoadINI(default_contents, false); diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index 8f104062d..a6edc089a 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp @@ -2,53 +2,27 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <algorithm> -#include <cstdlib> -#include <string> -#define SDL_MAIN_HANDLED #include <SDL.h> -#include <fmt/format.h> -#include <glad/glad.h> #include "common/logging/log.h" -#include "common/scm_rev.h" -#include "common/string_util.h" -#include "core/settings.h" #include "input_common/keyboard.h" #include "input_common/main.h" #include "input_common/motion_emu.h" #include "input_common/sdl/sdl.h" #include "yuzu_cmd/emu_window/emu_window_sdl2.h" -class SDLGLContext : public Core::Frontend::GraphicsContext { -public: - explicit SDLGLContext() { - // create a hidden window to make the shared context against - window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, // x position - SDL_WINDOWPOS_UNDEFINED, // y position - Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, - SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); - context = SDL_GL_CreateContext(window); - } - - ~SDLGLContext() { - SDL_GL_DeleteContext(context); - SDL_DestroyWindow(window); - } - - void MakeCurrent() override { - SDL_GL_MakeCurrent(window, context); - } - - void DoneCurrent() override { - SDL_GL_MakeCurrent(window, nullptr); +EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { + LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); + exit(1); } + InputCommon::Init(); + SDL_SetMainReady(); +} - void SwapBuffers() override {} - -private: - SDL_Window* window; - SDL_GLContext context; -}; +EmuWindow_SDL2::~EmuWindow_SDL2() { + InputCommon::Shutdown(); + SDL_Quit(); +} void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); @@ -139,112 +113,6 @@ void EmuWindow_SDL2::Fullscreen() { SDL_MaximizeWindow(render_window); } -bool EmuWindow_SDL2::SupportsRequiredGLExtensions() { - std::vector<std::string> unsupported_ext; - - if (!GLAD_GL_ARB_direct_state_access) - unsupported_ext.push_back("ARB_direct_state_access"); - if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) - unsupported_ext.push_back("ARB_vertex_type_10f_11f_11f_rev"); - if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) - unsupported_ext.push_back("ARB_texture_mirror_clamp_to_edge"); - if (!GLAD_GL_ARB_multi_bind) - unsupported_ext.push_back("ARB_multi_bind"); - - // Extensions required to support some texture formats. - if (!GLAD_GL_EXT_texture_compression_s3tc) - unsupported_ext.push_back("EXT_texture_compression_s3tc"); - if (!GLAD_GL_ARB_texture_compression_rgtc) - unsupported_ext.push_back("ARB_texture_compression_rgtc"); - if (!GLAD_GL_ARB_depth_buffer_float) - unsupported_ext.push_back("ARB_depth_buffer_float"); - - for (const std::string& ext : unsupported_ext) - LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext); - - return unsupported_ext.empty(); -} - -EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { - // Initialize the window - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { - LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); - exit(1); - } - - InputCommon::Init(); - - SDL_SetMainReady(); - - const SDL_GLprofile profile = Settings::values.use_compatibility_profile - ? SDL_GL_CONTEXT_PROFILE_COMPATIBILITY - : SDL_GL_CONTEXT_PROFILE_CORE; - - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profile); - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); - SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); - - std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname, - Common::g_scm_branch, Common::g_scm_desc); - render_window = - SDL_CreateWindow(window_title.c_str(), - SDL_WINDOWPOS_UNDEFINED, // x position - SDL_WINDOWPOS_UNDEFINED, // y position - Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, - SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); - - if (render_window == nullptr) { - LOG_CRITICAL(Frontend, "Failed to create SDL2 window! {}", SDL_GetError()); - exit(1); - } - - if (fullscreen) { - Fullscreen(); - } - gl_context = SDL_GL_CreateContext(render_window); - - if (gl_context == nullptr) { - LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context! {}", SDL_GetError()); - exit(1); - } - - if (!gladLoadGLLoader(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) { - LOG_CRITICAL(Frontend, "Failed to initialize GL functions! {}", SDL_GetError()); - exit(1); - } - - if (!SupportsRequiredGLExtensions()) { - LOG_CRITICAL(Frontend, "GPU does not support all required OpenGL extensions! Exiting..."); - exit(1); - } - - OnResize(); - OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); - SDL_PumpEvents(); - SDL_GL_SetSwapInterval(false); - LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, - Common::g_scm_desc); - Settings::LogSettings(); - - DoneCurrent(); -} - -EmuWindow_SDL2::~EmuWindow_SDL2() { - InputCommon::Shutdown(); - SDL_GL_DeleteContext(gl_context); - SDL_Quit(); -} - -void EmuWindow_SDL2::SwapBuffers() { - SDL_GL_SwapWindow(render_window); -} - void EmuWindow_SDL2::PollEvents() { SDL_Event event; @@ -257,7 +125,11 @@ void EmuWindow_SDL2::PollEvents() { case SDL_WINDOWEVENT_RESIZED: case SDL_WINDOWEVENT_MAXIMIZED: case SDL_WINDOWEVENT_RESTORED: + OnResize(); + break; case SDL_WINDOWEVENT_MINIMIZED: + case SDL_WINDOWEVENT_EXPOSED: + is_shown = event.window.event == SDL_WINDOWEVENT_EXPOSED; OnResize(); break; case SDL_WINDOWEVENT_CLOSE: @@ -300,20 +172,6 @@ void EmuWindow_SDL2::PollEvents() { } } -void EmuWindow_SDL2::MakeCurrent() { - SDL_GL_MakeCurrent(render_window, gl_context); -} - -void EmuWindow_SDL2::DoneCurrent() { - SDL_GL_MakeCurrent(render_window, nullptr); -} - -void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest( - const std::pair<unsigned, unsigned>& minimal_size) { - +void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) { SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second); } - -std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2::CreateSharedContext() const { - return std::make_unique<SDLGLContext>(); -} diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h index 17e98227f..d8051ebdf 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h @@ -15,24 +15,13 @@ public: explicit EmuWindow_SDL2(bool fullscreen); ~EmuWindow_SDL2(); - /// Swap buffers to display the next frame - void SwapBuffers() override; - /// Polls window events void PollEvents() override; - /// Makes the graphics context current for the caller thread - void MakeCurrent() override; - - /// Releases the GL context from the caller thread - void DoneCurrent() override; - - std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; - /// Whether the window is still open, and a close request hasn't yet been sent bool IsOpen() const; -private: +protected: /// Called by PollEvents when a key is pressed or released. void OnKeyEvent(int key, u8 state); @@ -60,20 +49,15 @@ private: /// Called when user passes the fullscreen parameter flag void Fullscreen(); - /// Whether the GPU and driver supports the OpenGL extension required - bool SupportsRequiredGLExtensions(); - /// Called when a configuration change affects the minimal size of the window - void OnMinimalClientAreaChangeRequest( - const std::pair<unsigned, unsigned>& minimal_size) override; + void OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) override; /// Is the window still open? bool is_open = true; + /// Is the window being shown? + bool is_shown = true; + /// Internal SDL2 render window SDL_Window* render_window; - - using SDL_GLContext = void*; - /// The OpenGL context associated with the window - SDL_GLContext gl_context; }; diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp new file mode 100644 index 000000000..904022137 --- /dev/null +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp @@ -0,0 +1,154 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <cstdlib> +#include <string> +#define SDL_MAIN_HANDLED +#include <SDL.h> +#include <fmt/format.h> +#include <glad/glad.h> +#include "common/logging/log.h" +#include "common/scm_rev.h" +#include "common/string_util.h" +#include "core/settings.h" +#include "input_common/keyboard.h" +#include "input_common/main.h" +#include "input_common/motion_emu.h" +#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" + +class SDLGLContext : public Core::Frontend::GraphicsContext { +public: + explicit SDLGLContext() { + // create a hidden window to make the shared context against + window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, // x position + SDL_WINDOWPOS_UNDEFINED, // y position + Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, + SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); + context = SDL_GL_CreateContext(window); + } + + ~SDLGLContext() { + SDL_GL_DeleteContext(context); + SDL_DestroyWindow(window); + } + + void MakeCurrent() override { + SDL_GL_MakeCurrent(window, context); + } + + void DoneCurrent() override { + SDL_GL_MakeCurrent(window, nullptr); + } + + void SwapBuffers() override {} + +private: + SDL_Window* window; + SDL_GLContext context; +}; + +bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() { + std::vector<std::string> unsupported_ext; + + if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) + unsupported_ext.push_back("ARB_vertex_type_10f_11f_11f_rev"); + if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) + unsupported_ext.push_back("ARB_texture_mirror_clamp_to_edge"); + if (!GLAD_GL_ARB_multi_bind) + unsupported_ext.push_back("ARB_multi_bind"); + + // Extensions required to support some texture formats. + if (!GLAD_GL_EXT_texture_compression_s3tc) + unsupported_ext.push_back("EXT_texture_compression_s3tc"); + if (!GLAD_GL_ARB_texture_compression_rgtc) + unsupported_ext.push_back("ARB_texture_compression_rgtc"); + if (!GLAD_GL_ARB_depth_buffer_float) + unsupported_ext.push_back("ARB_depth_buffer_float"); + + for (const std::string& ext : unsupported_ext) + LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext); + + return unsupported_ext.empty(); +} + +EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(bool fullscreen) : EmuWindow_SDL2(fullscreen) { + const SDL_GLprofile profile = Settings::values.use_compatibility_profile + ? SDL_GL_CONTEXT_PROFILE_COMPATIBILITY + : SDL_GL_CONTEXT_PROFILE_CORE; + + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profile); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); + SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); + + std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname, + Common::g_scm_branch, Common::g_scm_desc); + render_window = + SDL_CreateWindow(window_title.c_str(), + SDL_WINDOWPOS_UNDEFINED, // x position + SDL_WINDOWPOS_UNDEFINED, // y position + Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, + SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); + + if (render_window == nullptr) { + LOG_CRITICAL(Frontend, "Failed to create SDL2 window! {}", SDL_GetError()); + exit(1); + } + + if (fullscreen) { + Fullscreen(); + } + gl_context = SDL_GL_CreateContext(render_window); + + if (gl_context == nullptr) { + LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context! {}", SDL_GetError()); + exit(1); + } + + if (!gladLoadGLLoader(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) { + LOG_CRITICAL(Frontend, "Failed to initialize GL functions! {}", SDL_GetError()); + exit(1); + } + + if (!SupportsRequiredGLExtensions()) { + LOG_CRITICAL(Frontend, "GPU does not support all required OpenGL extensions! Exiting..."); + exit(1); + } + + OnResize(); + OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); + SDL_PumpEvents(); + SDL_GL_SetSwapInterval(false); + LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, + Common::g_scm_desc); + Settings::LogSettings(); + + DoneCurrent(); +} + +EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() { + SDL_GL_DeleteContext(gl_context); +} + +void EmuWindow_SDL2_GL::SwapBuffers() { + SDL_GL_SwapWindow(render_window); +} + +void EmuWindow_SDL2_GL::MakeCurrent() { + SDL_GL_MakeCurrent(render_window, gl_context); +} + +void EmuWindow_SDL2_GL::DoneCurrent() { + SDL_GL_MakeCurrent(render_window, nullptr); +} + +std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const { + return std::make_unique<SDLGLContext>(); +} diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h new file mode 100644 index 000000000..630deba93 --- /dev/null +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h @@ -0,0 +1,34 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include "core/frontend/emu_window.h" +#include "yuzu_cmd/emu_window/emu_window_sdl2.h" + +class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 { +public: + explicit EmuWindow_SDL2_GL(bool fullscreen); + ~EmuWindow_SDL2_GL(); + + /// Swap buffers to display the next frame + void SwapBuffers() override; + + /// Makes the graphics context current for the caller thread + void MakeCurrent() override; + + /// Releases the GL context from the caller thread + void DoneCurrent() override; + + std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; + +private: + /// Whether the GPU and driver supports the OpenGL extension required + bool SupportsRequiredGLExtensions(); + + using SDL_GLContext = void*; + /// The OpenGL context associated with the window + SDL_GLContext gl_context; +}; diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index d3734927b..5d9442646 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -31,6 +31,7 @@ #include "video_core/renderer_base.h" #include "yuzu_cmd/config.h" #include "yuzu_cmd/emu_window/emu_window_sdl2.h" +#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" #include "core/file_sys/registered_cache.h" @@ -173,7 +174,7 @@ int main(int argc, char** argv) { Settings::values.use_gdbstub = use_gdbstub; Settings::Apply(); - std::unique_ptr<EmuWindow_SDL2> emu_window{std::make_unique<EmuWindow_SDL2>(fullscreen)}; + std::unique_ptr<EmuWindow_SDL2> emu_window{std::make_unique<EmuWindow_SDL2_GL>(fullscreen)}; if (!Settings::values.use_multi_core) { // Single core mode must acquire OpenGL context for entire emulation session |