diff options
Diffstat (limited to 'src')
162 files changed, 3841 insertions, 2605 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9aea4af87..04018233f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt | |||
| @@ -21,15 +21,29 @@ if (MSVC) | |||
| 21 | # Ensure that projects build with Unicode support. | 21 | # Ensure that projects build with Unicode support. |
| 22 | add_definitions(-DUNICODE -D_UNICODE) | 22 | add_definitions(-DUNICODE -D_UNICODE) |
| 23 | 23 | ||
| 24 | # /W3 - Level 3 warnings | 24 | # /W3 - Level 3 warnings |
| 25 | # /MP - Multi-threaded compilation | 25 | # /MP - Multi-threaded compilation |
| 26 | # /Zi - Output debugging information | 26 | # /Zi - Output debugging information |
| 27 | # /Zo - enhanced debug info for optimized builds | 27 | # /Zo - Enhanced debug info for optimized builds |
| 28 | # /permissive- - enables stricter C++ standards conformance checks | 28 | # /permissive- - Enables stricter C++ standards conformance checks |
| 29 | # /EHsc - C++-only exception handling semantics | 29 | # /EHsc - C++-only exception handling semantics |
| 30 | # /Zc:throwingNew - let codegen assume `operator new` will never return null | 30 | # /volatile:iso - Use strict standards-compliant volatile semantics. |
| 31 | # /Zc:inline - let codegen omit inline functions in object files | 31 | # /Zc:externConstexpr - Allow extern constexpr variables to have external linkage, like the standard mandates |
| 32 | add_compile_options(/W3 /MP /Zi /Zo /permissive- /EHsc /std:c++latest /Zc:throwingNew,inline) | 32 | # /Zc:inline - Let codegen omit inline functions in object files |
| 33 | # /Zc:throwingNew - Let codegen assume `operator new` (without std::nothrow) will never return null | ||
| 34 | add_compile_options( | ||
| 35 | /W3 | ||
| 36 | /MP | ||
| 37 | /Zi | ||
| 38 | /Zo | ||
| 39 | /permissive- | ||
| 40 | /EHsc | ||
| 41 | /std:c++latest | ||
| 42 | /volatile:iso | ||
| 43 | /Zc:externConstexpr | ||
| 44 | /Zc:inline | ||
| 45 | /Zc:throwingNew | ||
| 46 | ) | ||
| 33 | 47 | ||
| 34 | # /GS- - No stack buffer overflow checks | 48 | # /GS- - No stack buffer overflow checks |
| 35 | add_compile_options("$<$<CONFIG:Release>:/GS->") | 49 | add_compile_options("$<$<CONFIG:Release>:/GS->") |
| @@ -37,7 +51,10 @@ if (MSVC) | |||
| 37 | set(CMAKE_EXE_LINKER_FLAGS_DEBUG "/DEBUG /MANIFEST:NO" CACHE STRING "" FORCE) | 51 | set(CMAKE_EXE_LINKER_FLAGS_DEBUG "/DEBUG /MANIFEST:NO" CACHE STRING "" FORCE) |
| 38 | set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE) | 52 | set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE) |
| 39 | else() | 53 | else() |
| 40 | add_compile_options("-Wno-attributes") | 54 | add_compile_options( |
| 55 | -Wall | ||
| 56 | -Wno-attributes | ||
| 57 | ) | ||
| 41 | 58 | ||
| 42 | if (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang) | 59 | if (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang) |
| 43 | add_compile_options("-stdlib=libc++") | 60 | add_compile_options("-stdlib=libc++") |
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 { | |||
| 78 | // Remove any ending forward slashes from directory paths | 78 | // Remove any ending forward slashes from directory paths |
| 79 | // Modifies argument. | 79 | // Modifies argument. |
| 80 | static void StripTailDirSlashes(std::string& fname) { | 80 | static void StripTailDirSlashes(std::string& fname) { |
| 81 | if (fname.length() > 1) { | 81 | if (fname.length() <= 1) { |
| 82 | std::size_t i = fname.length(); | 82 | return; |
| 83 | while (i > 0 && fname[i - 1] == DIR_SEP_CHR) | 83 | } |
| 84 | --i; | 84 | |
| 85 | fname.resize(i); | 85 | std::size_t i = fname.length(); |
| 86 | while (i > 0 && fname[i - 1] == DIR_SEP_CHR) { | ||
| 87 | --i; | ||
| 86 | } | 88 | } |
| 87 | return; | 89 | fname.resize(i); |
| 88 | } | 90 | } |
| 89 | 91 | ||
| 90 | // Returns true if file filename exists | ||
| 91 | bool Exists(const std::string& filename) { | 92 | bool Exists(const std::string& filename) { |
| 92 | struct stat file_info; | 93 | struct stat file_info; |
| 93 | 94 | ||
| @@ -107,7 +108,6 @@ bool Exists(const std::string& filename) { | |||
| 107 | return (result == 0); | 108 | return (result == 0); |
| 108 | } | 109 | } |
| 109 | 110 | ||
| 110 | // Returns true if filename is a directory | ||
| 111 | bool IsDirectory(const std::string& filename) { | 111 | bool IsDirectory(const std::string& filename) { |
| 112 | struct stat file_info; | 112 | struct stat file_info; |
| 113 | 113 | ||
| @@ -132,8 +132,6 @@ bool IsDirectory(const std::string& filename) { | |||
| 132 | return S_ISDIR(file_info.st_mode); | 132 | return S_ISDIR(file_info.st_mode); |
| 133 | } | 133 | } |
| 134 | 134 | ||
| 135 | // Deletes a given filename, return true on success | ||
| 136 | // Doesn't supports deleting a directory | ||
| 137 | bool Delete(const std::string& filename) { | 135 | bool Delete(const std::string& filename) { |
| 138 | LOG_TRACE(Common_Filesystem, "file {}", filename); | 136 | LOG_TRACE(Common_Filesystem, "file {}", filename); |
| 139 | 137 | ||
| @@ -165,7 +163,6 @@ bool Delete(const std::string& filename) { | |||
| 165 | return true; | 163 | return true; |
| 166 | } | 164 | } |
| 167 | 165 | ||
| 168 | // Returns true if successful, or path already exists. | ||
| 169 | bool CreateDir(const std::string& path) { | 166 | bool CreateDir(const std::string& path) { |
| 170 | LOG_TRACE(Common_Filesystem, "directory {}", path); | 167 | LOG_TRACE(Common_Filesystem, "directory {}", path); |
| 171 | #ifdef _WIN32 | 168 | #ifdef _WIN32 |
| @@ -194,7 +191,6 @@ bool CreateDir(const std::string& path) { | |||
| 194 | #endif | 191 | #endif |
| 195 | } | 192 | } |
| 196 | 193 | ||
| 197 | // Creates the full path of fullPath returns true on success | ||
| 198 | bool CreateFullPath(const std::string& fullPath) { | 194 | bool CreateFullPath(const std::string& fullPath) { |
| 199 | int panicCounter = 100; | 195 | int panicCounter = 100; |
| 200 | LOG_TRACE(Common_Filesystem, "path {}", fullPath); | 196 | LOG_TRACE(Common_Filesystem, "path {}", fullPath); |
| @@ -230,7 +226,6 @@ bool CreateFullPath(const std::string& fullPath) { | |||
| 230 | } | 226 | } |
| 231 | } | 227 | } |
| 232 | 228 | ||
| 233 | // Deletes a directory filename, returns true on success | ||
| 234 | bool DeleteDir(const std::string& filename) { | 229 | bool DeleteDir(const std::string& filename) { |
| 235 | LOG_TRACE(Common_Filesystem, "directory {}", filename); | 230 | LOG_TRACE(Common_Filesystem, "directory {}", filename); |
| 236 | 231 | ||
| @@ -252,7 +247,6 @@ bool DeleteDir(const std::string& filename) { | |||
| 252 | return false; | 247 | return false; |
| 253 | } | 248 | } |
| 254 | 249 | ||
| 255 | // renames file srcFilename to destFilename, returns true on success | ||
| 256 | bool Rename(const std::string& srcFilename, const std::string& destFilename) { | 250 | bool Rename(const std::string& srcFilename, const std::string& destFilename) { |
| 257 | LOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename); | 251 | LOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename); |
| 258 | #ifdef _WIN32 | 252 | #ifdef _WIN32 |
| @@ -268,7 +262,6 @@ bool Rename(const std::string& srcFilename, const std::string& destFilename) { | |||
| 268 | return false; | 262 | return false; |
| 269 | } | 263 | } |
| 270 | 264 | ||
| 271 | // copies file srcFilename to destFilename, returns true on success | ||
| 272 | bool Copy(const std::string& srcFilename, const std::string& destFilename) { | 265 | bool Copy(const std::string& srcFilename, const std::string& destFilename) { |
| 273 | LOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename); | 266 | LOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename); |
| 274 | #ifdef _WIN32 | 267 | #ifdef _WIN32 |
| @@ -324,7 +317,6 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) { | |||
| 324 | #endif | 317 | #endif |
| 325 | } | 318 | } |
| 326 | 319 | ||
| 327 | // Returns the size of filename (64bit) | ||
| 328 | u64 GetSize(const std::string& filename) { | 320 | u64 GetSize(const std::string& filename) { |
| 329 | if (!Exists(filename)) { | 321 | if (!Exists(filename)) { |
| 330 | LOG_ERROR(Common_Filesystem, "failed {}: No such file", filename); | 322 | LOG_ERROR(Common_Filesystem, "failed {}: No such file", filename); |
| @@ -351,7 +343,6 @@ u64 GetSize(const std::string& filename) { | |||
| 351 | return 0; | 343 | return 0; |
| 352 | } | 344 | } |
| 353 | 345 | ||
| 354 | // Overloaded GetSize, accepts file descriptor | ||
| 355 | u64 GetSize(const int fd) { | 346 | u64 GetSize(const int fd) { |
| 356 | struct stat buf; | 347 | struct stat buf; |
| 357 | if (fstat(fd, &buf) != 0) { | 348 | if (fstat(fd, &buf) != 0) { |
| @@ -361,7 +352,6 @@ u64 GetSize(const int fd) { | |||
| 361 | return buf.st_size; | 352 | return buf.st_size; |
| 362 | } | 353 | } |
| 363 | 354 | ||
| 364 | // Overloaded GetSize, accepts FILE* | ||
| 365 | u64 GetSize(FILE* f) { | 355 | u64 GetSize(FILE* f) { |
| 366 | // can't use off_t here because it can be 32-bit | 356 | // can't use off_t here because it can be 32-bit |
| 367 | u64 pos = ftello(f); | 357 | u64 pos = ftello(f); |
| @@ -377,7 +367,6 @@ u64 GetSize(FILE* f) { | |||
| 377 | return size; | 367 | return size; |
| 378 | } | 368 | } |
| 379 | 369 | ||
| 380 | // creates an empty file filename, returns true on success | ||
| 381 | bool CreateEmptyFile(const std::string& filename) { | 370 | bool CreateEmptyFile(const std::string& filename) { |
| 382 | LOG_TRACE(Common_Filesystem, "{}", filename); | 371 | LOG_TRACE(Common_Filesystem, "{}", filename); |
| 383 | 372 | ||
| @@ -502,7 +491,6 @@ bool DeleteDirRecursively(const std::string& directory, unsigned int recursion) | |||
| 502 | return true; | 491 | return true; |
| 503 | } | 492 | } |
| 504 | 493 | ||
| 505 | // Create directory and copy contents (does not overwrite existing files) | ||
| 506 | void CopyDir(const std::string& source_path, const std::string& dest_path) { | 494 | void CopyDir(const std::string& source_path, const std::string& dest_path) { |
| 507 | #ifndef _WIN32 | 495 | #ifndef _WIN32 |
| 508 | if (source_path == dest_path) | 496 | if (source_path == dest_path) |
| @@ -539,8 +527,7 @@ void CopyDir(const std::string& source_path, const std::string& dest_path) { | |||
| 539 | #endif | 527 | #endif |
| 540 | } | 528 | } |
| 541 | 529 | ||
| 542 | // Returns the current directory | 530 | std::optional<std::string> GetCurrentDir() { |
| 543 | std::string GetCurrentDir() { | ||
| 544 | // Get the current working directory (getcwd uses malloc) | 531 | // Get the current working directory (getcwd uses malloc) |
| 545 | #ifdef _WIN32 | 532 | #ifdef _WIN32 |
| 546 | wchar_t* dir; | 533 | wchar_t* dir; |
| @@ -550,7 +537,7 @@ std::string GetCurrentDir() { | |||
| 550 | if (!(dir = getcwd(nullptr, 0))) { | 537 | if (!(dir = getcwd(nullptr, 0))) { |
| 551 | #endif | 538 | #endif |
| 552 | LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: {}", GetLastErrorMsg()); | 539 | LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: {}", GetLastErrorMsg()); |
| 553 | return nullptr; | 540 | return {}; |
| 554 | } | 541 | } |
| 555 | #ifdef _WIN32 | 542 | #ifdef _WIN32 |
| 556 | std::string strDir = Common::UTF16ToUTF8(dir); | 543 | std::string strDir = Common::UTF16ToUTF8(dir); |
| @@ -561,7 +548,6 @@ std::string GetCurrentDir() { | |||
| 561 | return strDir; | 548 | return strDir; |
| 562 | } | 549 | } |
| 563 | 550 | ||
| 564 | // Sets the current directory to the given directory | ||
| 565 | bool SetCurrentDir(const std::string& directory) { | 551 | bool SetCurrentDir(const std::string& directory) { |
| 566 | #ifdef _WIN32 | 552 | #ifdef _WIN32 |
| 567 | return _wchdir(Common::UTF8ToUTF16W(directory).c_str()) == 0; | 553 | return _wchdir(Common::UTF8ToUTF16W(directory).c_str()) == 0; |
| @@ -673,8 +659,6 @@ std::string GetSysDirectory() { | |||
| 673 | return sysDir; | 659 | return sysDir; |
| 674 | } | 660 | } |
| 675 | 661 | ||
| 676 | // Returns a string with a yuzu data dir or file in the user's home | ||
| 677 | // directory. To be used in "multi-user" mode (that is, installed). | ||
| 678 | const std::string& GetUserPath(UserPath path, const std::string& new_path) { | 662 | const std::string& GetUserPath(UserPath path, const std::string& new_path) { |
| 679 | static std::unordered_map<UserPath, std::string> paths; | 663 | static std::unordered_map<UserPath, std::string> paths; |
| 680 | auto& user_path = paths[UserPath::UserDir]; | 664 | auto& user_path = paths[UserPath::UserDir]; |
| @@ -762,11 +746,11 @@ std::string GetNANDRegistrationDir(bool system) { | |||
| 762 | return GetUserPath(UserPath::NANDDir) + "user/Contents/registered/"; | 746 | return GetUserPath(UserPath::NANDDir) + "user/Contents/registered/"; |
| 763 | } | 747 | } |
| 764 | 748 | ||
| 765 | std::size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) { | 749 | std::size_t WriteStringToFile(bool text_file, const std::string& filename, std::string_view str) { |
| 766 | return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size()); | 750 | return IOFile(filename, text_file ? "w" : "wb").WriteString(str); |
| 767 | } | 751 | } |
| 768 | 752 | ||
| 769 | std::size_t ReadFileToString(bool text_file, const char* filename, std::string& str) { | 753 | std::size_t ReadFileToString(bool text_file, const std::string& filename, std::string& str) { |
| 770 | IOFile file(filename, text_file ? "r" : "rb"); | 754 | IOFile file(filename, text_file ? "r" : "rb"); |
| 771 | 755 | ||
| 772 | if (!file.IsOpen()) | 756 | if (!file.IsOpen()) |
| @@ -776,13 +760,6 @@ std::size_t ReadFileToString(bool text_file, const char* filename, std::string& | |||
| 776 | return file.ReadArray(&str[0], str.size()); | 760 | return file.ReadArray(&str[0], str.size()); |
| 777 | } | 761 | } |
| 778 | 762 | ||
| 779 | /** | ||
| 780 | * Splits the filename into 8.3 format | ||
| 781 | * Loosely implemented following https://en.wikipedia.org/wiki/8.3_filename | ||
| 782 | * @param filename The normal filename to use | ||
| 783 | * @param short_name A 9-char array in which the short name will be written | ||
| 784 | * @param extension A 4-char array in which the extension will be written | ||
| 785 | */ | ||
| 786 | void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name, | 763 | void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name, |
| 787 | std::array<char, 4>& extension) { | 764 | std::array<char, 4>& extension) { |
| 788 | const std::string forbidden_characters = ".\"/\\[]:;=, "; | 765 | 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 @@ | |||
| 9 | #include <fstream> | 9 | #include <fstream> |
| 10 | #include <functional> | 10 | #include <functional> |
| 11 | #include <limits> | 11 | #include <limits> |
| 12 | #include <optional> | ||
| 12 | #include <string> | 13 | #include <string> |
| 13 | #include <string_view> | 14 | #include <string_view> |
| 14 | #include <type_traits> | 15 | #include <type_traits> |
| @@ -118,7 +119,7 @@ u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry, | |||
| 118 | bool DeleteDirRecursively(const std::string& directory, unsigned int recursion = 256); | 119 | bool DeleteDirRecursively(const std::string& directory, unsigned int recursion = 256); |
| 119 | 120 | ||
| 120 | // Returns the current directory | 121 | // Returns the current directory |
| 121 | std::string GetCurrentDir(); | 122 | std::optional<std::string> GetCurrentDir(); |
| 122 | 123 | ||
| 123 | // Create directory and copy contents (does not overwrite existing files) | 124 | // Create directory and copy contents (does not overwrite existing files) |
| 124 | void CopyDir(const std::string& source_path, const std::string& dest_path); | 125 | void CopyDir(const std::string& source_path, const std::string& dest_path); |
| @@ -146,9 +147,9 @@ const std::string& GetExeDirectory(); | |||
| 146 | std::string AppDataRoamingDirectory(); | 147 | std::string AppDataRoamingDirectory(); |
| 147 | #endif | 148 | #endif |
| 148 | 149 | ||
| 149 | std::size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename); | 150 | std::size_t WriteStringToFile(bool text_file, const std::string& filename, std::string_view str); |
| 150 | 151 | ||
| 151 | std::size_t ReadFileToString(bool text_file, const char* filename, std::string& str); | 152 | std::size_t ReadFileToString(bool text_file, const std::string& filename, std::string& str); |
| 152 | 153 | ||
| 153 | /** | 154 | /** |
| 154 | * Splits the filename into 8.3 format | 155 | * Splits the filename into 8.3 format |
| @@ -257,8 +258,8 @@ public: | |||
| 257 | return WriteArray(&object, 1); | 258 | return WriteArray(&object, 1); |
| 258 | } | 259 | } |
| 259 | 260 | ||
| 260 | std::size_t WriteString(const std::string& str) { | 261 | std::size_t WriteString(std::string_view str) { |
| 261 | return WriteArray(str.c_str(), str.length()); | 262 | return WriteArray(str.data(), str.length()); |
| 262 | } | 263 | } |
| 263 | 264 | ||
| 264 | bool IsOpen() const { | 265 | bool IsOpen() const { |
| @@ -286,8 +287,8 @@ private: | |||
| 286 | template <typename T> | 287 | template <typename T> |
| 287 | void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) { | 288 | void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) { |
| 288 | #ifdef _MSC_VER | 289 | #ifdef _MSC_VER |
| 289 | fstream.open(Common::UTF8ToUTF16W(filename).c_str(), openmode); | 290 | fstream.open(Common::UTF8ToUTF16W(filename), openmode); |
| 290 | #else | 291 | #else |
| 291 | fstream.open(filename.c_str(), openmode); | 292 | fstream.open(filename, openmode); |
| 292 | #endif | 293 | #endif |
| 293 | } | 294 | } |
diff --git a/src/common/zstd_compression.cpp b/src/common/zstd_compression.cpp index 60a35c67c..978526492 100644 --- a/src/common/zstd_compression.cpp +++ b/src/common/zstd_compression.cpp | |||
| @@ -2,8 +2,6 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <algorithm> | 5 | #include <algorithm> |
| 8 | #include <zstd.h> | 6 | #include <zstd.h> |
| 9 | 7 | ||
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 { | |||
| 14 | constexpr u64 MAX_VALUE_TO_MULTIPLY = std::numeric_limits<s64>::max() / BASE_CLOCK_RATE; | 14 | constexpr u64 MAX_VALUE_TO_MULTIPLY = std::numeric_limits<s64>::max() / BASE_CLOCK_RATE; |
| 15 | 15 | ||
| 16 | s64 usToCycles(s64 us) { | 16 | s64 usToCycles(s64 us) { |
| 17 | if (us / 1000000 > MAX_VALUE_TO_MULTIPLY) { | 17 | if (static_cast<u64>(us / 1000000) > MAX_VALUE_TO_MULTIPLY) { |
| 18 | LOG_ERROR(Core_Timing, "Integer overflow, use max value"); | 18 | LOG_ERROR(Core_Timing, "Integer overflow, use max value"); |
| 19 | return std::numeric_limits<s64>::max(); | 19 | return std::numeric_limits<s64>::max(); |
| 20 | } | 20 | } |
| 21 | if (us > MAX_VALUE_TO_MULTIPLY) { | 21 | if (static_cast<u64>(us) > MAX_VALUE_TO_MULTIPLY) { |
| 22 | LOG_DEBUG(Core_Timing, "Time very big, do rounding"); | 22 | LOG_DEBUG(Core_Timing, "Time very big, do rounding"); |
| 23 | return BASE_CLOCK_RATE * (us / 1000000); | 23 | return BASE_CLOCK_RATE * (us / 1000000); |
| 24 | } | 24 | } |
| @@ -38,11 +38,11 @@ s64 usToCycles(u64 us) { | |||
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | s64 nsToCycles(s64 ns) { | 40 | s64 nsToCycles(s64 ns) { |
| 41 | if (ns / 1000000000 > MAX_VALUE_TO_MULTIPLY) { | 41 | if (static_cast<u64>(ns / 1000000000) > MAX_VALUE_TO_MULTIPLY) { |
| 42 | LOG_ERROR(Core_Timing, "Integer overflow, use max value"); | 42 | LOG_ERROR(Core_Timing, "Integer overflow, use max value"); |
| 43 | return std::numeric_limits<s64>::max(); | 43 | return std::numeric_limits<s64>::max(); |
| 44 | } | 44 | } |
| 45 | if (ns > MAX_VALUE_TO_MULTIPLY) { | 45 | if (static_cast<u64>(ns) > MAX_VALUE_TO_MULTIPLY) { |
| 46 | LOG_DEBUG(Core_Timing, "Time very big, do rounding"); | 46 | LOG_DEBUG(Core_Timing, "Time very big, do rounding"); |
| 47 | return BASE_CLOCK_RATE * (ns / 1000000000); | 47 | return BASE_CLOCK_RATE * (ns / 1000000000); |
| 48 | } | 48 | } |
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index 1320bbe77..eda466a5d 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp | |||
| @@ -10,6 +10,8 @@ | |||
| 10 | 10 | ||
| 11 | namespace Core::Frontend { | 11 | namespace Core::Frontend { |
| 12 | 12 | ||
| 13 | GraphicsContext::~GraphicsContext() = default; | ||
| 14 | |||
| 13 | class EmuWindow::TouchState : public Input::Factory<Input::TouchDevice>, | 15 | class EmuWindow::TouchState : public Input::Factory<Input::TouchDevice>, |
| 14 | public std::enable_shared_from_this<TouchState> { | 16 | public std::enable_shared_from_this<TouchState> { |
| 15 | public: | 17 | public: |
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 70a522556..4a9912641 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h | |||
| @@ -19,6 +19,8 @@ namespace Core::Frontend { | |||
| 19 | */ | 19 | */ |
| 20 | class GraphicsContext { | 20 | class GraphicsContext { |
| 21 | public: | 21 | public: |
| 22 | virtual ~GraphicsContext(); | ||
| 23 | |||
| 22 | /// Makes the graphics context current for the caller thread | 24 | /// Makes the graphics context current for the caller thread |
| 23 | virtual void MakeCurrent() = 0; | 25 | virtual void MakeCurrent() = 0; |
| 24 | 26 | ||
| @@ -167,8 +169,7 @@ private: | |||
| 167 | * For the request to be honored, EmuWindow implementations will usually reimplement this | 169 | * For the request to be honored, EmuWindow implementations will usually reimplement this |
| 168 | * function. | 170 | * function. |
| 169 | */ | 171 | */ |
| 170 | virtual void OnMinimalClientAreaChangeRequest( | 172 | virtual void OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned>) { |
| 171 | const std::pair<unsigned, unsigned>& minimal_size) { | ||
| 172 | // By default, ignore this request and do nothing. | 173 | // By default, ignore this request and do nothing. |
| 173 | } | 174 | } |
| 174 | 175 | ||
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index ac0e1d796..5bb139483 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h | |||
| @@ -438,7 +438,7 @@ inline float RequestParser::Pop() { | |||
| 438 | template <> | 438 | template <> |
| 439 | inline double RequestParser::Pop() { | 439 | inline double RequestParser::Pop() { |
| 440 | const u64 value = Pop<u64>(); | 440 | const u64 value = Pop<u64>(); |
| 441 | float real; | 441 | double real; |
| 442 | std::memcpy(&real, &value, sizeof(real)); | 442 | std::memcpy(&real, &value, sizeof(real)); |
| 443 | return real; | 443 | return real; |
| 444 | } | 444 | } |
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index fe710eb6e..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 | |||
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | SharedPtr<WritableEvent> HLERequestContext::SleepClientThread( | 45 | SharedPtr<WritableEvent> HLERequestContext::SleepClientThread( |
| 46 | SharedPtr<Thread> thread, const std::string& reason, u64 timeout, WakeupCallback&& callback, | 46 | const std::string& reason, u64 timeout, WakeupCallback&& callback, |
| 47 | SharedPtr<WritableEvent> writable_event) { | 47 | SharedPtr<WritableEvent> writable_event) { |
| 48 | // Put the client thread to sleep until the wait event is signaled or the timeout expires. | 48 | // Put the client thread to sleep until the wait event is signaled or the timeout expires. |
| 49 | thread->SetWakeupCallback([context = *this, callback]( | 49 | thread->SetWakeupCallback([context = *this, callback]( |
| @@ -58,7 +58,7 @@ SharedPtr<WritableEvent> HLERequestContext::SleepClientThread( | |||
| 58 | auto& kernel = Core::System::GetInstance().Kernel(); | 58 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 59 | if (!writable_event) { | 59 | if (!writable_event) { |
| 60 | // Create event if not provided | 60 | // Create event if not provided |
| 61 | const auto pair = WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, | 61 | const auto pair = WritableEvent::CreateEventPair(kernel, ResetType::Automatic, |
| 62 | "HLE Pause Event: " + reason); | 62 | "HLE Pause Event: " + reason); |
| 63 | writable_event = pair.writable; | 63 | writable_event = pair.writable; |
| 64 | } | 64 | } |
| @@ -76,8 +76,9 @@ SharedPtr<WritableEvent> HLERequestContext::SleepClientThread( | |||
| 76 | return writable_event; | 76 | return writable_event; |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_session) | 79 | HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_session, |
| 80 | : server_session(std::move(server_session)) { | 80 | SharedPtr<Thread> thread) |
| 81 | : server_session(std::move(server_session)), thread(std::move(thread)) { | ||
| 81 | cmd_buf[0] = 0; | 82 | cmd_buf[0] = 0; |
| 82 | } | 83 | } |
| 83 | 84 | ||
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: | |||
| 97 | */ | 97 | */ |
| 98 | class HLERequestContext { | 98 | class HLERequestContext { |
| 99 | public: | 99 | public: |
| 100 | explicit HLERequestContext(SharedPtr<ServerSession> session); | 100 | explicit HLERequestContext(SharedPtr<ServerSession> session, SharedPtr<Thread> thread); |
| 101 | ~HLERequestContext(); | 101 | ~HLERequestContext(); |
| 102 | 102 | ||
| 103 | /// Returns a pointer to the IPC command buffer for this request. | 103 | /// Returns a pointer to the IPC command buffer for this request. |
| @@ -119,7 +119,6 @@ public: | |||
| 119 | /** | 119 | /** |
| 120 | * Puts the specified guest thread to sleep until the returned event is signaled or until the | 120 | * Puts the specified guest thread to sleep until the returned event is signaled or until the |
| 121 | * specified timeout expires. | 121 | * specified timeout expires. |
| 122 | * @param thread Thread to be put to sleep. | ||
| 123 | * @param reason Reason for pausing the thread, to be used for debugging purposes. | 122 | * @param reason Reason for pausing the thread, to be used for debugging purposes. |
| 124 | * @param timeout Timeout in nanoseconds after which the thread will be awoken and the callback | 123 | * @param timeout Timeout in nanoseconds after which the thread will be awoken and the callback |
| 125 | * invoked with a Timeout reason. | 124 | * invoked with a Timeout reason. |
| @@ -130,8 +129,8 @@ public: | |||
| 130 | * created. | 129 | * created. |
| 131 | * @returns Event that when signaled will resume the thread and call the callback function. | 130 | * @returns Event that when signaled will resume the thread and call the callback function. |
| 132 | */ | 131 | */ |
| 133 | SharedPtr<WritableEvent> SleepClientThread(SharedPtr<Thread> thread, const std::string& reason, | 132 | SharedPtr<WritableEvent> SleepClientThread(const std::string& reason, u64 timeout, |
| 134 | u64 timeout, WakeupCallback&& callback, | 133 | WakeupCallback&& callback, |
| 135 | SharedPtr<WritableEvent> writable_event = nullptr); | 134 | SharedPtr<WritableEvent> writable_event = nullptr); |
| 136 | 135 | ||
| 137 | /// Populates this context with data from the requesting process/thread. | 136 | /// Populates this context with data from the requesting process/thread. |
| @@ -268,6 +267,7 @@ private: | |||
| 268 | 267 | ||
| 269 | std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; | 268 | std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; |
| 270 | SharedPtr<Kernel::ServerSession> server_session; | 269 | SharedPtr<Kernel::ServerSession> server_session; |
| 270 | SharedPtr<Thread> thread; | ||
| 271 | // TODO(yuriks): Check common usage of this and optimize size accordingly | 271 | // TODO(yuriks): Check common usage of this and optimize size accordingly |
| 272 | boost::container::small_vector<SharedPtr<Object>, 8> move_objects; | 272 | boost::container::small_vector<SharedPtr<Object>, 8> move_objects; |
| 273 | boost::container::small_vector<SharedPtr<Object>, 8> copy_objects; | 273 | boost::container::small_vector<SharedPtr<Object>, 8> copy_objects; |
diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h index 332876c27..2821176a7 100644 --- a/src/core/hle/kernel/object.h +++ b/src/core/hle/kernel/object.h | |||
| @@ -33,8 +33,8 @@ enum class HandleType : u32 { | |||
| 33 | }; | 33 | }; |
| 34 | 34 | ||
| 35 | enum class ResetType { | 35 | enum class ResetType { |
| 36 | OneShot, ///< Reset automatically on object acquisition | 36 | Automatic, ///< Reset automatically on object acquisition |
| 37 | Sticky, ///< Never reset automatically | 37 | Manual, ///< Never reset automatically |
| 38 | }; | 38 | }; |
| 39 | 39 | ||
| 40 | class Object : NonCopyable { | 40 | class Object : NonCopyable { |
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 20d01fc88..0775a89fb 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp | |||
| @@ -241,7 +241,8 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) { | |||
| 241 | } | 241 | } |
| 242 | 242 | ||
| 243 | Process::Process(Core::System& system) | 243 | Process::Process(Core::System& system) |
| 244 | : WaitObject{system.Kernel()}, address_arbiter{system}, mutex{system}, system{system} {} | 244 | : WaitObject{system.Kernel()}, vm_manager{system}, |
| 245 | address_arbiter{system}, mutex{system}, system{system} {} | ||
| 245 | 246 | ||
| 246 | Process::~Process() = default; | 247 | Process::~Process() = default; |
| 247 | 248 | ||
diff --git a/src/core/hle/kernel/readable_event.cpp b/src/core/hle/kernel/readable_event.cpp index c2b798a4e..06463cd26 100644 --- a/src/core/hle/kernel/readable_event.cpp +++ b/src/core/hle/kernel/readable_event.cpp | |||
| @@ -21,8 +21,9 @@ bool ReadableEvent::ShouldWait(const Thread* thread) const { | |||
| 21 | void ReadableEvent::Acquire(Thread* thread) { | 21 | void ReadableEvent::Acquire(Thread* thread) { |
| 22 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); | 22 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); |
| 23 | 23 | ||
| 24 | if (reset_type == ResetType::OneShot) | 24 | if (reset_type == ResetType::Automatic) { |
| 25 | signaled = false; | 25 | signaled = false; |
| 26 | } | ||
| 26 | } | 27 | } |
| 27 | 28 | ||
| 28 | void ReadableEvent::Signal() { | 29 | void ReadableEvent::Signal() { |
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) { | |||
| 130 | // The ServerSession received a sync request, this means that there's new data available | 130 | // The ServerSession received a sync request, this means that there's new data available |
| 131 | // from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or | 131 | // from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or |
| 132 | // similar. | 132 | // similar. |
| 133 | Kernel::HLERequestContext context(this); | 133 | Kernel::HLERequestContext context(this, thread); |
| 134 | u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress()); | 134 | u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress()); |
| 135 | context.PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf); | 135 | context.PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf); |
| 136 | 136 | ||
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 2dcf174c5..5a5851f66 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -1255,8 +1255,8 @@ static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_hand | |||
| 1255 | return vm_manager.MapCodeMemory(dst_address, src_address, size); | 1255 | return vm_manager.MapCodeMemory(dst_address, src_address, size); |
| 1256 | } | 1256 | } |
| 1257 | 1257 | ||
| 1258 | ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, | 1258 | static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle, |
| 1259 | u64 src_address, u64 size) { | 1259 | u64 dst_address, u64 src_address, u64 size) { |
| 1260 | LOG_DEBUG(Kernel_SVC, | 1260 | LOG_DEBUG(Kernel_SVC, |
| 1261 | "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, " | 1261 | "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, " |
| 1262 | "size=0x{:016X}", | 1262 | "size=0x{:016X}", |
| @@ -1342,7 +1342,7 @@ static void ExitProcess(Core::System& system) { | |||
| 1342 | /// Creates a new thread | 1342 | /// Creates a new thread |
| 1343 | static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg, | 1343 | static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg, |
| 1344 | VAddr stack_top, u32 priority, s32 processor_id) { | 1344 | VAddr stack_top, u32 priority, s32 processor_id) { |
| 1345 | LOG_TRACE(Kernel_SVC, | 1345 | LOG_DEBUG(Kernel_SVC, |
| 1346 | "called entrypoint=0x{:08X}, arg=0x{:08X}, stacktop=0x{:08X}, " | 1346 | "called entrypoint=0x{:08X}, arg=0x{:08X}, stacktop=0x{:08X}, " |
| 1347 | "threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}", | 1347 | "threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}", |
| 1348 | entry_point, arg, stack_top, priority, processor_id, *out_handle); | 1348 | entry_point, arg, stack_top, priority, processor_id, *out_handle); |
| @@ -1402,7 +1402,7 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e | |||
| 1402 | 1402 | ||
| 1403 | /// Starts the thread for the provided handle | 1403 | /// Starts the thread for the provided handle |
| 1404 | static ResultCode StartThread(Core::System& system, Handle thread_handle) { | 1404 | static ResultCode StartThread(Core::System& system, Handle thread_handle) { |
| 1405 | LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); | 1405 | LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle); |
| 1406 | 1406 | ||
| 1407 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | 1407 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 1408 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); | 1408 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); |
| @@ -1425,7 +1425,7 @@ static ResultCode StartThread(Core::System& system, Handle thread_handle) { | |||
| 1425 | 1425 | ||
| 1426 | /// Called when a thread exits | 1426 | /// Called when a thread exits |
| 1427 | static void ExitThread(Core::System& system) { | 1427 | static void ExitThread(Core::System& system) { |
| 1428 | LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC()); | 1428 | LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC()); |
| 1429 | 1429 | ||
| 1430 | auto* const current_thread = system.CurrentScheduler().GetCurrentThread(); | 1430 | auto* const current_thread = system.CurrentScheduler().GetCurrentThread(); |
| 1431 | current_thread->Stop(); | 1431 | current_thread->Stop(); |
| @@ -1435,7 +1435,7 @@ static void ExitThread(Core::System& system) { | |||
| 1435 | 1435 | ||
| 1436 | /// Sleep the current thread | 1436 | /// Sleep the current thread |
| 1437 | static void SleepThread(Core::System& system, s64 nanoseconds) { | 1437 | static void SleepThread(Core::System& system, s64 nanoseconds) { |
| 1438 | LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); | 1438 | LOG_DEBUG(Kernel_SVC, "called nanoseconds={}", nanoseconds); |
| 1439 | 1439 | ||
| 1440 | enum class SleepType : s64 { | 1440 | enum class SleepType : s64 { |
| 1441 | YieldWithoutLoadBalancing = 0, | 1441 | YieldWithoutLoadBalancing = 0, |
| @@ -1880,52 +1880,59 @@ static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, | |||
| 1880 | } | 1880 | } |
| 1881 | 1881 | ||
| 1882 | static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, u32 core, | 1882 | static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, u32 core, |
| 1883 | u64 mask) { | 1883 | u64 affinity_mask) { |
| 1884 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:016X}, core=0x{:X}", thread_handle, | 1884 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, core=0x{:X}, affinity_mask=0x{:016X}", |
| 1885 | mask, core); | 1885 | thread_handle, core, affinity_mask); |
| 1886 | 1886 | ||
| 1887 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | 1887 | const auto* const current_process = system.Kernel().CurrentProcess(); |
| 1888 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); | ||
| 1889 | if (!thread) { | ||
| 1890 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", | ||
| 1891 | thread_handle); | ||
| 1892 | return ERR_INVALID_HANDLE; | ||
| 1893 | } | ||
| 1894 | 1888 | ||
| 1895 | if (core == static_cast<u32>(THREADPROCESSORID_IDEAL)) { | 1889 | if (core == static_cast<u32>(THREADPROCESSORID_IDEAL)) { |
| 1896 | const u8 ideal_cpu_core = thread->GetOwnerProcess()->GetIdealCore(); | 1890 | const u8 ideal_cpu_core = current_process->GetIdealCore(); |
| 1897 | 1891 | ||
| 1898 | ASSERT(ideal_cpu_core != static_cast<u8>(THREADPROCESSORID_IDEAL)); | 1892 | ASSERT(ideal_cpu_core != static_cast<u8>(THREADPROCESSORID_IDEAL)); |
| 1899 | 1893 | ||
| 1900 | // Set the target CPU to the ideal core specified by the process. | 1894 | // Set the target CPU to the ideal core specified by the process. |
| 1901 | core = ideal_cpu_core; | 1895 | core = ideal_cpu_core; |
| 1902 | mask = 1ULL << core; | 1896 | affinity_mask = 1ULL << core; |
| 1903 | } | 1897 | } else { |
| 1904 | 1898 | const u64 core_mask = current_process->GetCoreMask(); | |
| 1905 | if (mask == 0) { | 1899 | |
| 1906 | LOG_ERROR(Kernel_SVC, "Mask is 0"); | 1900 | if ((core_mask | affinity_mask) != core_mask) { |
| 1907 | return ERR_INVALID_COMBINATION; | 1901 | LOG_ERROR( |
| 1908 | } | 1902 | Kernel_SVC, |
| 1903 | "Invalid processor ID specified (core_mask=0x{:08X}, affinity_mask=0x{:016X})", | ||
| 1904 | core_mask, affinity_mask); | ||
| 1905 | return ERR_INVALID_PROCESSOR_ID; | ||
| 1906 | } | ||
| 1909 | 1907 | ||
| 1910 | /// This value is used to only change the affinity mask without changing the current ideal core. | 1908 | if (affinity_mask == 0) { |
| 1911 | static constexpr u32 OnlyChangeMask = static_cast<u32>(-3); | 1909 | LOG_ERROR(Kernel_SVC, "Specfified affinity mask is zero."); |
| 1910 | return ERR_INVALID_COMBINATION; | ||
| 1911 | } | ||
| 1912 | 1912 | ||
| 1913 | if (core == OnlyChangeMask) { | 1913 | if (core < Core::NUM_CPU_CORES) { |
| 1914 | core = thread->GetIdealCore(); | 1914 | if ((affinity_mask & (1ULL << core)) == 0) { |
| 1915 | } else if (core >= Core::NUM_CPU_CORES && core != static_cast<u32>(-1)) { | 1915 | LOG_ERROR(Kernel_SVC, |
| 1916 | LOG_ERROR(Kernel_SVC, "Invalid core specified, got {}", core); | 1916 | "Core is not enabled for the current mask, core={}, mask={:016X}", core, |
| 1917 | return ERR_INVALID_PROCESSOR_ID; | 1917 | affinity_mask); |
| 1918 | return ERR_INVALID_COMBINATION; | ||
| 1919 | } | ||
| 1920 | } else if (core != static_cast<u32>(THREADPROCESSORID_DONT_CARE) && | ||
| 1921 | core != static_cast<u32>(THREADPROCESSORID_DONT_UPDATE)) { | ||
| 1922 | LOG_ERROR(Kernel_SVC, "Invalid processor ID specified (core={}).", core); | ||
| 1923 | return ERR_INVALID_PROCESSOR_ID; | ||
| 1924 | } | ||
| 1918 | } | 1925 | } |
| 1919 | 1926 | ||
| 1920 | // Error out if the input core isn't enabled in the input mask. | 1927 | const auto& handle_table = current_process->GetHandleTable(); |
| 1921 | if (core < Core::NUM_CPU_CORES && (mask & (1ull << core)) == 0) { | 1928 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); |
| 1922 | LOG_ERROR(Kernel_SVC, "Core is not enabled for the current mask, core={}, mask={:016X}", | 1929 | if (!thread) { |
| 1923 | core, mask); | 1930 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", |
| 1924 | return ERR_INVALID_COMBINATION; | 1931 | thread_handle); |
| 1932 | return ERR_INVALID_HANDLE; | ||
| 1925 | } | 1933 | } |
| 1926 | 1934 | ||
| 1927 | thread->ChangeCore(core, mask); | 1935 | thread->ChangeCore(core, affinity_mask); |
| 1928 | |||
| 1929 | return RESULT_SUCCESS; | 1936 | return RESULT_SUCCESS; |
| 1930 | } | 1937 | } |
| 1931 | 1938 | ||
| @@ -1980,7 +1987,7 @@ static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle | |||
| 1980 | 1987 | ||
| 1981 | auto& kernel = system.Kernel(); | 1988 | auto& kernel = system.Kernel(); |
| 1982 | const auto [readable_event, writable_event] = | 1989 | const auto [readable_event, writable_event] = |
| 1983 | WritableEvent::CreateEventPair(kernel, ResetType::Sticky, "CreateEvent"); | 1990 | WritableEvent::CreateEventPair(kernel, ResetType::Manual, "CreateEvent"); |
| 1984 | 1991 | ||
| 1985 | HandleTable& handle_table = kernel.CurrentProcess()->GetHandleTable(); | 1992 | HandleTable& handle_table = kernel.CurrentProcess()->GetHandleTable(); |
| 1986 | 1993 | ||
| @@ -2183,8 +2190,8 @@ static ResultCode GetProcessList(Core::System& system, u32* out_num_processes, | |||
| 2183 | return RESULT_SUCCESS; | 2190 | return RESULT_SUCCESS; |
| 2184 | } | 2191 | } |
| 2185 | 2192 | ||
| 2186 | ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids, | 2193 | static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids, |
| 2187 | u32 out_thread_ids_size, Handle debug_handle) { | 2194 | u32 out_thread_ids_size, Handle debug_handle) { |
| 2188 | // TODO: Handle this case when debug events are supported. | 2195 | // TODO: Handle this case when debug events are supported. |
| 2189 | UNIMPLEMENTED_IF(debug_handle != InvalidHandle); | 2196 | UNIMPLEMENTED_IF(debug_handle != InvalidHandle); |
| 2190 | 2197 | ||
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index f07332f02..b4b9cda7c 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -30,12 +30,21 @@ enum ThreadPriority : u32 { | |||
| 30 | }; | 30 | }; |
| 31 | 31 | ||
| 32 | enum ThreadProcessorId : s32 { | 32 | enum ThreadProcessorId : s32 { |
| 33 | THREADPROCESSORID_IDEAL = -2, ///< Run thread on the ideal core specified by the process. | 33 | /// Indicates that no particular processor core is preferred. |
| 34 | THREADPROCESSORID_0 = 0, ///< Run thread on core 0 | 34 | THREADPROCESSORID_DONT_CARE = -1, |
| 35 | THREADPROCESSORID_1 = 1, ///< Run thread on core 1 | 35 | |
| 36 | THREADPROCESSORID_2 = 2, ///< Run thread on core 2 | 36 | /// Run thread on the ideal core specified by the process. |
| 37 | THREADPROCESSORID_3 = 3, ///< Run thread on core 3 | 37 | THREADPROCESSORID_IDEAL = -2, |
| 38 | THREADPROCESSORID_MAX = 4, ///< Processor ID must be less than this | 38 | |
| 39 | /// Indicates that the preferred processor ID shouldn't be updated in | ||
| 40 | /// a core mask setting operation. | ||
| 41 | THREADPROCESSORID_DONT_UPDATE = -3, | ||
| 42 | |||
| 43 | THREADPROCESSORID_0 = 0, ///< Run thread on core 0 | ||
| 44 | THREADPROCESSORID_1 = 1, ///< Run thread on core 1 | ||
| 45 | THREADPROCESSORID_2 = 2, ///< Run thread on core 2 | ||
| 46 | THREADPROCESSORID_3 = 3, ///< Run thread on core 3 | ||
| 47 | THREADPROCESSORID_MAX = 4, ///< Processor ID must be less than this | ||
| 39 | 48 | ||
| 40 | /// Allowed CPU mask | 49 | /// Allowed CPU mask |
| 41 | THREADPROCESSORID_DEFAULT_MASK = (1 << THREADPROCESSORID_0) | (1 << THREADPROCESSORID_1) | | 50 | THREADPROCESSORID_DEFAULT_MASK = (1 << THREADPROCESSORID_0) | (1 << THREADPROCESSORID_1) | |
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index f0c0c12fc..48b13cfdd 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp | |||
| @@ -62,7 +62,7 @@ bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const { | |||
| 62 | return true; | 62 | return true; |
| 63 | } | 63 | } |
| 64 | 64 | ||
| 65 | VMManager::VMManager() { | 65 | VMManager::VMManager(Core::System& system) : system{system} { |
| 66 | // Default to assuming a 39-bit address space. This way we have a sane | 66 | // Default to assuming a 39-bit address space. This way we have a sane |
| 67 | // starting point with executables that don't provide metadata. | 67 | // starting point with executables that don't provide metadata. |
| 68 | Reset(FileSys::ProgramAddressSpaceType::Is39Bit); | 68 | Reset(FileSys::ProgramAddressSpaceType::Is39Bit); |
| @@ -111,7 +111,6 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target, | |||
| 111 | VirtualMemoryArea& final_vma = vma_handle->second; | 111 | VirtualMemoryArea& final_vma = vma_handle->second; |
| 112 | ASSERT(final_vma.size == size); | 112 | ASSERT(final_vma.size == size); |
| 113 | 113 | ||
| 114 | auto& system = Core::System::GetInstance(); | ||
| 115 | system.ArmInterface(0).MapBackingMemory(target, size, block->data() + offset, | 114 | system.ArmInterface(0).MapBackingMemory(target, size, block->data() + offset, |
| 116 | VMAPermission::ReadWriteExecute); | 115 | VMAPermission::ReadWriteExecute); |
| 117 | system.ArmInterface(1).MapBackingMemory(target, size, block->data() + offset, | 116 | system.ArmInterface(1).MapBackingMemory(target, size, block->data() + offset, |
| @@ -140,7 +139,6 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me | |||
| 140 | VirtualMemoryArea& final_vma = vma_handle->second; | 139 | VirtualMemoryArea& final_vma = vma_handle->second; |
| 141 | ASSERT(final_vma.size == size); | 140 | ASSERT(final_vma.size == size); |
| 142 | 141 | ||
| 143 | auto& system = Core::System::GetInstance(); | ||
| 144 | system.ArmInterface(0).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute); | 142 | system.ArmInterface(0).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute); |
| 145 | system.ArmInterface(1).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute); | 143 | system.ArmInterface(1).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute); |
| 146 | system.ArmInterface(2).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute); | 144 | system.ArmInterface(2).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute); |
| @@ -223,7 +221,6 @@ ResultCode VMManager::UnmapRange(VAddr target, u64 size) { | |||
| 223 | 221 | ||
| 224 | ASSERT(FindVMA(target)->second.size >= size); | 222 | ASSERT(FindVMA(target)->second.size >= size); |
| 225 | 223 | ||
| 226 | auto& system = Core::System::GetInstance(); | ||
| 227 | system.ArmInterface(0).UnmapMemory(target, size); | 224 | system.ArmInterface(0).UnmapMemory(target, size); |
| 228 | system.ArmInterface(1).UnmapMemory(target, size); | 225 | system.ArmInterface(1).UnmapMemory(target, size); |
| 229 | system.ArmInterface(2).UnmapMemory(target, size); | 226 | system.ArmInterface(2).UnmapMemory(target, size); |
| @@ -376,7 +373,7 @@ ResultCode VMManager::UnmapCodeMemory(VAddr dst_address, VAddr src_address, u64 | |||
| 376 | Reprotect(src_vma_iter, VMAPermission::ReadWrite); | 373 | Reprotect(src_vma_iter, VMAPermission::ReadWrite); |
| 377 | 374 | ||
| 378 | if (dst_memory_state == MemoryState::ModuleCode) { | 375 | if (dst_memory_state == MemoryState::ModuleCode) { |
| 379 | Core::System::GetInstance().InvalidateCpuInstructionCaches(); | 376 | system.InvalidateCpuInstructionCaches(); |
| 380 | } | 377 | } |
| 381 | 378 | ||
| 382 | return unmap_result; | 379 | return unmap_result; |
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 288eb9450..ec84d9a70 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h | |||
| @@ -14,6 +14,10 @@ | |||
| 14 | #include "core/hle/result.h" | 14 | #include "core/hle/result.h" |
| 15 | #include "core/memory.h" | 15 | #include "core/memory.h" |
| 16 | 16 | ||
| 17 | namespace Core { | ||
| 18 | class System; | ||
| 19 | } | ||
| 20 | |||
| 17 | namespace FileSys { | 21 | namespace FileSys { |
| 18 | enum class ProgramAddressSpaceType : u8; | 22 | enum class ProgramAddressSpaceType : u8; |
| 19 | } | 23 | } |
| @@ -321,7 +325,7 @@ class VMManager final { | |||
| 321 | public: | 325 | public: |
| 322 | using VMAHandle = VMAMap::const_iterator; | 326 | using VMAHandle = VMAMap::const_iterator; |
| 323 | 327 | ||
| 324 | VMManager(); | 328 | explicit VMManager(Core::System& system); |
| 325 | ~VMManager(); | 329 | ~VMManager(); |
| 326 | 330 | ||
| 327 | /// Clears the address space map, re-initializing with a single free area. | 331 | /// Clears the address space map, re-initializing with a single free area. |
| @@ -712,5 +716,7 @@ private: | |||
| 712 | // The end of the currently allocated heap. This is not an inclusive | 716 | // The end of the currently allocated heap. This is not an inclusive |
| 713 | // end of the range. This is essentially 'base_address + current_size'. | 717 | // end of the range. This is essentially 'base_address + current_size'. |
| 714 | VAddr heap_end = 0; | 718 | VAddr heap_end = 0; |
| 719 | |||
| 720 | Core::System& system; | ||
| 715 | }; | 721 | }; |
| 716 | } // namespace Kernel | 722 | } // namespace Kernel |
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 26a665bfd..1a32a109f 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -276,7 +276,7 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger | |||
| 276 | RegisterHandlers(functions); | 276 | RegisterHandlers(functions); |
| 277 | 277 | ||
| 278 | auto& kernel = Core::System::GetInstance().Kernel(); | 278 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 279 | launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, | 279 | launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual, |
| 280 | "ISelfController:LaunchableEvent"); | 280 | "ISelfController:LaunchableEvent"); |
| 281 | } | 281 | } |
| 282 | 282 | ||
| @@ -442,10 +442,10 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c | |||
| 442 | 442 | ||
| 443 | AppletMessageQueue::AppletMessageQueue() { | 443 | AppletMessageQueue::AppletMessageQueue() { |
| 444 | auto& kernel = Core::System::GetInstance().Kernel(); | 444 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 445 | on_new_message = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, | 445 | on_new_message = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual, |
| 446 | "AMMessageQueue:OnMessageRecieved"); | 446 | "AMMessageQueue:OnMessageRecieved"); |
| 447 | on_operation_mode_changed = Kernel::WritableEvent::CreateEventPair( | 447 | on_operation_mode_changed = Kernel::WritableEvent::CreateEventPair( |
| 448 | kernel, Kernel::ResetType::OneShot, "AMMessageQueue:OperationModeChanged"); | 448 | kernel, Kernel::ResetType::Automatic, "AMMessageQueue:OperationModeChanged"); |
| 449 | } | 449 | } |
| 450 | 450 | ||
| 451 | AppletMessageQueue::~AppletMessageQueue() = default; | 451 | AppletMessageQueue::~AppletMessageQueue() = default; |
| @@ -835,6 +835,7 @@ void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) { | |||
| 835 | 835 | ||
| 836 | IPC::ResponseBuilder rb{ctx, 2}; | 836 | IPC::ResponseBuilder rb{ctx, 2}; |
| 837 | rb.Push(ERR_SIZE_OUT_OF_BOUNDS); | 837 | rb.Push(ERR_SIZE_OUT_OF_BOUNDS); |
| 838 | return; | ||
| 838 | } | 839 | } |
| 839 | 840 | ||
| 840 | std::memcpy(backing.buffer.data() + offset, data.data(), data.size()); | 841 | std::memcpy(backing.buffer.data() + offset, data.data(), data.size()); |
| @@ -857,6 +858,7 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) { | |||
| 857 | 858 | ||
| 858 | IPC::ResponseBuilder rb{ctx, 2}; | 859 | IPC::ResponseBuilder rb{ctx, 2}; |
| 859 | rb.Push(ERR_SIZE_OUT_OF_BOUNDS); | 860 | rb.Push(ERR_SIZE_OUT_OF_BOUNDS); |
| 861 | return; | ||
| 860 | } | 862 | } |
| 861 | 863 | ||
| 862 | ctx.WriteBuffer(backing.buffer.data() + offset, size); | 864 | ctx.WriteBuffer(backing.buffer.data() + offset, size); |
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index 7f70b10df..e812c66e9 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp | |||
| @@ -26,11 +26,11 @@ namespace Service::AM::Applets { | |||
| 26 | AppletDataBroker::AppletDataBroker() { | 26 | AppletDataBroker::AppletDataBroker() { |
| 27 | auto& kernel = Core::System::GetInstance().Kernel(); | 27 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 28 | state_changed_event = Kernel::WritableEvent::CreateEventPair( | 28 | state_changed_event = Kernel::WritableEvent::CreateEventPair( |
| 29 | kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:StateChangedEvent"); | 29 | kernel, Kernel::ResetType::Manual, "ILibraryAppletAccessor:StateChangedEvent"); |
| 30 | pop_out_data_event = Kernel::WritableEvent::CreateEventPair( | 30 | pop_out_data_event = Kernel::WritableEvent::CreateEventPair( |
| 31 | kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:PopDataOutEvent"); | 31 | kernel, Kernel::ResetType::Manual, "ILibraryAppletAccessor:PopDataOutEvent"); |
| 32 | pop_interactive_out_data_event = Kernel::WritableEvent::CreateEventPair( | 32 | pop_interactive_out_data_event = Kernel::WritableEvent::CreateEventPair( |
| 33 | kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:PopInteractiveDataOutEvent"); | 33 | kernel, Kernel::ResetType::Manual, "ILibraryAppletAccessor:PopInteractiveDataOutEvent"); |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | AppletDataBroker::~AppletDataBroker() = default; | 36 | AppletDataBroker::~AppletDataBroker() = default; |
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index 51d8c26b4..d3e97776b 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp | |||
| @@ -9,7 +9,6 @@ | |||
| 9 | #include "core/file_sys/content_archive.h" | 9 | #include "core/file_sys/content_archive.h" |
| 10 | #include "core/file_sys/control_metadata.h" | 10 | #include "core/file_sys/control_metadata.h" |
| 11 | #include "core/file_sys/nca_metadata.h" | 11 | #include "core/file_sys/nca_metadata.h" |
| 12 | #include "core/file_sys/partition_filesystem.h" | ||
| 13 | #include "core/file_sys/patch_manager.h" | 12 | #include "core/file_sys/patch_manager.h" |
| 14 | #include "core/file_sys/registered_cache.h" | 13 | #include "core/file_sys/registered_cache.h" |
| 15 | #include "core/hle/ipc_helpers.h" | 14 | #include "core/hle/ipc_helpers.h" |
| @@ -18,7 +17,6 @@ | |||
| 18 | #include "core/hle/kernel/readable_event.h" | 17 | #include "core/hle/kernel/readable_event.h" |
| 19 | #include "core/hle/kernel/writable_event.h" | 18 | #include "core/hle/kernel/writable_event.h" |
| 20 | #include "core/hle/service/aoc/aoc_u.h" | 19 | #include "core/hle/service/aoc/aoc_u.h" |
| 21 | #include "core/hle/service/filesystem/filesystem.h" | ||
| 22 | #include "core/loader/loader.h" | 20 | #include "core/loader/loader.h" |
| 23 | #include "core/settings.h" | 21 | #include "core/settings.h" |
| 24 | 22 | ||
| @@ -68,14 +66,22 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs | |||
| 68 | RegisterHandlers(functions); | 66 | RegisterHandlers(functions); |
| 69 | 67 | ||
| 70 | auto& kernel = Core::System::GetInstance().Kernel(); | 68 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 71 | aoc_change_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, | 69 | aoc_change_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual, |
| 72 | "GetAddOnContentListChanged:Event"); | 70 | "GetAddOnContentListChanged:Event"); |
| 73 | } | 71 | } |
| 74 | 72 | ||
| 75 | AOC_U::~AOC_U() = default; | 73 | AOC_U::~AOC_U() = default; |
| 76 | 74 | ||
| 77 | void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) { | 75 | void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) { |
| 78 | LOG_DEBUG(Service_AOC, "called"); | 76 | struct Parameters { |
| 77 | u64 process_id; | ||
| 78 | }; | ||
| 79 | static_assert(sizeof(Parameters) == 8); | ||
| 80 | |||
| 81 | IPC::RequestParser rp{ctx}; | ||
| 82 | const auto params = rp.PopRaw<Parameters>(); | ||
| 83 | |||
| 84 | LOG_DEBUG(Service_AOC, "called. process_id={}", params.process_id); | ||
| 79 | 85 | ||
| 80 | IPC::ResponseBuilder rb{ctx, 3}; | 86 | IPC::ResponseBuilder rb{ctx, 3}; |
| 81 | rb.Push(RESULT_SUCCESS); | 87 | rb.Push(RESULT_SUCCESS); |
| @@ -94,23 +100,32 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) { | |||
| 94 | } | 100 | } |
| 95 | 101 | ||
| 96 | void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { | 102 | void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { |
| 103 | struct Parameters { | ||
| 104 | u32 offset; | ||
| 105 | u32 count; | ||
| 106 | u64 process_id; | ||
| 107 | }; | ||
| 108 | static_assert(sizeof(Parameters) == 16); | ||
| 109 | |||
| 97 | IPC::RequestParser rp{ctx}; | 110 | IPC::RequestParser rp{ctx}; |
| 111 | const auto [offset, count, process_id] = rp.PopRaw<Parameters>(); | ||
| 98 | 112 | ||
| 99 | const auto offset = rp.PopRaw<u32>(); | 113 | LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count, |
| 100 | auto count = rp.PopRaw<u32>(); | 114 | process_id); |
| 101 | LOG_DEBUG(Service_AOC, "called with offset={}, count={}", offset, count); | ||
| 102 | 115 | ||
| 103 | const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); | 116 | const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); |
| 104 | 117 | ||
| 105 | std::vector<u32> out; | 118 | std::vector<u32> out; |
| 106 | for (size_t i = 0; i < add_on_content.size(); ++i) { | ||
| 107 | if ((add_on_content[i] & DLC_BASE_TITLE_ID_MASK) == current) | ||
| 108 | out.push_back(static_cast<u32>(add_on_content[i] & 0x7FF)); | ||
| 109 | } | ||
| 110 | |||
| 111 | const auto& disabled = Settings::values.disabled_addons[current]; | 119 | const auto& disabled = Settings::values.disabled_addons[current]; |
| 112 | if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) | 120 | if (std::find(disabled.begin(), disabled.end(), "DLC") == disabled.end()) { |
| 113 | out = {}; | 121 | for (u64 content_id : add_on_content) { |
| 122 | if ((content_id & DLC_BASE_TITLE_ID_MASK) != current) { | ||
| 123 | continue; | ||
| 124 | } | ||
| 125 | |||
| 126 | out.push_back(static_cast<u32>(content_id & 0x7FF)); | ||
| 127 | } | ||
| 128 | } | ||
| 114 | 129 | ||
| 115 | if (out.size() < offset) { | 130 | if (out.size() < offset) { |
| 116 | IPC::ResponseBuilder rb{ctx, 2}; | 131 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -119,22 +134,31 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { | |||
| 119 | return; | 134 | return; |
| 120 | } | 135 | } |
| 121 | 136 | ||
| 122 | count = static_cast<u32>(std::min<size_t>(out.size() - offset, count)); | 137 | const auto out_count = static_cast<u32>(std::min<size_t>(out.size() - offset, count)); |
| 123 | std::rotate(out.begin(), out.begin() + offset, out.end()); | 138 | std::rotate(out.begin(), out.begin() + offset, out.end()); |
| 124 | out.resize(count); | 139 | out.resize(out_count); |
| 125 | 140 | ||
| 126 | ctx.WriteBuffer(out); | 141 | ctx.WriteBuffer(out); |
| 127 | 142 | ||
| 128 | IPC::ResponseBuilder rb{ctx, 3}; | 143 | IPC::ResponseBuilder rb{ctx, 3}; |
| 129 | rb.Push(RESULT_SUCCESS); | 144 | rb.Push(RESULT_SUCCESS); |
| 130 | rb.Push(count); | 145 | rb.Push(out_count); |
| 131 | } | 146 | } |
| 132 | 147 | ||
| 133 | void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) { | 148 | void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) { |
| 134 | LOG_DEBUG(Service_AOC, "called"); | 149 | struct Parameters { |
| 150 | u64 process_id; | ||
| 151 | }; | ||
| 152 | static_assert(sizeof(Parameters) == 8); | ||
| 153 | |||
| 154 | IPC::RequestParser rp{ctx}; | ||
| 155 | const auto params = rp.PopRaw<Parameters>(); | ||
| 156 | |||
| 157 | LOG_DEBUG(Service_AOC, "called. process_id={}", params.process_id); | ||
| 135 | 158 | ||
| 136 | IPC::ResponseBuilder rb{ctx, 4}; | 159 | IPC::ResponseBuilder rb{ctx, 4}; |
| 137 | rb.Push(RESULT_SUCCESS); | 160 | rb.Push(RESULT_SUCCESS); |
| 161 | |||
| 138 | const auto title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); | 162 | const auto title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); |
| 139 | FileSys::PatchManager pm{title_id}; | 163 | FileSys::PatchManager pm{title_id}; |
| 140 | 164 | ||
| @@ -148,10 +172,17 @@ void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) { | |||
| 148 | } | 172 | } |
| 149 | 173 | ||
| 150 | void AOC_U::PrepareAddOnContent(Kernel::HLERequestContext& ctx) { | 174 | void AOC_U::PrepareAddOnContent(Kernel::HLERequestContext& ctx) { |
| 175 | struct Parameters { | ||
| 176 | s32 addon_index; | ||
| 177 | u64 process_id; | ||
| 178 | }; | ||
| 179 | static_assert(sizeof(Parameters) == 16); | ||
| 180 | |||
| 151 | IPC::RequestParser rp{ctx}; | 181 | IPC::RequestParser rp{ctx}; |
| 182 | const auto [addon_index, process_id] = rp.PopRaw<Parameters>(); | ||
| 152 | 183 | ||
| 153 | const auto aoc_id = rp.PopRaw<u32>(); | 184 | LOG_WARNING(Service_AOC, "(STUBBED) called with addon_index={}, process_id={}", addon_index, |
| 154 | LOG_WARNING(Service_AOC, "(STUBBED) called with aoc_id={:08X}", aoc_id); | 185 | process_id); |
| 155 | 186 | ||
| 156 | IPC::ResponseBuilder rb{ctx, 2}; | 187 | IPC::ResponseBuilder rb{ctx, 2}; |
| 157 | rb.Push(RESULT_SUCCESS); | 188 | rb.Push(RESULT_SUCCESS); |
diff --git a/src/core/hle/service/audio/audctl.cpp b/src/core/hle/service/audio/audctl.cpp index f43e512e9..6a01d4d29 100644 --- a/src/core/hle/service/audio/audctl.cpp +++ b/src/core/hle/service/audio/audctl.cpp | |||
| @@ -50,7 +50,7 @@ void AudCtl::GetTargetVolumeMin(Kernel::HLERequestContext& ctx) { | |||
| 50 | LOG_DEBUG(Audio, "called."); | 50 | LOG_DEBUG(Audio, "called."); |
| 51 | 51 | ||
| 52 | // This service function is currently hardcoded on the | 52 | // This service function is currently hardcoded on the |
| 53 | // actual console to this value (as of 6.0.0). | 53 | // actual console to this value (as of 8.0.0). |
| 54 | constexpr s32 target_min_volume = 0; | 54 | constexpr s32 target_min_volume = 0; |
| 55 | 55 | ||
| 56 | IPC::ResponseBuilder rb{ctx, 3}; | 56 | IPC::ResponseBuilder rb{ctx, 3}; |
| @@ -62,7 +62,7 @@ void AudCtl::GetTargetVolumeMax(Kernel::HLERequestContext& ctx) { | |||
| 62 | LOG_DEBUG(Audio, "called."); | 62 | LOG_DEBUG(Audio, "called."); |
| 63 | 63 | ||
| 64 | // This service function is currently hardcoded on the | 64 | // This service function is currently hardcoded on the |
| 65 | // actual console to this value (as of 6.0.0). | 65 | // actual console to this value (as of 8.0.0). |
| 66 | constexpr s32 target_max_volume = 15; | 66 | constexpr s32 target_max_volume = 15; |
| 67 | 67 | ||
| 68 | IPC::ResponseBuilder rb{ctx, 3}; | 68 | IPC::ResponseBuilder rb{ctx, 3}; |
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 12875fb42..6ba41b20a 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp | |||
| @@ -67,7 +67,7 @@ public: | |||
| 67 | // This is the event handle used to check if the audio buffer was released | 67 | // This is the event handle used to check if the audio buffer was released |
| 68 | auto& system = Core::System::GetInstance(); | 68 | auto& system = Core::System::GetInstance(); |
| 69 | buffer_event = Kernel::WritableEvent::CreateEventPair( | 69 | buffer_event = Kernel::WritableEvent::CreateEventPair( |
| 70 | system.Kernel(), Kernel::ResetType::Sticky, "IAudioOutBufferReleased"); | 70 | system.Kernel(), Kernel::ResetType::Manual, "IAudioOutBufferReleased"); |
| 71 | 71 | ||
| 72 | stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate, | 72 | stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate, |
| 73 | audio_params.channel_count, std::move(unique_name), | 73 | audio_params.channel_count, std::move(unique_name), |
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 1dde6edb7..75db0c2dc 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | 8 | ||
| 9 | #include "audio_core/audio_renderer.h" | 9 | #include "audio_core/audio_renderer.h" |
| 10 | #include "common/alignment.h" | 10 | #include "common/alignment.h" |
| 11 | #include "common/bit_util.h" | ||
| 11 | #include "common/common_funcs.h" | 12 | #include "common/common_funcs.h" |
| 12 | #include "common/logging/log.h" | 13 | #include "common/logging/log.h" |
| 13 | #include "common/string_util.h" | 14 | #include "common/string_util.h" |
| @@ -46,7 +47,7 @@ public: | |||
| 46 | 47 | ||
| 47 | auto& system = Core::System::GetInstance(); | 48 | auto& system = Core::System::GetInstance(); |
| 48 | system_event = Kernel::WritableEvent::CreateEventPair( | 49 | system_event = Kernel::WritableEvent::CreateEventPair( |
| 49 | system.Kernel(), Kernel::ResetType::Sticky, "IAudioRenderer:SystemEvent"); | 50 | system.Kernel(), Kernel::ResetType::Manual, "IAudioRenderer:SystemEvent"); |
| 50 | renderer = std::make_unique<AudioCore::AudioRenderer>(system.CoreTiming(), audren_params, | 51 | renderer = std::make_unique<AudioCore::AudioRenderer>(system.CoreTiming(), audren_params, |
| 51 | system_event.writable); | 52 | system_event.writable); |
| 52 | } | 53 | } |
| @@ -178,7 +179,7 @@ public: | |||
| 178 | RegisterHandlers(functions); | 179 | RegisterHandlers(functions); |
| 179 | 180 | ||
| 180 | auto& kernel = Core::System::GetInstance().Kernel(); | 181 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 181 | buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, | 182 | buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, |
| 182 | "IAudioOutBufferReleasedEvent"); | 183 | "IAudioOutBufferReleasedEvent"); |
| 183 | } | 184 | } |
| 184 | 185 | ||
| @@ -262,64 +263,304 @@ void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) { | |||
| 262 | OpenAudioRendererImpl(ctx); | 263 | OpenAudioRendererImpl(ctx); |
| 263 | } | 264 | } |
| 264 | 265 | ||
| 266 | static u64 CalculateNumPerformanceEntries(const AudioCore::AudioRendererParameter& params) { | ||
| 267 | // +1 represents the final mix. | ||
| 268 | return u64{params.effect_count} + params.submix_count + params.sink_count + params.voice_count + | ||
| 269 | 1; | ||
| 270 | } | ||
| 271 | |||
| 265 | void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { | 272 | void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { |
| 266 | IPC::RequestParser rp{ctx}; | ||
| 267 | auto params = rp.PopRaw<AudioCore::AudioRendererParameter>(); | ||
| 268 | LOG_DEBUG(Service_Audio, "called"); | 273 | LOG_DEBUG(Service_Audio, "called"); |
| 269 | 274 | ||
| 270 | u64 buffer_sz = Common::AlignUp(4 * params.mix_buffer_count, 0x40); | 275 | // Several calculations below align the sizes being calculated |
| 271 | buffer_sz += params.submix_count * 1024; | 276 | // onto a 64 byte boundary. |
| 272 | buffer_sz += 0x940 * (params.submix_count + 1); | 277 | static constexpr u64 buffer_alignment_size = 64; |
| 273 | buffer_sz += 0x3F0 * params.voice_count; | 278 | |
| 274 | buffer_sz += Common::AlignUp(8 * (params.submix_count + 1), 0x10); | 279 | // Some calculations that calculate portions of the buffer |
| 275 | buffer_sz += Common::AlignUp(8 * params.voice_count, 0x10); | 280 | // that will contain information, on the other hand, align |
| 276 | buffer_sz += Common::AlignUp( | 281 | // the result of some of their calcularions on a 16 byte boundary. |
| 277 | (0x3C0 * (params.sink_count + params.submix_count) + 4 * params.sample_count) * | 282 | static constexpr u64 info_field_alignment_size = 16; |
| 278 | (params.mix_buffer_count + 6), | 283 | |
| 279 | 0x40); | 284 | // Maximum detail entries that may exist at one time for performance |
| 280 | 285 | // frame statistics. | |
| 281 | if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) { | 286 | static constexpr u64 max_perf_detail_entries = 100; |
| 282 | const u32 count = params.submix_count + 1; | 287 | |
| 283 | u64 node_count = Common::AlignUp(count, 0x40); | 288 | // Size of the data structure representing the bulk of the voice-related state. |
| 284 | const u64 node_state_buffer_sz = | 289 | static constexpr u64 voice_state_size = 0x100; |
| 285 | 4 * (node_count * node_count) + 0xC * node_count + 2 * (node_count / 8); | 290 | |
| 286 | u64 edge_matrix_buffer_sz = 0; | 291 | // Size of the upsampler manager data structure |
| 287 | node_count = Common::AlignUp(count * count, 0x40); | 292 | constexpr u64 upsampler_manager_size = 0x48; |
| 288 | if (node_count >> 31 != 0) { | 293 | |
| 289 | edge_matrix_buffer_sz = (node_count | 7) / 8; | 294 | // Calculates the part of the size that relates to mix buffers. |
| 290 | } else { | 295 | const auto calculate_mix_buffer_sizes = [](const AudioCore::AudioRendererParameter& params) { |
| 291 | edge_matrix_buffer_sz = node_count / 8; | 296 | // As of 8.0.0 this is the maximum on voice channels. |
| 297 | constexpr u64 max_voice_channels = 6; | ||
| 298 | |||
| 299 | // The service expects the sample_count member of the parameters to either be | ||
| 300 | // a value of 160 or 240, so the maximum sample count is assumed in order | ||
| 301 | // to adequately handle all values at runtime. | ||
| 302 | constexpr u64 default_max_sample_count = 240; | ||
| 303 | |||
| 304 | const u64 total_mix_buffers = params.mix_buffer_count + max_voice_channels; | ||
| 305 | |||
| 306 | u64 size = 0; | ||
| 307 | size += total_mix_buffers * (sizeof(s32) * params.sample_count); | ||
| 308 | size += total_mix_buffers * (sizeof(s32) * default_max_sample_count); | ||
| 309 | size += u64{params.submix_count} + params.sink_count; | ||
| 310 | size = Common::AlignUp(size, buffer_alignment_size); | ||
| 311 | size += Common::AlignUp(params.unknown_30, buffer_alignment_size); | ||
| 312 | size += Common::AlignUp(sizeof(s32) * params.mix_buffer_count, buffer_alignment_size); | ||
| 313 | return size; | ||
| 314 | }; | ||
| 315 | |||
| 316 | // Calculates the portion of the size related to the mix data (and the sorting thereof). | ||
| 317 | const auto calculate_mix_info_size = [this](const AudioCore::AudioRendererParameter& params) { | ||
| 318 | // The size of the mixing info data structure. | ||
| 319 | constexpr u64 mix_info_size = 0x940; | ||
| 320 | |||
| 321 | // Consists of total submixes with the final mix included. | ||
| 322 | const u64 total_mix_count = u64{params.submix_count} + 1; | ||
| 323 | |||
| 324 | // The total number of effects that may be available to the audio renderer at any time. | ||
| 325 | constexpr u64 max_effects = 256; | ||
| 326 | |||
| 327 | // Calculates the part of the size related to the audio node state. | ||
| 328 | // This will only be used if the audio revision supports the splitter. | ||
| 329 | const auto calculate_node_state_size = [](std::size_t num_nodes) { | ||
| 330 | // Internally within a nodestate, it appears to use a data structure | ||
| 331 | // similar to a std::bitset<64> twice. | ||
| 332 | constexpr u64 bit_size = Common::BitSize<u64>(); | ||
| 333 | constexpr u64 num_bitsets = 2; | ||
| 334 | |||
| 335 | // Node state instances have three states internally for performing | ||
| 336 | // depth-first searches of nodes. Initialized, Found, and Done Sorting. | ||
| 337 | constexpr u64 num_states = 3; | ||
| 338 | |||
| 339 | u64 size = 0; | ||
| 340 | size += (num_nodes * num_nodes) * sizeof(s32); | ||
| 341 | size += num_states * (num_nodes * sizeof(s32)); | ||
| 342 | size += num_bitsets * (Common::AlignUp(num_nodes, bit_size) / Common::BitSize<u8>()); | ||
| 343 | return size; | ||
| 344 | }; | ||
| 345 | |||
| 346 | // Calculates the part of the size related to the adjacency (aka edge) matrix. | ||
| 347 | const auto calculate_edge_matrix_size = [](std::size_t num_nodes) { | ||
| 348 | return (num_nodes * num_nodes) * sizeof(s32); | ||
| 349 | }; | ||
| 350 | |||
| 351 | u64 size = 0; | ||
| 352 | size += Common::AlignUp(sizeof(void*) * total_mix_count, info_field_alignment_size); | ||
| 353 | size += Common::AlignUp(mix_info_size * total_mix_count, info_field_alignment_size); | ||
| 354 | size += Common::AlignUp(sizeof(s32) * max_effects * params.submix_count, | ||
| 355 | info_field_alignment_size); | ||
| 356 | |||
| 357 | if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) { | ||
| 358 | size += Common::AlignUp(calculate_node_state_size(total_mix_count) + | ||
| 359 | calculate_edge_matrix_size(total_mix_count), | ||
| 360 | info_field_alignment_size); | ||
| 292 | } | 361 | } |
| 293 | buffer_sz += Common::AlignUp(node_state_buffer_sz + edge_matrix_buffer_sz, 0x10); | ||
| 294 | } | ||
| 295 | 362 | ||
| 296 | buffer_sz += 0x20 * (params.effect_count + 4 * params.voice_count) + 0x50; | 363 | return size; |
| 297 | if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) { | 364 | }; |
| 298 | buffer_sz += 0xE0 * params.num_splitter_send_channels; | ||
| 299 | buffer_sz += 0x20 * params.splitter_count; | ||
| 300 | buffer_sz += Common::AlignUp(4 * params.num_splitter_send_channels, 0x10); | ||
| 301 | } | ||
| 302 | buffer_sz = Common::AlignUp(buffer_sz, 0x40) + 0x170 * params.sink_count; | ||
| 303 | u64 output_sz = buffer_sz + 0x280 * params.sink_count + 0x4B0 * params.effect_count + | ||
| 304 | ((params.voice_count * 256) | 0x40); | ||
| 305 | |||
| 306 | if (params.performance_frame_count >= 1) { | ||
| 307 | output_sz = Common::AlignUp(((16 * params.sink_count + 16 * params.effect_count + | ||
| 308 | 16 * params.voice_count + 16) + | ||
| 309 | 0x658) * | ||
| 310 | (params.performance_frame_count + 1) + | ||
| 311 | 0xc0, | ||
| 312 | 0x40) + | ||
| 313 | output_sz; | ||
| 314 | } | ||
| 315 | output_sz = Common::AlignUp(output_sz + 0x1807e, 0x1000); | ||
| 316 | 365 | ||
| 317 | IPC::ResponseBuilder rb{ctx, 4}; | 366 | // Calculates the part of the size related to voice channel info. |
| 367 | const auto calculate_voice_info_size = [](const AudioCore::AudioRendererParameter& params) { | ||
| 368 | constexpr u64 voice_info_size = 0x220; | ||
| 369 | constexpr u64 voice_resource_size = 0xD0; | ||
| 370 | |||
| 371 | u64 size = 0; | ||
| 372 | size += Common::AlignUp(sizeof(void*) * params.voice_count, info_field_alignment_size); | ||
| 373 | size += Common::AlignUp(voice_info_size * params.voice_count, info_field_alignment_size); | ||
| 374 | size += | ||
| 375 | Common::AlignUp(voice_resource_size * params.voice_count, info_field_alignment_size); | ||
| 376 | size += Common::AlignUp(voice_state_size * params.voice_count, info_field_alignment_size); | ||
| 377 | return size; | ||
| 378 | }; | ||
| 379 | |||
| 380 | // Calculates the part of the size related to memory pools. | ||
| 381 | const auto calculate_memory_pools_size = [](const AudioCore::AudioRendererParameter& params) { | ||
| 382 | const u64 num_memory_pools = sizeof(s32) * (u64{params.effect_count} + params.voice_count); | ||
| 383 | const u64 memory_pool_info_size = 0x20; | ||
| 384 | return Common::AlignUp(num_memory_pools * memory_pool_info_size, info_field_alignment_size); | ||
| 385 | }; | ||
| 386 | |||
| 387 | // Calculates the part of the size related to the splitter context. | ||
| 388 | const auto calculate_splitter_context_size = | ||
| 389 | [this](const AudioCore::AudioRendererParameter& params) -> u64 { | ||
| 390 | if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) { | ||
| 391 | return 0; | ||
| 392 | } | ||
| 393 | |||
| 394 | constexpr u64 splitter_info_size = 0x20; | ||
| 395 | constexpr u64 splitter_destination_data_size = 0xE0; | ||
| 396 | |||
| 397 | u64 size = 0; | ||
| 398 | size += params.num_splitter_send_channels; | ||
| 399 | size += | ||
| 400 | Common::AlignUp(splitter_info_size * params.splitter_count, info_field_alignment_size); | ||
| 401 | size += Common::AlignUp(splitter_destination_data_size * params.num_splitter_send_channels, | ||
| 402 | info_field_alignment_size); | ||
| 403 | |||
| 404 | return size; | ||
| 405 | }; | ||
| 406 | |||
| 407 | // Calculates the part of the size related to the upsampler info. | ||
| 408 | const auto calculate_upsampler_info_size = [](const AudioCore::AudioRendererParameter& params) { | ||
| 409 | constexpr u64 upsampler_info_size = 0x280; | ||
| 410 | // Yes, using the buffer size over info alignment size is intentional here. | ||
| 411 | return Common::AlignUp(upsampler_info_size * (u64{params.submix_count} + params.sink_count), | ||
| 412 | buffer_alignment_size); | ||
| 413 | }; | ||
| 414 | |||
| 415 | // Calculates the part of the size related to effect info. | ||
| 416 | const auto calculate_effect_info_size = [](const AudioCore::AudioRendererParameter& params) { | ||
| 417 | constexpr u64 effect_info_size = 0x2B0; | ||
| 418 | return Common::AlignUp(effect_info_size * params.effect_count, info_field_alignment_size); | ||
| 419 | }; | ||
| 420 | |||
| 421 | // Calculates the part of the size related to audio sink info. | ||
| 422 | const auto calculate_sink_info_size = [](const AudioCore::AudioRendererParameter& params) { | ||
| 423 | const u64 sink_info_size = 0x170; | ||
| 424 | return Common::AlignUp(sink_info_size * params.sink_count, info_field_alignment_size); | ||
| 425 | }; | ||
| 426 | |||
| 427 | // Calculates the part of the size related to voice state info. | ||
| 428 | const auto calculate_voice_state_size = [](const AudioCore::AudioRendererParameter& params) { | ||
| 429 | const u64 voice_state_size = 0x100; | ||
| 430 | const u64 additional_size = buffer_alignment_size - 1; | ||
| 431 | return Common::AlignUp(voice_state_size * params.voice_count + additional_size, | ||
| 432 | info_field_alignment_size); | ||
| 433 | }; | ||
| 434 | |||
| 435 | // Calculates the part of the size related to performance statistics. | ||
| 436 | const auto calculate_perf_size = [this](const AudioCore::AudioRendererParameter& params) { | ||
| 437 | // Extra size value appended to the end of the calculation. | ||
| 438 | constexpr u64 appended = 128; | ||
| 439 | |||
| 440 | // Whether or not we assume the newer version of performance metrics data structures. | ||
| 441 | const bool is_v2 = | ||
| 442 | IsFeatureSupported(AudioFeatures::PerformanceMetricsVersion2, params.revision); | ||
| 443 | |||
| 444 | // Data structure sizes | ||
| 445 | constexpr u64 perf_statistics_size = 0x0C; | ||
| 446 | const u64 header_size = is_v2 ? 0x30 : 0x18; | ||
| 447 | const u64 entry_size = is_v2 ? 0x18 : 0x10; | ||
| 448 | const u64 detail_size = is_v2 ? 0x18 : 0x10; | ||
| 449 | |||
| 450 | const u64 entry_count = CalculateNumPerformanceEntries(params); | ||
| 451 | const u64 size_per_frame = | ||
| 452 | header_size + (entry_size * entry_count) + (detail_size * max_perf_detail_entries); | ||
| 453 | |||
| 454 | u64 size = 0; | ||
| 455 | size += Common::AlignUp(size_per_frame * params.performance_frame_count + 1, | ||
| 456 | buffer_alignment_size); | ||
| 457 | size += Common::AlignUp(perf_statistics_size, buffer_alignment_size); | ||
| 458 | size += appended; | ||
| 459 | return size; | ||
| 460 | }; | ||
| 461 | |||
| 462 | // Calculates the part of the size that relates to the audio command buffer. | ||
| 463 | const auto calculate_command_buffer_size = | ||
| 464 | [this](const AudioCore::AudioRendererParameter& params) { | ||
| 465 | constexpr u64 alignment = (buffer_alignment_size - 1) * 2; | ||
| 466 | |||
| 467 | if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) { | ||
| 468 | constexpr u64 command_buffer_size = 0x18000; | ||
| 469 | |||
| 470 | return command_buffer_size + alignment; | ||
| 471 | } | ||
| 472 | |||
| 473 | // When the variadic command buffer is supported, this means | ||
| 474 | // the command generator for the audio renderer can issue commands | ||
| 475 | // that are (as one would expect), variable in size. So what we need to do | ||
| 476 | // is determine the maximum possible size for a few command data structures | ||
| 477 | // then multiply them by the amount of present commands indicated by the given | ||
| 478 | // respective audio parameters. | ||
| 479 | |||
| 480 | constexpr u64 max_biquad_filters = 2; | ||
| 481 | constexpr u64 max_mix_buffers = 24; | ||
| 482 | |||
| 483 | constexpr u64 biquad_filter_command_size = 0x2C; | ||
| 484 | |||
| 485 | constexpr u64 depop_mix_command_size = 0x24; | ||
| 486 | constexpr u64 depop_setup_command_size = 0x50; | ||
| 487 | |||
| 488 | constexpr u64 effect_command_max_size = 0x540; | ||
| 489 | |||
| 490 | constexpr u64 mix_command_size = 0x1C; | ||
| 491 | constexpr u64 mix_ramp_command_size = 0x24; | ||
| 492 | constexpr u64 mix_ramp_grouped_command_size = 0x13C; | ||
| 493 | |||
| 494 | constexpr u64 perf_command_size = 0x28; | ||
| 495 | |||
| 496 | constexpr u64 sink_command_size = 0x130; | ||
| 497 | |||
| 498 | constexpr u64 submix_command_max_size = | ||
| 499 | depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers; | ||
| 500 | |||
| 501 | constexpr u64 volume_command_size = 0x1C; | ||
| 502 | constexpr u64 volume_ramp_command_size = 0x20; | ||
| 503 | |||
| 504 | constexpr u64 voice_biquad_filter_command_size = | ||
| 505 | biquad_filter_command_size * max_biquad_filters; | ||
| 506 | constexpr u64 voice_data_command_size = 0x9C; | ||
| 507 | const u64 voice_command_max_size = | ||
| 508 | (params.splitter_count * depop_setup_command_size) + | ||
| 509 | (voice_data_command_size + voice_biquad_filter_command_size + | ||
| 510 | volume_ramp_command_size + mix_ramp_grouped_command_size); | ||
| 511 | |||
| 512 | // Now calculate the individual elements that comprise the size and add them together. | ||
| 513 | const u64 effect_commands_size = params.effect_count * effect_command_max_size; | ||
| 514 | |||
| 515 | const u64 final_mix_commands_size = | ||
| 516 | depop_mix_command_size + volume_command_size * max_mix_buffers; | ||
| 318 | 517 | ||
| 518 | const u64 perf_commands_size = | ||
| 519 | perf_command_size * | ||
| 520 | (CalculateNumPerformanceEntries(params) + max_perf_detail_entries); | ||
| 521 | |||
| 522 | const u64 sink_commands_size = params.sink_count * sink_command_size; | ||
| 523 | |||
| 524 | const u64 splitter_commands_size = | ||
| 525 | params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size; | ||
| 526 | |||
| 527 | const u64 submix_commands_size = params.submix_count * submix_command_max_size; | ||
| 528 | |||
| 529 | const u64 voice_commands_size = params.voice_count * voice_command_max_size; | ||
| 530 | |||
| 531 | return effect_commands_size + final_mix_commands_size + perf_commands_size + | ||
| 532 | sink_commands_size + splitter_commands_size + submix_commands_size + | ||
| 533 | voice_commands_size + alignment; | ||
| 534 | }; | ||
| 535 | |||
| 536 | IPC::RequestParser rp{ctx}; | ||
| 537 | const auto params = rp.PopRaw<AudioCore::AudioRendererParameter>(); | ||
| 538 | |||
| 539 | u64 size = 0; | ||
| 540 | size += calculate_mix_buffer_sizes(params); | ||
| 541 | size += calculate_mix_info_size(params); | ||
| 542 | size += calculate_voice_info_size(params); | ||
| 543 | size += upsampler_manager_size; | ||
| 544 | size += calculate_memory_pools_size(params); | ||
| 545 | size += calculate_splitter_context_size(params); | ||
| 546 | |||
| 547 | size = Common::AlignUp(size, buffer_alignment_size); | ||
| 548 | |||
| 549 | size += calculate_upsampler_info_size(params); | ||
| 550 | size += calculate_effect_info_size(params); | ||
| 551 | size += calculate_sink_info_size(params); | ||
| 552 | size += calculate_voice_state_size(params); | ||
| 553 | size += calculate_perf_size(params); | ||
| 554 | size += calculate_command_buffer_size(params); | ||
| 555 | |||
| 556 | // finally, 4KB page align the size, and we're done. | ||
| 557 | size = Common::AlignUp(size, 4096); | ||
| 558 | |||
| 559 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 319 | rb.Push(RESULT_SUCCESS); | 560 | rb.Push(RESULT_SUCCESS); |
| 320 | rb.Push<u64>(output_sz); | 561 | rb.Push<u64>(size); |
| 321 | 562 | ||
| 322 | LOG_DEBUG(Service_Audio, "buffer_size=0x{:X}", output_sz); | 563 | LOG_DEBUG(Service_Audio, "buffer_size=0x{:X}", size); |
| 323 | } | 564 | } |
| 324 | 565 | ||
| 325 | void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) { | 566 | void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) { |
| @@ -357,10 +598,15 @@ void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) { | |||
| 357 | } | 598 | } |
| 358 | 599 | ||
| 359 | bool AudRenU::IsFeatureSupported(AudioFeatures feature, u32_le revision) const { | 600 | bool AudRenU::IsFeatureSupported(AudioFeatures feature, u32_le revision) const { |
| 360 | u32_be version_num = (revision - Common::MakeMagic('R', 'E', 'V', '0')); // Byte swap | 601 | // Byte swap |
| 602 | const u32_be version_num = revision - Common::MakeMagic('R', 'E', 'V', '0'); | ||
| 603 | |||
| 361 | switch (feature) { | 604 | switch (feature) { |
| 362 | case AudioFeatures::Splitter: | 605 | case AudioFeatures::Splitter: |
| 363 | return version_num >= 2u; | 606 | return version_num >= 2U; |
| 607 | case AudioFeatures::PerformanceMetricsVersion2: | ||
| 608 | case AudioFeatures::VariadicCommandBuffer: | ||
| 609 | return version_num >= 5U; | ||
| 364 | default: | 610 | default: |
| 365 | return false; | 611 | return false; |
| 366 | } | 612 | } |
diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h index e55d25973..1d3c8df61 100644 --- a/src/core/hle/service/audio/audren_u.h +++ b/src/core/hle/service/audio/audren_u.h | |||
| @@ -28,6 +28,8 @@ private: | |||
| 28 | 28 | ||
| 29 | enum class AudioFeatures : u32 { | 29 | enum class AudioFeatures : u32 { |
| 30 | Splitter, | 30 | Splitter, |
| 31 | PerformanceMetricsVersion2, | ||
| 32 | VariadicCommandBuffer, | ||
| 31 | }; | 33 | }; |
| 32 | 34 | ||
| 33 | bool IsFeatureSupported(AudioFeatures feature, u32_le revision) const; | 35 | bool IsFeatureSupported(AudioFeatures feature, u32_le revision) const; |
diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp index 974ff8e1a..3c7ca2c44 100644 --- a/src/core/hle/service/btdrv/btdrv.cpp +++ b/src/core/hle/service/btdrv/btdrv.cpp | |||
| @@ -34,8 +34,8 @@ public: | |||
| 34 | RegisterHandlers(functions); | 34 | RegisterHandlers(functions); |
| 35 | 35 | ||
| 36 | auto& kernel = Core::System::GetInstance().Kernel(); | 36 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 37 | register_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, | 37 | register_event = Kernel::WritableEvent::CreateEventPair( |
| 38 | "BT:RegisterEvent"); | 38 | kernel, Kernel::ResetType::Automatic, "BT:RegisterEvent"); |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | private: | 41 | private: |
diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp index 4f15c3f19..b439ee7ec 100644 --- a/src/core/hle/service/btm/btm.cpp +++ b/src/core/hle/service/btm/btm.cpp | |||
| @@ -57,13 +57,13 @@ public: | |||
| 57 | RegisterHandlers(functions); | 57 | RegisterHandlers(functions); |
| 58 | 58 | ||
| 59 | auto& kernel = Core::System::GetInstance().Kernel(); | 59 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 60 | scan_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, | 60 | scan_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, |
| 61 | "IBtmUserCore:ScanEvent"); | 61 | "IBtmUserCore:ScanEvent"); |
| 62 | connection_event = Kernel::WritableEvent::CreateEventPair( | 62 | connection_event = Kernel::WritableEvent::CreateEventPair( |
| 63 | kernel, Kernel::ResetType::OneShot, "IBtmUserCore:ConnectionEvent"); | 63 | kernel, Kernel::ResetType::Automatic, "IBtmUserCore:ConnectionEvent"); |
| 64 | service_discovery = Kernel::WritableEvent::CreateEventPair( | 64 | service_discovery = Kernel::WritableEvent::CreateEventPair( |
| 65 | kernel, Kernel::ResetType::OneShot, "IBtmUserCore:Discovery"); | 65 | kernel, Kernel::ResetType::Automatic, "IBtmUserCore:Discovery"); |
| 66 | config_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, | 66 | config_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, |
| 67 | "IBtmUserCore:ConfigEvent"); | 67 | "IBtmUserCore:ConfigEvent"); |
| 68 | } | 68 | } |
| 69 | 69 | ||
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index e7fc7a619..fdd6d79a2 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp | |||
| @@ -170,7 +170,7 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { | |||
| 170 | void Controller_NPad::OnInit() { | 170 | void Controller_NPad::OnInit() { |
| 171 | auto& kernel = Core::System::GetInstance().Kernel(); | 171 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 172 | styleset_changed_event = Kernel::WritableEvent::CreateEventPair( | 172 | styleset_changed_event = Kernel::WritableEvent::CreateEventPair( |
| 173 | kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged"); | 173 | kernel, Kernel::ResetType::Automatic, "npad:NpadStyleSetChanged"); |
| 174 | 174 | ||
| 175 | if (!IsControllerActivated()) { | 175 | if (!IsControllerActivated()) { |
| 176 | return; | 176 | return; |
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index c6babdd4d..a5cb06f8a 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp | |||
| @@ -26,7 +26,7 @@ constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152); | |||
| 26 | Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) | 26 | Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) |
| 27 | : ServiceFramework(name), module(std::move(module)) { | 27 | : ServiceFramework(name), module(std::move(module)) { |
| 28 | auto& kernel = Core::System::GetInstance().Kernel(); | 28 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 29 | nfc_tag_load = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, | 29 | nfc_tag_load = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, |
| 30 | "IUser:NFCTagDetected"); | 30 | "IUser:NFCTagDetected"); |
| 31 | } | 31 | } |
| 32 | 32 | ||
| @@ -67,9 +67,9 @@ public: | |||
| 67 | 67 | ||
| 68 | auto& kernel = Core::System::GetInstance().Kernel(); | 68 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 69 | deactivate_event = Kernel::WritableEvent::CreateEventPair( | 69 | deactivate_event = Kernel::WritableEvent::CreateEventPair( |
| 70 | kernel, Kernel::ResetType::OneShot, "IUser:DeactivateEvent"); | 70 | kernel, Kernel::ResetType::Automatic, "IUser:DeactivateEvent"); |
| 71 | availability_change_event = Kernel::WritableEvent::CreateEventPair( | 71 | availability_change_event = Kernel::WritableEvent::CreateEventPair( |
| 72 | kernel, Kernel::ResetType::OneShot, "IUser:AvailabilityChangeEvent"); | 72 | kernel, Kernel::ResetType::Automatic, "IUser:AvailabilityChangeEvent"); |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | private: | 75 | private: |
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index f92571008..76b12b482 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp | |||
| @@ -62,9 +62,9 @@ public: | |||
| 62 | RegisterHandlers(functions); | 62 | RegisterHandlers(functions); |
| 63 | 63 | ||
| 64 | auto& kernel = Core::System::GetInstance().Kernel(); | 64 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 65 | event1 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, | 65 | event1 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, |
| 66 | "IRequest:Event1"); | 66 | "IRequest:Event1"); |
| 67 | event2 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, | 67 | event2 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, |
| 68 | "IRequest:Event2"); | 68 | "IRequest:Event2"); |
| 69 | } | 69 | } |
| 70 | 70 | ||
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp index 0dabcd23b..f319a3ca1 100644 --- a/src/core/hle/service/nim/nim.cpp +++ b/src/core/hle/service/nim/nim.cpp | |||
| @@ -141,7 +141,7 @@ public: | |||
| 141 | 141 | ||
| 142 | auto& kernel = Core::System::GetInstance().Kernel(); | 142 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 143 | finished_event = Kernel::WritableEvent::CreateEventPair( | 143 | finished_event = Kernel::WritableEvent::CreateEventPair( |
| 144 | kernel, Kernel::ResetType::OneShot, | 144 | kernel, Kernel::ResetType::Automatic, |
| 145 | "IEnsureNetworkClockAvailabilityService:FinishEvent"); | 145 | "IEnsureNetworkClockAvailabilityService:FinishEvent"); |
| 146 | } | 146 | } |
| 147 | 147 | ||
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp index 3b9ab4b14..b60fc748b 100644 --- a/src/core/hle/service/nvdrv/interface.cpp +++ b/src/core/hle/service/nvdrv/interface.cpp | |||
| @@ -129,7 +129,7 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name) | |||
| 129 | RegisterHandlers(functions); | 129 | RegisterHandlers(functions); |
| 130 | 130 | ||
| 131 | auto& kernel = Core::System::GetInstance().Kernel(); | 131 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 132 | query_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, | 132 | query_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, |
| 133 | "NVDRV::query_event"); | 133 | "NVDRV::query_event"); |
| 134 | } | 134 | } |
| 135 | 135 | ||
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp index 4d150fc71..5731e815f 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue.cpp | |||
| @@ -16,7 +16,7 @@ namespace Service::NVFlinger { | |||
| 16 | 16 | ||
| 17 | BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) { | 17 | BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) { |
| 18 | auto& kernel = Core::System::GetInstance().Kernel(); | 18 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 19 | buffer_wait_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, | 19 | buffer_wait_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual, |
| 20 | "BufferQueue NativeHandle"); | 20 | "BufferQueue NativeHandle"); |
| 21 | } | 21 | } |
| 22 | 22 | ||
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp index 4ecb6bcef..298d85011 100644 --- a/src/core/hle/service/set/set.cpp +++ b/src/core/hle/service/set/set.cpp | |||
| @@ -2,16 +2,15 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | ||
| 5 | #include <chrono> | 6 | #include <chrono> |
| 6 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 7 | #include "core/hle/ipc_helpers.h" | 8 | #include "core/hle/ipc_helpers.h" |
| 8 | #include "core/hle/kernel/client_port.h" | ||
| 9 | #include "core/hle/kernel/client_session.h" | ||
| 10 | #include "core/hle/service/set/set.h" | 9 | #include "core/hle/service/set/set.h" |
| 11 | #include "core/settings.h" | 10 | #include "core/settings.h" |
| 12 | 11 | ||
| 13 | namespace Service::Set { | 12 | namespace Service::Set { |
| 14 | 13 | namespace { | |
| 15 | constexpr std::array<LanguageCode, 17> available_language_codes = {{ | 14 | constexpr std::array<LanguageCode, 17> available_language_codes = {{ |
| 16 | LanguageCode::JA, | 15 | LanguageCode::JA, |
| 17 | LanguageCode::EN_US, | 16 | LanguageCode::EN_US, |
| @@ -32,41 +31,35 @@ constexpr std::array<LanguageCode, 17> available_language_codes = {{ | |||
| 32 | LanguageCode::ZH_HANT, | 31 | LanguageCode::ZH_HANT, |
| 33 | }}; | 32 | }}; |
| 34 | 33 | ||
| 35 | constexpr std::size_t pre4_0_0_max_entries = 0xF; | 34 | constexpr std::size_t pre4_0_0_max_entries = 15; |
| 36 | constexpr std::size_t post4_0_0_max_entries = 0x40; | 35 | constexpr std::size_t post4_0_0_max_entries = 17; |
| 37 | 36 | ||
| 38 | constexpr ResultCode ERR_INVALID_LANGUAGE{ErrorModule::Settings, 625}; | 37 | constexpr ResultCode ERR_INVALID_LANGUAGE{ErrorModule::Settings, 625}; |
| 39 | 38 | ||
| 40 | LanguageCode GetLanguageCodeFromIndex(std::size_t index) { | 39 | void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, std::size_t num_language_codes) { |
| 41 | return available_language_codes.at(index); | 40 | IPC::ResponseBuilder rb{ctx, 3}; |
| 41 | rb.Push(RESULT_SUCCESS); | ||
| 42 | rb.Push(static_cast<u32>(num_language_codes)); | ||
| 42 | } | 43 | } |
| 43 | 44 | ||
| 44 | template <std::size_t size> | 45 | void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t max_size) { |
| 45 | static std::array<LanguageCode, size> MakeLanguageCodeSubset() { | 46 | const std::size_t requested_amount = ctx.GetWriteBufferSize() / sizeof(LanguageCode); |
| 46 | std::array<LanguageCode, size> arr; | 47 | const std::size_t copy_amount = std::min(requested_amount, max_size); |
| 47 | std::copy_n(available_language_codes.begin(), size, arr.begin()); | 48 | const std::size_t copy_size = copy_amount * sizeof(LanguageCode); |
| 48 | return arr; | 49 | |
| 50 | ctx.WriteBuffer(available_language_codes.data(), copy_size); | ||
| 51 | PushResponseLanguageCode(ctx, copy_amount); | ||
| 49 | } | 52 | } |
| 53 | } // Anonymous namespace | ||
| 50 | 54 | ||
| 51 | static void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, std::size_t max_size) { | 55 | LanguageCode GetLanguageCodeFromIndex(std::size_t index) { |
| 52 | IPC::ResponseBuilder rb{ctx, 3}; | 56 | return available_language_codes.at(index); |
| 53 | rb.Push(RESULT_SUCCESS); | ||
| 54 | if (available_language_codes.size() > max_size) { | ||
| 55 | rb.Push(static_cast<u32>(max_size)); | ||
| 56 | } else { | ||
| 57 | rb.Push(static_cast<u32>(available_language_codes.size())); | ||
| 58 | } | ||
| 59 | } | 57 | } |
| 60 | 58 | ||
| 61 | void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) { | 59 | void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) { |
| 62 | LOG_DEBUG(Service_SET, "called"); | 60 | LOG_DEBUG(Service_SET, "called"); |
| 63 | 61 | ||
| 64 | if (available_language_codes.size() > pre4_0_0_max_entries) { | 62 | GetAvailableLanguageCodesImpl(ctx, pre4_0_0_max_entries); |
| 65 | ctx.WriteBuffer(MakeLanguageCodeSubset<pre4_0_0_max_entries>()); | ||
| 66 | } else { | ||
| 67 | ctx.WriteBuffer(available_language_codes); | ||
| 68 | } | ||
| 69 | PushResponseLanguageCode(ctx, pre4_0_0_max_entries); | ||
| 70 | } | 63 | } |
| 71 | 64 | ||
| 72 | void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) { | 65 | void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) { |
| @@ -87,12 +80,7 @@ void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) { | |||
| 87 | void SET::GetAvailableLanguageCodes2(Kernel::HLERequestContext& ctx) { | 80 | void SET::GetAvailableLanguageCodes2(Kernel::HLERequestContext& ctx) { |
| 88 | LOG_DEBUG(Service_SET, "called"); | 81 | LOG_DEBUG(Service_SET, "called"); |
| 89 | 82 | ||
| 90 | if (available_language_codes.size() > post4_0_0_max_entries) { | 83 | GetAvailableLanguageCodesImpl(ctx, post4_0_0_max_entries); |
| 91 | ctx.WriteBuffer(MakeLanguageCodeSubset<post4_0_0_max_entries>()); | ||
| 92 | } else { | ||
| 93 | ctx.WriteBuffer(available_language_codes); | ||
| 94 | } | ||
| 95 | PushResponseLanguageCode(ctx, post4_0_0_max_entries); | ||
| 96 | } | 84 | } |
| 97 | 85 | ||
| 98 | void SET::GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx) { | 86 | void SET::GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx) { |
| @@ -102,9 +90,9 @@ void SET::GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx) { | |||
| 102 | } | 90 | } |
| 103 | 91 | ||
| 104 | void SET::GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx) { | 92 | void SET::GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx) { |
| 105 | PushResponseLanguageCode(ctx, post4_0_0_max_entries); | ||
| 106 | |||
| 107 | LOG_DEBUG(Service_SET, "called"); | 93 | LOG_DEBUG(Service_SET, "called"); |
| 94 | |||
| 95 | PushResponseLanguageCode(ctx, post4_0_0_max_entries); | ||
| 108 | } | 96 | } |
| 109 | 97 | ||
| 110 | void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) { | 98 | void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) { |
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp index 01d80311b..a8d088305 100644 --- a/src/core/hle/service/vi/display/vi_display.cpp +++ b/src/core/hle/service/vi/display/vi_display.cpp | |||
| @@ -17,7 +17,7 @@ namespace Service::VI { | |||
| 17 | 17 | ||
| 18 | Display::Display(u64 id, std::string name) : id{id}, name{std::move(name)} { | 18 | Display::Display(u64 id, std::string name) : id{id}, name{std::move(name)} { |
| 19 | auto& kernel = Core::System::GetInstance().Kernel(); | 19 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 20 | vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, | 20 | vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual, |
| 21 | fmt::format("Display VSync Event {}", id)); | 21 | fmt::format("Display VSync Event {}", id)); |
| 22 | } | 22 | } |
| 23 | 23 | ||
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: | |||
| 556 | } else { | 556 | } else { |
| 557 | // Wait the current thread until a buffer becomes available | 557 | // Wait the current thread until a buffer becomes available |
| 558 | ctx.SleepClientThread( | 558 | ctx.SleepClientThread( |
| 559 | Kernel::GetCurrentThread(), "IHOSBinderDriver::DequeueBuffer", -1, | 559 | "IHOSBinderDriver::DequeueBuffer", -1, |
| 560 | [=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx, | 560 | [=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx, |
| 561 | Kernel::ThreadWakeupReason reason) { | 561 | Kernel::ThreadWakeupReason reason) { |
| 562 | // Repeat TransactParcel DequeueBuffer when a buffer is available | 562 | // Repeat TransactParcel DequeueBuffer when a buffer is available |
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index a86653204..62c090353 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp | |||
| @@ -21,8 +21,6 @@ | |||
| 21 | #include "core/memory.h" | 21 | #include "core/memory.h" |
| 22 | #include "core/settings.h" | 22 | #include "core/settings.h" |
| 23 | 23 | ||
| 24 | #pragma optimize("", off) | ||
| 25 | |||
| 26 | namespace Loader { | 24 | namespace Loader { |
| 27 | namespace { | 25 | namespace { |
| 28 | struct MODHeader { | 26 | struct MODHeader { |
| @@ -41,7 +39,7 @@ std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data, | |||
| 41 | const std::vector<u8> uncompressed_data = | 39 | const std::vector<u8> uncompressed_data = |
| 42 | Common::Compression::DecompressDataLZ4(compressed_data, header.size); | 40 | Common::Compression::DecompressDataLZ4(compressed_data, header.size); |
| 43 | 41 | ||
| 44 | ASSERT_MSG(uncompressed_data.size() == static_cast<int>(header.size), "{} != {}", header.size, | 42 | ASSERT_MSG(uncompressed_data.size() == header.size, "{} != {}", header.size, |
| 45 | uncompressed_data.size()); | 43 | uncompressed_data.size()); |
| 46 | 44 | ||
| 47 | return uncompressed_data; | 45 | return uncompressed_data; |
diff --git a/src/core/memory.h b/src/core/memory.h index b9fa18b1d..04e2c5f1d 100644 --- a/src/core/memory.h +++ b/src/core/memory.h | |||
| @@ -72,15 +72,6 @@ u8* GetPointer(VAddr vaddr); | |||
| 72 | 72 | ||
| 73 | std::string ReadCString(VAddr vaddr, std::size_t max_length); | 73 | std::string ReadCString(VAddr vaddr, std::size_t max_length); |
| 74 | 74 | ||
| 75 | enum class FlushMode { | ||
| 76 | /// Write back modified surfaces to RAM | ||
| 77 | Flush, | ||
| 78 | /// Remove region from the cache | ||
| 79 | Invalidate, | ||
| 80 | /// Write back modified surfaces to RAM, and also remove them from the cache | ||
| 81 | FlushAndInvalidate, | ||
| 82 | }; | ||
| 83 | |||
| 84 | /** | 75 | /** |
| 85 | * Mark each page touching the region as cached. | 76 | * Mark each page touching the region as cached. |
| 86 | */ | 77 | */ |
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index e1db06811..4b17bada5 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp | |||
| @@ -102,12 +102,6 @@ bool VerifyLogin(const std::string& username, const std::string& token) { | |||
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | TelemetrySession::TelemetrySession() { | 104 | TelemetrySession::TelemetrySession() { |
| 105 | #ifdef ENABLE_WEB_SERVICE | ||
| 106 | backend = std::make_unique<WebService::TelemetryJson>( | ||
| 107 | Settings::values.web_api_url, Settings::values.yuzu_username, Settings::values.yuzu_token); | ||
| 108 | #else | ||
| 109 | backend = std::make_unique<Telemetry::NullVisitor>(); | ||
| 110 | #endif | ||
| 111 | // Log one-time top-level information | 105 | // Log one-time top-level information |
| 112 | AddField(Telemetry::FieldType::None, "TelemetryId", GetTelemetryId()); | 106 | AddField(Telemetry::FieldType::None, "TelemetryId", GetTelemetryId()); |
| 113 | 107 | ||
| @@ -175,9 +169,14 @@ TelemetrySession::~TelemetrySession() { | |||
| 175 | .count()}; | 169 | .count()}; |
| 176 | AddField(Telemetry::FieldType::Session, "Shutdown_Time", shutdown_time); | 170 | AddField(Telemetry::FieldType::Session, "Shutdown_Time", shutdown_time); |
| 177 | 171 | ||
| 172 | #ifdef ENABLE_WEB_SERVICE | ||
| 173 | auto backend = std::make_unique<WebService::TelemetryJson>( | ||
| 174 | Settings::values.web_api_url, Settings::values.yuzu_username, Settings::values.yuzu_token); | ||
| 175 | #else | ||
| 176 | auto backend = std::make_unique<Telemetry::NullVisitor>(); | ||
| 177 | #endif | ||
| 178 | |||
| 178 | // Complete the session, submitting to web service if necessary | 179 | // Complete the session, submitting to web service if necessary |
| 179 | // This is just a placeholder to wrap up the session once the core completes and this is | ||
| 180 | // destroyed. This will be moved elsewhere once we are actually doing real I/O with the service. | ||
| 181 | field_collection.Accept(*backend); | 180 | field_collection.Accept(*backend); |
| 182 | if (Settings::values.enable_telemetry) | 181 | if (Settings::values.enable_telemetry) |
| 183 | backend->Complete(); | 182 | backend->Complete(); |
| @@ -186,6 +185,8 @@ TelemetrySession::~TelemetrySession() { | |||
| 186 | 185 | ||
| 187 | bool TelemetrySession::SubmitTestcase() { | 186 | bool TelemetrySession::SubmitTestcase() { |
| 188 | #ifdef ENABLE_WEB_SERVICE | 187 | #ifdef ENABLE_WEB_SERVICE |
| 188 | auto backend = std::make_unique<WebService::TelemetryJson>( | ||
| 189 | Settings::values.web_api_url, Settings::values.yuzu_username, Settings::values.yuzu_token); | ||
| 189 | field_collection.Accept(*backend); | 190 | field_collection.Accept(*backend); |
| 190 | return backend->SubmitTestcase(); | 191 | return backend->SubmitTestcase(); |
| 191 | #else | 192 | #else |
diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h index 023612b79..cae5a45a0 100644 --- a/src/core/telemetry_session.h +++ b/src/core/telemetry_session.h | |||
| @@ -39,7 +39,6 @@ public: | |||
| 39 | 39 | ||
| 40 | private: | 40 | private: |
| 41 | Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session | 41 | Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session |
| 42 | std::unique_ptr<Telemetry::VisitorInterface> backend; ///< Backend interface that logs fields | ||
| 43 | }; | 42 | }; |
| 44 | 43 | ||
| 45 | /** | 44 | /** |
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 6821f275d..1e010e4da 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt | |||
| @@ -3,6 +3,8 @@ add_library(video_core STATIC | |||
| 3 | dma_pusher.h | 3 | dma_pusher.h |
| 4 | debug_utils/debug_utils.cpp | 4 | debug_utils/debug_utils.cpp |
| 5 | debug_utils/debug_utils.h | 5 | debug_utils/debug_utils.h |
| 6 | engines/engine_upload.cpp | ||
| 7 | engines/engine_upload.h | ||
| 6 | engines/fermi_2d.cpp | 8 | engines/fermi_2d.cpp |
| 7 | engines/fermi_2d.h | 9 | engines/fermi_2d.h |
| 8 | engines/kepler_compute.cpp | 10 | engines/kepler_compute.cpp |
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp index 6674d9405..3175579cc 100644 --- a/src/video_core/dma_pusher.cpp +++ b/src/video_core/dma_pusher.cpp | |||
| @@ -40,6 +40,13 @@ bool DmaPusher::Step() { | |||
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | const CommandList& command_list{dma_pushbuffer.front()}; | 42 | const CommandList& command_list{dma_pushbuffer.front()}; |
| 43 | ASSERT_OR_EXECUTE(!command_list.empty(), { | ||
| 44 | // Somehow the command_list is empty, in order to avoid a crash | ||
| 45 | // We ignore it and assume its size is 0. | ||
| 46 | dma_pushbuffer.pop(); | ||
| 47 | dma_pushbuffer_subindex = 0; | ||
| 48 | return true; | ||
| 49 | }); | ||
| 43 | const CommandListHeader command_list_header{command_list[dma_pushbuffer_subindex++]}; | 50 | const CommandListHeader command_list_header{command_list[dma_pushbuffer_subindex++]}; |
| 44 | GPUVAddr dma_get = command_list_header.addr; | 51 | GPUVAddr dma_get = command_list_header.addr; |
| 45 | GPUVAddr dma_put = dma_get + command_list_header.size * sizeof(u32); | 52 | GPUVAddr dma_put = dma_get + command_list_header.size * sizeof(u32); |
| @@ -105,6 +112,8 @@ bool DmaPusher::Step() { | |||
| 105 | dma_state.non_incrementing = false; | 112 | dma_state.non_incrementing = false; |
| 106 | dma_increment_once = true; | 113 | dma_increment_once = true; |
| 107 | break; | 114 | break; |
| 115 | default: | ||
| 116 | break; | ||
| 108 | } | 117 | } |
| 109 | } | 118 | } |
| 110 | } | 119 | } |
diff --git a/src/video_core/engines/engine_upload.cpp b/src/video_core/engines/engine_upload.cpp new file mode 100644 index 000000000..082a40cd9 --- /dev/null +++ b/src/video_core/engines/engine_upload.cpp | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cstring> | ||
| 6 | |||
| 7 | #include "common/assert.h" | ||
| 8 | #include "video_core/engines/engine_upload.h" | ||
| 9 | #include "video_core/memory_manager.h" | ||
| 10 | #include "video_core/textures/decoders.h" | ||
| 11 | |||
| 12 | namespace Tegra::Engines::Upload { | ||
| 13 | |||
| 14 | State::State(MemoryManager& memory_manager, Registers& regs) | ||
| 15 | : regs{regs}, memory_manager{memory_manager} {} | ||
| 16 | |||
| 17 | State::~State() = default; | ||
| 18 | |||
| 19 | void State::ProcessExec(const bool is_linear) { | ||
| 20 | write_offset = 0; | ||
| 21 | copy_size = regs.line_length_in * regs.line_count; | ||
| 22 | inner_buffer.resize(copy_size); | ||
| 23 | this->is_linear = is_linear; | ||
| 24 | } | ||
| 25 | |||
| 26 | void State::ProcessData(const u32 data, const bool is_last_call) { | ||
| 27 | const u32 sub_copy_size = std::min(4U, copy_size - write_offset); | ||
| 28 | std::memcpy(&inner_buffer[write_offset], &data, sub_copy_size); | ||
| 29 | write_offset += sub_copy_size; | ||
| 30 | if (!is_last_call) { | ||
| 31 | return; | ||
| 32 | } | ||
| 33 | const GPUVAddr address{regs.dest.Address()}; | ||
| 34 | if (is_linear) { | ||
| 35 | memory_manager.WriteBlock(address, inner_buffer.data(), copy_size); | ||
| 36 | } else { | ||
| 37 | UNIMPLEMENTED_IF(regs.dest.z != 0); | ||
| 38 | UNIMPLEMENTED_IF(regs.dest.depth != 1); | ||
| 39 | UNIMPLEMENTED_IF(regs.dest.BlockWidth() != 1); | ||
| 40 | UNIMPLEMENTED_IF(regs.dest.BlockDepth() != 1); | ||
| 41 | const std::size_t dst_size = Tegra::Texture::CalculateSize( | ||
| 42 | true, 1, regs.dest.width, regs.dest.height, 1, regs.dest.BlockHeight(), 1); | ||
| 43 | tmp_buffer.resize(dst_size); | ||
| 44 | memory_manager.ReadBlock(address, tmp_buffer.data(), dst_size); | ||
| 45 | Tegra::Texture::SwizzleKepler(regs.dest.width, regs.dest.height, regs.dest.x, regs.dest.y, | ||
| 46 | regs.dest.BlockHeight(), copy_size, inner_buffer.data(), | ||
| 47 | tmp_buffer.data()); | ||
| 48 | memory_manager.WriteBlock(address, tmp_buffer.data(), dst_size); | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | } // namespace Tegra::Engines::Upload | ||
diff --git a/src/video_core/engines/engine_upload.h b/src/video_core/engines/engine_upload.h new file mode 100644 index 000000000..ef4f5839a --- /dev/null +++ b/src/video_core/engines/engine_upload.h | |||
| @@ -0,0 +1,73 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <vector> | ||
| 8 | #include "common/bit_field.h" | ||
| 9 | #include "common/common_types.h" | ||
| 10 | |||
| 11 | namespace Tegra { | ||
| 12 | class MemoryManager; | ||
| 13 | } | ||
| 14 | |||
| 15 | namespace Tegra::Engines::Upload { | ||
| 16 | |||
| 17 | struct Registers { | ||
| 18 | u32 line_length_in; | ||
| 19 | u32 line_count; | ||
| 20 | |||
| 21 | struct { | ||
| 22 | u32 address_high; | ||
| 23 | u32 address_low; | ||
| 24 | u32 pitch; | ||
| 25 | union { | ||
| 26 | BitField<0, 4, u32> block_width; | ||
| 27 | BitField<4, 4, u32> block_height; | ||
| 28 | BitField<8, 4, u32> block_depth; | ||
| 29 | }; | ||
| 30 | u32 width; | ||
| 31 | u32 height; | ||
| 32 | u32 depth; | ||
| 33 | u32 z; | ||
| 34 | u32 x; | ||
| 35 | u32 y; | ||
| 36 | |||
| 37 | GPUVAddr Address() const { | ||
| 38 | return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | address_low); | ||
| 39 | } | ||
| 40 | |||
| 41 | u32 BlockWidth() const { | ||
| 42 | return 1U << block_width.Value(); | ||
| 43 | } | ||
| 44 | |||
| 45 | u32 BlockHeight() const { | ||
| 46 | return 1U << block_height.Value(); | ||
| 47 | } | ||
| 48 | |||
| 49 | u32 BlockDepth() const { | ||
| 50 | return 1U << block_depth.Value(); | ||
| 51 | } | ||
| 52 | } dest; | ||
| 53 | }; | ||
| 54 | |||
| 55 | class State { | ||
| 56 | public: | ||
| 57 | State(MemoryManager& memory_manager, Registers& regs); | ||
| 58 | ~State(); | ||
| 59 | |||
| 60 | void ProcessExec(bool is_linear); | ||
| 61 | void ProcessData(u32 data, bool is_last_call); | ||
| 62 | |||
| 63 | private: | ||
| 64 | u32 write_offset = 0; | ||
| 65 | u32 copy_size = 0; | ||
| 66 | std::vector<u8> inner_buffer; | ||
| 67 | std::vector<u8> tmp_buffer; | ||
| 68 | bool is_linear = false; | ||
| 69 | Registers& regs; | ||
| 70 | MemoryManager& memory_manager; | ||
| 71 | }; | ||
| 72 | |||
| 73 | } // namespace Tegra::Engines::Upload | ||
diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h index 2e51b7f13..45f59a4d9 100644 --- a/src/video_core/engines/fermi_2d.h +++ b/src/video_core/engines/fermi_2d.h | |||
| @@ -21,6 +21,12 @@ class RasterizerInterface; | |||
| 21 | 21 | ||
| 22 | namespace Tegra::Engines { | 22 | namespace Tegra::Engines { |
| 23 | 23 | ||
| 24 | /** | ||
| 25 | * This Engine is known as G80_2D. Documentation can be found in: | ||
| 26 | * https://github.com/envytools/envytools/blob/master/rnndb/graph/g80_2d.xml | ||
| 27 | * https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/nouveau/nv50/nv50_2d.xml.h | ||
| 28 | */ | ||
| 29 | |||
| 24 | #define FERMI2D_REG_INDEX(field_name) \ | 30 | #define FERMI2D_REG_INDEX(field_name) \ |
| 25 | (offsetof(Tegra::Engines::Fermi2D::Regs, field_name) / sizeof(u32)) | 31 | (offsetof(Tegra::Engines::Fermi2D::Regs, field_name) / sizeof(u32)) |
| 26 | 32 | ||
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp index b1d950460..7404a8163 100644 --- a/src/video_core/engines/kepler_compute.cpp +++ b/src/video_core/engines/kepler_compute.cpp | |||
| @@ -4,12 +4,21 @@ | |||
| 4 | 4 | ||
| 5 | #include "common/assert.h" | 5 | #include "common/assert.h" |
| 6 | #include "common/logging/log.h" | 6 | #include "common/logging/log.h" |
| 7 | #include "core/core.h" | ||
| 7 | #include "video_core/engines/kepler_compute.h" | 8 | #include "video_core/engines/kepler_compute.h" |
| 9 | #include "video_core/engines/maxwell_3d.h" | ||
| 8 | #include "video_core/memory_manager.h" | 10 | #include "video_core/memory_manager.h" |
| 11 | #include "video_core/rasterizer_interface.h" | ||
| 12 | #include "video_core/renderer_base.h" | ||
| 13 | #include "video_core/textures/decoders.h" | ||
| 9 | 14 | ||
| 10 | namespace Tegra::Engines { | 15 | namespace Tegra::Engines { |
| 11 | 16 | ||
| 12 | KeplerCompute::KeplerCompute(MemoryManager& memory_manager) : memory_manager{memory_manager} {} | 17 | KeplerCompute::KeplerCompute(Core::System& system, VideoCore::RasterizerInterface& rasterizer, |
| 18 | MemoryManager& memory_manager) | ||
| 19 | : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager}, upload_state{ | ||
| 20 | memory_manager, | ||
| 21 | regs.upload} {} | ||
| 13 | 22 | ||
| 14 | KeplerCompute::~KeplerCompute() = default; | 23 | KeplerCompute::~KeplerCompute() = default; |
| 15 | 24 | ||
| @@ -20,14 +29,34 @@ void KeplerCompute::CallMethod(const GPU::MethodCall& method_call) { | |||
| 20 | regs.reg_array[method_call.method] = method_call.argument; | 29 | regs.reg_array[method_call.method] = method_call.argument; |
| 21 | 30 | ||
| 22 | switch (method_call.method) { | 31 | switch (method_call.method) { |
| 32 | case KEPLER_COMPUTE_REG_INDEX(exec_upload): { | ||
| 33 | upload_state.ProcessExec(regs.exec_upload.linear != 0); | ||
| 34 | break; | ||
| 35 | } | ||
| 36 | case KEPLER_COMPUTE_REG_INDEX(data_upload): { | ||
| 37 | const bool is_last_call = method_call.IsLastCall(); | ||
| 38 | upload_state.ProcessData(method_call.argument, is_last_call); | ||
| 39 | if (is_last_call) { | ||
| 40 | system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite(); | ||
| 41 | } | ||
| 42 | break; | ||
| 43 | } | ||
| 23 | case KEPLER_COMPUTE_REG_INDEX(launch): | 44 | case KEPLER_COMPUTE_REG_INDEX(launch): |
| 24 | // Abort execution since compute shaders can be used to alter game memory (e.g. CUDA | 45 | ProcessLaunch(); |
| 25 | // kernels) | ||
| 26 | UNREACHABLE_MSG("Compute shaders are not implemented"); | ||
| 27 | break; | 46 | break; |
| 28 | default: | 47 | default: |
| 29 | break; | 48 | break; |
| 30 | } | 49 | } |
| 31 | } | 50 | } |
| 32 | 51 | ||
| 52 | void KeplerCompute::ProcessLaunch() { | ||
| 53 | |||
| 54 | const GPUVAddr launch_desc_loc = regs.launch_desc_loc.Address(); | ||
| 55 | memory_manager.ReadBlockUnsafe(launch_desc_loc, &launch_description, | ||
| 56 | LaunchParams::NUM_LAUNCH_PARAMETERS * sizeof(u32)); | ||
| 57 | |||
| 58 | const GPUVAddr code_loc = regs.code_loc.Address() + launch_description.program_start; | ||
| 59 | LOG_WARNING(HW_GPU, "Compute Kernel Execute at Address 0x{:016x}, STUBBED", code_loc); | ||
| 60 | } | ||
| 61 | |||
| 33 | } // namespace Tegra::Engines | 62 | } // namespace Tegra::Engines |
diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h index fb6cdf432..5250b8d9b 100644 --- a/src/video_core/engines/kepler_compute.h +++ b/src/video_core/engines/kepler_compute.h | |||
| @@ -6,22 +6,40 @@ | |||
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <cstddef> | 8 | #include <cstddef> |
| 9 | #include <vector> | ||
| 10 | #include "common/bit_field.h" | ||
| 9 | #include "common/common_funcs.h" | 11 | #include "common/common_funcs.h" |
| 10 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 13 | #include "video_core/engines/engine_upload.h" | ||
| 11 | #include "video_core/gpu.h" | 14 | #include "video_core/gpu.h" |
| 12 | 15 | ||
| 16 | namespace Core { | ||
| 17 | class System; | ||
| 18 | } | ||
| 19 | |||
| 13 | namespace Tegra { | 20 | namespace Tegra { |
| 14 | class MemoryManager; | 21 | class MemoryManager; |
| 15 | } | 22 | } |
| 16 | 23 | ||
| 24 | namespace VideoCore { | ||
| 25 | class RasterizerInterface; | ||
| 26 | } | ||
| 27 | |||
| 17 | namespace Tegra::Engines { | 28 | namespace Tegra::Engines { |
| 18 | 29 | ||
| 30 | /** | ||
| 31 | * This Engine is known as GK104_Compute. Documentation can be found in: | ||
| 32 | * https://github.com/envytools/envytools/blob/master/rnndb/graph/gk104_compute.xml | ||
| 33 | * https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/nouveau/nvc0/nve4_compute.xml.h | ||
| 34 | */ | ||
| 35 | |||
| 19 | #define KEPLER_COMPUTE_REG_INDEX(field_name) \ | 36 | #define KEPLER_COMPUTE_REG_INDEX(field_name) \ |
| 20 | (offsetof(Tegra::Engines::KeplerCompute::Regs, field_name) / sizeof(u32)) | 37 | (offsetof(Tegra::Engines::KeplerCompute::Regs, field_name) / sizeof(u32)) |
| 21 | 38 | ||
| 22 | class KeplerCompute final { | 39 | class KeplerCompute final { |
| 23 | public: | 40 | public: |
| 24 | explicit KeplerCompute(MemoryManager& memory_manager); | 41 | explicit KeplerCompute(Core::System& system, VideoCore::RasterizerInterface& rasterizer, |
| 42 | MemoryManager& memory_manager); | ||
| 25 | ~KeplerCompute(); | 43 | ~KeplerCompute(); |
| 26 | 44 | ||
| 27 | static constexpr std::size_t NumConstBuffers = 8; | 45 | static constexpr std::size_t NumConstBuffers = 8; |
| @@ -31,30 +49,181 @@ public: | |||
| 31 | 49 | ||
| 32 | union { | 50 | union { |
| 33 | struct { | 51 | struct { |
| 34 | INSERT_PADDING_WORDS(0xAF); | 52 | INSERT_PADDING_WORDS(0x60); |
| 53 | |||
| 54 | Upload::Registers upload; | ||
| 55 | |||
| 56 | struct { | ||
| 57 | union { | ||
| 58 | BitField<0, 1, u32> linear; | ||
| 59 | }; | ||
| 60 | } exec_upload; | ||
| 61 | |||
| 62 | u32 data_upload; | ||
| 63 | |||
| 64 | INSERT_PADDING_WORDS(0x3F); | ||
| 65 | |||
| 66 | struct { | ||
| 67 | u32 address; | ||
| 68 | GPUVAddr Address() const { | ||
| 69 | return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address) << 8)); | ||
| 70 | } | ||
| 71 | } launch_desc_loc; | ||
| 72 | |||
| 73 | INSERT_PADDING_WORDS(0x1); | ||
| 35 | 74 | ||
| 36 | u32 launch; | 75 | u32 launch; |
| 37 | 76 | ||
| 38 | INSERT_PADDING_WORDS(0xC48); | 77 | INSERT_PADDING_WORDS(0x4A7); |
| 78 | |||
| 79 | struct { | ||
| 80 | u32 address_high; | ||
| 81 | u32 address_low; | ||
| 82 | u32 limit; | ||
| 83 | GPUVAddr Address() const { | ||
| 84 | return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | | ||
| 85 | address_low); | ||
| 86 | } | ||
| 87 | } tsc; | ||
| 88 | |||
| 89 | INSERT_PADDING_WORDS(0x3); | ||
| 90 | |||
| 91 | struct { | ||
| 92 | u32 address_high; | ||
| 93 | u32 address_low; | ||
| 94 | u32 limit; | ||
| 95 | GPUVAddr Address() const { | ||
| 96 | return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | | ||
| 97 | address_low); | ||
| 98 | } | ||
| 99 | } tic; | ||
| 100 | |||
| 101 | INSERT_PADDING_WORDS(0x22); | ||
| 102 | |||
| 103 | struct { | ||
| 104 | u32 address_high; | ||
| 105 | u32 address_low; | ||
| 106 | GPUVAddr Address() const { | ||
| 107 | return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | | ||
| 108 | address_low); | ||
| 109 | } | ||
| 110 | } code_loc; | ||
| 111 | |||
| 112 | INSERT_PADDING_WORDS(0x3FE); | ||
| 113 | |||
| 114 | u32 texture_const_buffer_index; | ||
| 115 | |||
| 116 | INSERT_PADDING_WORDS(0x374); | ||
| 39 | }; | 117 | }; |
| 40 | std::array<u32, NUM_REGS> reg_array; | 118 | std::array<u32, NUM_REGS> reg_array; |
| 41 | }; | 119 | }; |
| 42 | } regs{}; | 120 | } regs{}; |
| 121 | |||
| 122 | struct LaunchParams { | ||
| 123 | static constexpr std::size_t NUM_LAUNCH_PARAMETERS = 0x40; | ||
| 124 | |||
| 125 | INSERT_PADDING_WORDS(0x8); | ||
| 126 | |||
| 127 | u32 program_start; | ||
| 128 | |||
| 129 | INSERT_PADDING_WORDS(0x2); | ||
| 130 | |||
| 131 | BitField<30, 1, u32> linked_tsc; | ||
| 132 | |||
| 133 | BitField<0, 31, u32> grid_dim_x; | ||
| 134 | union { | ||
| 135 | BitField<0, 16, u32> grid_dim_y; | ||
| 136 | BitField<16, 16, u32> grid_dim_z; | ||
| 137 | }; | ||
| 138 | |||
| 139 | INSERT_PADDING_WORDS(0x3); | ||
| 140 | |||
| 141 | BitField<0, 16, u32> shared_alloc; | ||
| 142 | |||
| 143 | BitField<0, 31, u32> block_dim_x; | ||
| 144 | union { | ||
| 145 | BitField<0, 16, u32> block_dim_y; | ||
| 146 | BitField<16, 16, u32> block_dim_z; | ||
| 147 | }; | ||
| 148 | |||
| 149 | union { | ||
| 150 | BitField<0, 8, u32> const_buffer_enable_mask; | ||
| 151 | BitField<29, 2, u32> cache_layout; | ||
| 152 | } memory_config; | ||
| 153 | |||
| 154 | INSERT_PADDING_WORDS(0x8); | ||
| 155 | |||
| 156 | struct { | ||
| 157 | u32 address_low; | ||
| 158 | union { | ||
| 159 | BitField<0, 8, u32> address_high; | ||
| 160 | BitField<15, 17, u32> size; | ||
| 161 | }; | ||
| 162 | GPUVAddr Address() const { | ||
| 163 | return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high.Value()) << 32) | | ||
| 164 | address_low); | ||
| 165 | } | ||
| 166 | } const_buffer_config[8]; | ||
| 167 | |||
| 168 | union { | ||
| 169 | BitField<0, 20, u32> local_pos_alloc; | ||
| 170 | BitField<27, 5, u32> barrier_alloc; | ||
| 171 | }; | ||
| 172 | |||
| 173 | union { | ||
| 174 | BitField<0, 20, u32> local_neg_alloc; | ||
| 175 | BitField<24, 5, u32> gpr_alloc; | ||
| 176 | }; | ||
| 177 | |||
| 178 | INSERT_PADDING_WORDS(0x11); | ||
| 179 | } launch_description; | ||
| 180 | |||
| 181 | struct { | ||
| 182 | u32 write_offset = 0; | ||
| 183 | u32 copy_size = 0; | ||
| 184 | std::vector<u8> inner_buffer; | ||
| 185 | } state{}; | ||
| 186 | |||
| 43 | static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), | 187 | static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), |
| 44 | "KeplerCompute Regs has wrong size"); | 188 | "KeplerCompute Regs has wrong size"); |
| 45 | 189 | ||
| 190 | static_assert(sizeof(LaunchParams) == LaunchParams::NUM_LAUNCH_PARAMETERS * sizeof(u32), | ||
| 191 | "KeplerCompute LaunchParams has wrong size"); | ||
| 192 | |||
| 46 | /// Write the value to the register identified by method. | 193 | /// Write the value to the register identified by method. |
| 47 | void CallMethod(const GPU::MethodCall& method_call); | 194 | void CallMethod(const GPU::MethodCall& method_call); |
| 48 | 195 | ||
| 49 | private: | 196 | private: |
| 197 | Core::System& system; | ||
| 198 | VideoCore::RasterizerInterface& rasterizer; | ||
| 50 | MemoryManager& memory_manager; | 199 | MemoryManager& memory_manager; |
| 200 | Upload::State upload_state; | ||
| 201 | |||
| 202 | void ProcessLaunch(); | ||
| 51 | }; | 203 | }; |
| 52 | 204 | ||
| 53 | #define ASSERT_REG_POSITION(field_name, position) \ | 205 | #define ASSERT_REG_POSITION(field_name, position) \ |
| 54 | static_assert(offsetof(KeplerCompute::Regs, field_name) == position * 4, \ | 206 | static_assert(offsetof(KeplerCompute::Regs, field_name) == position * 4, \ |
| 55 | "Field " #field_name " has invalid position") | 207 | "Field " #field_name " has invalid position") |
| 56 | 208 | ||
| 209 | #define ASSERT_LAUNCH_PARAM_POSITION(field_name, position) \ | ||
| 210 | static_assert(offsetof(KeplerCompute::LaunchParams, field_name) == position * 4, \ | ||
| 211 | "Field " #field_name " has invalid position") | ||
| 212 | |||
| 213 | ASSERT_REG_POSITION(upload, 0x60); | ||
| 214 | ASSERT_REG_POSITION(exec_upload, 0x6C); | ||
| 215 | ASSERT_REG_POSITION(data_upload, 0x6D); | ||
| 57 | ASSERT_REG_POSITION(launch, 0xAF); | 216 | ASSERT_REG_POSITION(launch, 0xAF); |
| 217 | ASSERT_REG_POSITION(tsc, 0x557); | ||
| 218 | ASSERT_REG_POSITION(tic, 0x55D); | ||
| 219 | ASSERT_REG_POSITION(code_loc, 0x582); | ||
| 220 | ASSERT_REG_POSITION(texture_const_buffer_index, 0x982); | ||
| 221 | ASSERT_LAUNCH_PARAM_POSITION(program_start, 0x8); | ||
| 222 | ASSERT_LAUNCH_PARAM_POSITION(grid_dim_x, 0xC); | ||
| 223 | ASSERT_LAUNCH_PARAM_POSITION(shared_alloc, 0x11); | ||
| 224 | ASSERT_LAUNCH_PARAM_POSITION(block_dim_x, 0x12); | ||
| 225 | ASSERT_LAUNCH_PARAM_POSITION(memory_config, 0x14); | ||
| 226 | ASSERT_LAUNCH_PARAM_POSITION(const_buffer_config, 0x1D); | ||
| 58 | 227 | ||
| 59 | #undef ASSERT_REG_POSITION | 228 | #undef ASSERT_REG_POSITION |
| 60 | 229 | ||
diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp index 7387886a3..0561f676c 100644 --- a/src/video_core/engines/kepler_memory.cpp +++ b/src/video_core/engines/kepler_memory.cpp | |||
| @@ -14,9 +14,8 @@ | |||
| 14 | 14 | ||
| 15 | namespace Tegra::Engines { | 15 | namespace Tegra::Engines { |
| 16 | 16 | ||
| 17 | KeplerMemory::KeplerMemory(Core::System& system, VideoCore::RasterizerInterface& rasterizer, | 17 | KeplerMemory::KeplerMemory(Core::System& system, MemoryManager& memory_manager) |
| 18 | MemoryManager& memory_manager) | 18 | : system{system}, memory_manager{memory_manager}, upload_state{memory_manager, regs.upload} {} |
| 19 | : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager} {} | ||
| 20 | 19 | ||
| 21 | KeplerMemory::~KeplerMemory() = default; | 20 | KeplerMemory::~KeplerMemory() = default; |
| 22 | 21 | ||
| @@ -28,46 +27,18 @@ void KeplerMemory::CallMethod(const GPU::MethodCall& method_call) { | |||
| 28 | 27 | ||
| 29 | switch (method_call.method) { | 28 | switch (method_call.method) { |
| 30 | case KEPLERMEMORY_REG_INDEX(exec): { | 29 | case KEPLERMEMORY_REG_INDEX(exec): { |
| 31 | ProcessExec(); | 30 | upload_state.ProcessExec(regs.exec.linear != 0); |
| 32 | break; | 31 | break; |
| 33 | } | 32 | } |
| 34 | case KEPLERMEMORY_REG_INDEX(data): { | 33 | case KEPLERMEMORY_REG_INDEX(data): { |
| 35 | ProcessData(method_call.argument, method_call.IsLastCall()); | 34 | const bool is_last_call = method_call.IsLastCall(); |
| 35 | upload_state.ProcessData(method_call.argument, is_last_call); | ||
| 36 | if (is_last_call) { | ||
| 37 | system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite(); | ||
| 38 | } | ||
| 36 | break; | 39 | break; |
| 37 | } | 40 | } |
| 38 | } | 41 | } |
| 39 | } | 42 | } |
| 40 | 43 | ||
| 41 | void KeplerMemory::ProcessExec() { | ||
| 42 | state.write_offset = 0; | ||
| 43 | state.copy_size = regs.line_length_in * regs.line_count; | ||
| 44 | state.inner_buffer.resize(state.copy_size); | ||
| 45 | } | ||
| 46 | |||
| 47 | void KeplerMemory::ProcessData(u32 data, bool is_last_call) { | ||
| 48 | const u32 sub_copy_size = std::min(4U, state.copy_size - state.write_offset); | ||
| 49 | std::memcpy(&state.inner_buffer[state.write_offset], ®s.data, sub_copy_size); | ||
| 50 | state.write_offset += sub_copy_size; | ||
| 51 | if (is_last_call) { | ||
| 52 | const GPUVAddr address{regs.dest.Address()}; | ||
| 53 | if (regs.exec.linear != 0) { | ||
| 54 | memory_manager.WriteBlock(address, state.inner_buffer.data(), state.copy_size); | ||
| 55 | } else { | ||
| 56 | UNIMPLEMENTED_IF(regs.dest.z != 0); | ||
| 57 | UNIMPLEMENTED_IF(regs.dest.depth != 1); | ||
| 58 | UNIMPLEMENTED_IF(regs.dest.BlockWidth() != 1); | ||
| 59 | UNIMPLEMENTED_IF(regs.dest.BlockDepth() != 1); | ||
| 60 | const std::size_t dst_size = Tegra::Texture::CalculateSize( | ||
| 61 | true, 1, regs.dest.width, regs.dest.height, 1, regs.dest.BlockHeight(), 1); | ||
| 62 | std::vector<u8> tmp_buffer(dst_size); | ||
| 63 | memory_manager.ReadBlock(address, tmp_buffer.data(), dst_size); | ||
| 64 | Tegra::Texture::SwizzleKepler(regs.dest.width, regs.dest.height, regs.dest.x, | ||
| 65 | regs.dest.y, regs.dest.BlockHeight(), state.copy_size, | ||
| 66 | state.inner_buffer.data(), tmp_buffer.data()); | ||
| 67 | memory_manager.WriteBlock(address, tmp_buffer.data(), dst_size); | ||
| 68 | } | ||
| 69 | system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite(); | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | } // namespace Tegra::Engines | 44 | } // namespace Tegra::Engines |
diff --git a/src/video_core/engines/kepler_memory.h b/src/video_core/engines/kepler_memory.h index 5f892ddad..f3bc675a9 100644 --- a/src/video_core/engines/kepler_memory.h +++ b/src/video_core/engines/kepler_memory.h | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include "common/bit_field.h" | 10 | #include "common/bit_field.h" |
| 11 | #include "common/common_funcs.h" | 11 | #include "common/common_funcs.h" |
| 12 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 13 | #include "video_core/engines/engine_upload.h" | ||
| 13 | #include "video_core/gpu.h" | 14 | #include "video_core/gpu.h" |
| 14 | 15 | ||
| 15 | namespace Core { | 16 | namespace Core { |
| @@ -20,19 +21,20 @@ namespace Tegra { | |||
| 20 | class MemoryManager; | 21 | class MemoryManager; |
| 21 | } | 22 | } |
| 22 | 23 | ||
| 23 | namespace VideoCore { | ||
| 24 | class RasterizerInterface; | ||
| 25 | } | ||
| 26 | |||
| 27 | namespace Tegra::Engines { | 24 | namespace Tegra::Engines { |
| 28 | 25 | ||
| 26 | /** | ||
| 27 | * This Engine is known as P2MF. Documentation can be found in: | ||
| 28 | * https://github.com/envytools/envytools/blob/master/rnndb/graph/gk104_p2mf.xml | ||
| 29 | * https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/nouveau/nvc0/nve4_p2mf.xml.h | ||
| 30 | */ | ||
| 31 | |||
| 29 | #define KEPLERMEMORY_REG_INDEX(field_name) \ | 32 | #define KEPLERMEMORY_REG_INDEX(field_name) \ |
| 30 | (offsetof(Tegra::Engines::KeplerMemory::Regs, field_name) / sizeof(u32)) | 33 | (offsetof(Tegra::Engines::KeplerMemory::Regs, field_name) / sizeof(u32)) |
| 31 | 34 | ||
| 32 | class KeplerMemory final { | 35 | class KeplerMemory final { |
| 33 | public: | 36 | public: |
| 34 | KeplerMemory(Core::System& system, VideoCore::RasterizerInterface& rasterizer, | 37 | KeplerMemory(Core::System& system, MemoryManager& memory_manager); |
| 35 | MemoryManager& memory_manager); | ||
| 36 | ~KeplerMemory(); | 38 | ~KeplerMemory(); |
| 37 | 39 | ||
| 38 | /// Write the value to the register identified by method. | 40 | /// Write the value to the register identified by method. |
| @@ -45,42 +47,7 @@ public: | |||
| 45 | struct { | 47 | struct { |
| 46 | INSERT_PADDING_WORDS(0x60); | 48 | INSERT_PADDING_WORDS(0x60); |
| 47 | 49 | ||
| 48 | u32 line_length_in; | 50 | Upload::Registers upload; |
| 49 | u32 line_count; | ||
| 50 | |||
| 51 | struct { | ||
| 52 | u32 address_high; | ||
| 53 | u32 address_low; | ||
| 54 | u32 pitch; | ||
| 55 | union { | ||
| 56 | BitField<0, 4, u32> block_width; | ||
| 57 | BitField<4, 4, u32> block_height; | ||
| 58 | BitField<8, 4, u32> block_depth; | ||
| 59 | }; | ||
| 60 | u32 width; | ||
| 61 | u32 height; | ||
| 62 | u32 depth; | ||
| 63 | u32 z; | ||
| 64 | u32 x; | ||
| 65 | u32 y; | ||
| 66 | |||
| 67 | GPUVAddr Address() const { | ||
| 68 | return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | | ||
| 69 | address_low); | ||
| 70 | } | ||
| 71 | |||
| 72 | u32 BlockWidth() const { | ||
| 73 | return 1U << block_width.Value(); | ||
| 74 | } | ||
| 75 | |||
| 76 | u32 BlockHeight() const { | ||
| 77 | return 1U << block_height.Value(); | ||
| 78 | } | ||
| 79 | |||
| 80 | u32 BlockDepth() const { | ||
| 81 | return 1U << block_depth.Value(); | ||
| 82 | } | ||
| 83 | } dest; | ||
| 84 | 51 | ||
| 85 | struct { | 52 | struct { |
| 86 | union { | 53 | union { |
| @@ -96,28 +63,17 @@ public: | |||
| 96 | }; | 63 | }; |
| 97 | } regs{}; | 64 | } regs{}; |
| 98 | 65 | ||
| 99 | struct { | ||
| 100 | u32 write_offset = 0; | ||
| 101 | u32 copy_size = 0; | ||
| 102 | std::vector<u8> inner_buffer; | ||
| 103 | } state{}; | ||
| 104 | |||
| 105 | private: | 66 | private: |
| 106 | Core::System& system; | 67 | Core::System& system; |
| 107 | VideoCore::RasterizerInterface& rasterizer; | ||
| 108 | MemoryManager& memory_manager; | 68 | MemoryManager& memory_manager; |
| 109 | 69 | Upload::State upload_state; | |
| 110 | void ProcessExec(); | ||
| 111 | void ProcessData(u32 data, bool is_last_call); | ||
| 112 | }; | 70 | }; |
| 113 | 71 | ||
| 114 | #define ASSERT_REG_POSITION(field_name, position) \ | 72 | #define ASSERT_REG_POSITION(field_name, position) \ |
| 115 | static_assert(offsetof(KeplerMemory::Regs, field_name) == position * 4, \ | 73 | static_assert(offsetof(KeplerMemory::Regs, field_name) == position * 4, \ |
| 116 | "Field " #field_name " has invalid position") | 74 | "Field " #field_name " has invalid position") |
| 117 | 75 | ||
| 118 | ASSERT_REG_POSITION(line_length_in, 0x60); | 76 | ASSERT_REG_POSITION(upload, 0x60); |
| 119 | ASSERT_REG_POSITION(line_count, 0x61); | ||
| 120 | ASSERT_REG_POSITION(dest, 0x62); | ||
| 121 | ASSERT_REG_POSITION(exec, 0x6C); | 77 | ASSERT_REG_POSITION(exec, 0x6C); |
| 122 | ASSERT_REG_POSITION(data, 0x6D); | 78 | ASSERT_REG_POSITION(data, 0x6D); |
| 123 | #undef ASSERT_REG_POSITION | 79 | #undef ASSERT_REG_POSITION |
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 9780417f2..39968d403 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp | |||
| @@ -20,8 +20,8 @@ constexpr u32 MacroRegistersStart = 0xE00; | |||
| 20 | 20 | ||
| 21 | Maxwell3D::Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& rasterizer, | 21 | Maxwell3D::Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& rasterizer, |
| 22 | MemoryManager& memory_manager) | 22 | MemoryManager& memory_manager) |
| 23 | : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager}, macro_interpreter{ | 23 | : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager}, |
| 24 | *this} { | 24 | macro_interpreter{*this}, upload_state{memory_manager, regs.upload} { |
| 25 | InitializeRegisterDefaults(); | 25 | InitializeRegisterDefaults(); |
| 26 | } | 26 | } |
| 27 | 27 | ||
| @@ -34,9 +34,9 @@ void Maxwell3D::InitializeRegisterDefaults() { | |||
| 34 | 34 | ||
| 35 | // Depth range near/far is not always set, but is expected to be the default 0.0f, 1.0f. This is | 35 | // Depth range near/far is not always set, but is expected to be the default 0.0f, 1.0f. This is |
| 36 | // needed for ARMS. | 36 | // needed for ARMS. |
| 37 | for (std::size_t viewport{}; viewport < Regs::NumViewports; ++viewport) { | 37 | for (auto& viewport : regs.viewports) { |
| 38 | regs.viewports[viewport].depth_range_near = 0.0f; | 38 | viewport.depth_range_near = 0.0f; |
| 39 | regs.viewports[viewport].depth_range_far = 1.0f; | 39 | viewport.depth_range_far = 1.0f; |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | // Doom and Bomberman seems to use the uninitialized registers and just enable blend | 42 | // Doom and Bomberman seems to use the uninitialized registers and just enable blend |
| @@ -47,13 +47,13 @@ void Maxwell3D::InitializeRegisterDefaults() { | |||
| 47 | regs.blend.equation_a = Regs::Blend::Equation::Add; | 47 | regs.blend.equation_a = Regs::Blend::Equation::Add; |
| 48 | regs.blend.factor_source_a = Regs::Blend::Factor::One; | 48 | regs.blend.factor_source_a = Regs::Blend::Factor::One; |
| 49 | regs.blend.factor_dest_a = Regs::Blend::Factor::Zero; | 49 | regs.blend.factor_dest_a = Regs::Blend::Factor::Zero; |
| 50 | for (std::size_t blend_index = 0; blend_index < Regs::NumRenderTargets; blend_index++) { | 50 | for (auto& blend : regs.independent_blend) { |
| 51 | regs.independent_blend[blend_index].equation_rgb = Regs::Blend::Equation::Add; | 51 | blend.equation_rgb = Regs::Blend::Equation::Add; |
| 52 | regs.independent_blend[blend_index].factor_source_rgb = Regs::Blend::Factor::One; | 52 | blend.factor_source_rgb = Regs::Blend::Factor::One; |
| 53 | regs.independent_blend[blend_index].factor_dest_rgb = Regs::Blend::Factor::Zero; | 53 | blend.factor_dest_rgb = Regs::Blend::Factor::Zero; |
| 54 | regs.independent_blend[blend_index].equation_a = Regs::Blend::Equation::Add; | 54 | blend.equation_a = Regs::Blend::Equation::Add; |
| 55 | regs.independent_blend[blend_index].factor_source_a = Regs::Blend::Factor::One; | 55 | blend.factor_source_a = Regs::Blend::Factor::One; |
| 56 | regs.independent_blend[blend_index].factor_dest_a = Regs::Blend::Factor::Zero; | 56 | blend.factor_dest_a = Regs::Blend::Factor::Zero; |
| 57 | } | 57 | } |
| 58 | regs.stencil_front_op_fail = Regs::StencilOp::Keep; | 58 | regs.stencil_front_op_fail = Regs::StencilOp::Keep; |
| 59 | regs.stencil_front_op_zfail = Regs::StencilOp::Keep; | 59 | regs.stencil_front_op_zfail = Regs::StencilOp::Keep; |
| @@ -75,11 +75,11 @@ void Maxwell3D::InitializeRegisterDefaults() { | |||
| 75 | 75 | ||
| 76 | // TODO(bunnei): Some games do not initialize the color masks (e.g. Sonic Mania). Assuming a | 76 | // TODO(bunnei): Some games do not initialize the color masks (e.g. Sonic Mania). Assuming a |
| 77 | // default of enabled fixes rendering here. | 77 | // default of enabled fixes rendering here. |
| 78 | for (std::size_t color_mask = 0; color_mask < Regs::NumRenderTargets; color_mask++) { | 78 | for (auto& color_mask : regs.color_mask) { |
| 79 | regs.color_mask[color_mask].R.Assign(1); | 79 | color_mask.R.Assign(1); |
| 80 | regs.color_mask[color_mask].G.Assign(1); | 80 | color_mask.G.Assign(1); |
| 81 | regs.color_mask[color_mask].B.Assign(1); | 81 | color_mask.B.Assign(1); |
| 82 | regs.color_mask[color_mask].A.Assign(1); | 82 | color_mask.A.Assign(1); |
| 83 | } | 83 | } |
| 84 | 84 | ||
| 85 | // Commercial games seem to assume this value is enabled and nouveau sets this value manually. | 85 | // Commercial games seem to assume this value is enabled and nouveau sets this value manually. |
| @@ -178,13 +178,13 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) { | |||
| 178 | 178 | ||
| 179 | // Vertex buffer | 179 | // Vertex buffer |
| 180 | if (method >= MAXWELL3D_REG_INDEX(vertex_array) && | 180 | if (method >= MAXWELL3D_REG_INDEX(vertex_array) && |
| 181 | method < MAXWELL3D_REG_INDEX(vertex_array) + 4 * 32) { | 181 | method < MAXWELL3D_REG_INDEX(vertex_array) + 4 * Regs::NumVertexArrays) { |
| 182 | dirty_flags.vertex_array.set((method - MAXWELL3D_REG_INDEX(vertex_array)) >> 2); | 182 | dirty_flags.vertex_array.set((method - MAXWELL3D_REG_INDEX(vertex_array)) >> 2); |
| 183 | } else if (method >= MAXWELL3D_REG_INDEX(vertex_array_limit) && | 183 | } else if (method >= MAXWELL3D_REG_INDEX(vertex_array_limit) && |
| 184 | method < MAXWELL3D_REG_INDEX(vertex_array_limit) + 2 * 32) { | 184 | method < MAXWELL3D_REG_INDEX(vertex_array_limit) + 2 * Regs::NumVertexArrays) { |
| 185 | dirty_flags.vertex_array.set((method - MAXWELL3D_REG_INDEX(vertex_array_limit)) >> 1); | 185 | dirty_flags.vertex_array.set((method - MAXWELL3D_REG_INDEX(vertex_array_limit)) >> 1); |
| 186 | } else if (method >= MAXWELL3D_REG_INDEX(instanced_arrays) && | 186 | } else if (method >= MAXWELL3D_REG_INDEX(instanced_arrays) && |
| 187 | method < MAXWELL3D_REG_INDEX(instanced_arrays) + 32) { | 187 | method < MAXWELL3D_REG_INDEX(instanced_arrays) + Regs::NumVertexArrays) { |
| 188 | dirty_flags.vertex_array.set(method - MAXWELL3D_REG_INDEX(instanced_arrays)); | 188 | dirty_flags.vertex_array.set(method - MAXWELL3D_REG_INDEX(instanced_arrays)); |
| 189 | } | 189 | } |
| 190 | } | 190 | } |
| @@ -253,6 +253,18 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) { | |||
| 253 | ProcessSyncPoint(); | 253 | ProcessSyncPoint(); |
| 254 | break; | 254 | break; |
| 255 | } | 255 | } |
| 256 | case MAXWELL3D_REG_INDEX(exec_upload): { | ||
| 257 | upload_state.ProcessExec(regs.exec_upload.linear != 0); | ||
| 258 | break; | ||
| 259 | } | ||
| 260 | case MAXWELL3D_REG_INDEX(data_upload): { | ||
| 261 | const bool is_last_call = method_call.IsLastCall(); | ||
| 262 | upload_state.ProcessData(method_call.argument, is_last_call); | ||
| 263 | if (is_last_call) { | ||
| 264 | dirty_flags.OnMemoryWrite(); | ||
| 265 | } | ||
| 266 | break; | ||
| 267 | } | ||
| 256 | default: | 268 | default: |
| 257 | break; | 269 | break; |
| 258 | } | 270 | } |
| @@ -430,7 +442,7 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const { | |||
| 430 | const auto a_type = tic_entry.a_type.Value(); | 442 | const auto a_type = tic_entry.a_type.Value(); |
| 431 | 443 | ||
| 432 | // TODO(Subv): Different data types for separate components are not supported | 444 | // TODO(Subv): Different data types for separate components are not supported |
| 433 | ASSERT(r_type == g_type && r_type == b_type && r_type == a_type); | 445 | DEBUG_ASSERT(r_type == g_type && r_type == b_type && r_type == a_type); |
| 434 | 446 | ||
| 435 | return tic_entry; | 447 | return tic_entry; |
| 436 | } | 448 | } |
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index cc2424d38..f342c78e6 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <bitset> | 8 | #include <bitset> |
| 9 | #include <type_traits> | ||
| 9 | #include <unordered_map> | 10 | #include <unordered_map> |
| 10 | #include <vector> | 11 | #include <vector> |
| 11 | 12 | ||
| @@ -14,6 +15,7 @@ | |||
| 14 | #include "common/common_funcs.h" | 15 | #include "common/common_funcs.h" |
| 15 | #include "common/common_types.h" | 16 | #include "common/common_types.h" |
| 16 | #include "common/math_util.h" | 17 | #include "common/math_util.h" |
| 18 | #include "video_core/engines/engine_upload.h" | ||
| 17 | #include "video_core/gpu.h" | 19 | #include "video_core/gpu.h" |
| 18 | #include "video_core/macro_interpreter.h" | 20 | #include "video_core/macro_interpreter.h" |
| 19 | #include "video_core/textures/texture.h" | 21 | #include "video_core/textures/texture.h" |
| @@ -32,6 +34,12 @@ class RasterizerInterface; | |||
| 32 | 34 | ||
| 33 | namespace Tegra::Engines { | 35 | namespace Tegra::Engines { |
| 34 | 36 | ||
| 37 | /** | ||
| 38 | * This Engine is known as GF100_3D. Documentation can be found in: | ||
| 39 | * https://github.com/envytools/envytools/blob/master/rnndb/graph/gf100_3d.xml | ||
| 40 | * https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/nouveau/nvc0/nvc0_3d.xml.h | ||
| 41 | */ | ||
| 42 | |||
| 35 | #define MAXWELL3D_REG_INDEX(field_name) \ | 43 | #define MAXWELL3D_REG_INDEX(field_name) \ |
| 36 | (offsetof(Tegra::Engines::Maxwell3D::Regs, field_name) / sizeof(u32)) | 44 | (offsetof(Tegra::Engines::Maxwell3D::Regs, field_name) / sizeof(u32)) |
| 37 | 45 | ||
| @@ -51,6 +59,7 @@ public: | |||
| 51 | static constexpr std::size_t NumCBData = 16; | 59 | static constexpr std::size_t NumCBData = 16; |
| 52 | static constexpr std::size_t NumVertexArrays = 32; | 60 | static constexpr std::size_t NumVertexArrays = 32; |
| 53 | static constexpr std::size_t NumVertexAttributes = 32; | 61 | static constexpr std::size_t NumVertexAttributes = 32; |
| 62 | static constexpr std::size_t NumVaryings = 31; | ||
| 54 | static constexpr std::size_t NumTextureSamplers = 32; | 63 | static constexpr std::size_t NumTextureSamplers = 32; |
| 55 | static constexpr std::size_t NumClipDistances = 8; | 64 | static constexpr std::size_t NumClipDistances = 8; |
| 56 | static constexpr std::size_t MaxShaderProgram = 6; | 65 | static constexpr std::size_t MaxShaderProgram = 6; |
| @@ -243,9 +252,10 @@ public: | |||
| 243 | return "10_10_10_2"; | 252 | return "10_10_10_2"; |
| 244 | case Size::Size_11_11_10: | 253 | case Size::Size_11_11_10: |
| 245 | return "11_11_10"; | 254 | return "11_11_10"; |
| 255 | default: | ||
| 256 | UNREACHABLE(); | ||
| 257 | return {}; | ||
| 246 | } | 258 | } |
| 247 | UNREACHABLE(); | ||
| 248 | return {}; | ||
| 249 | } | 259 | } |
| 250 | 260 | ||
| 251 | std::string TypeString() const { | 261 | std::string TypeString() const { |
| @@ -579,7 +589,18 @@ public: | |||
| 579 | u32 bind; | 589 | u32 bind; |
| 580 | } macros; | 590 | } macros; |
| 581 | 591 | ||
| 582 | INSERT_PADDING_WORDS(0x69); | 592 | INSERT_PADDING_WORDS(0x17); |
| 593 | |||
| 594 | Upload::Registers upload; | ||
| 595 | struct { | ||
| 596 | union { | ||
| 597 | BitField<0, 1, u32> linear; | ||
| 598 | }; | ||
| 599 | } exec_upload; | ||
| 600 | |||
| 601 | u32 data_upload; | ||
| 602 | |||
| 603 | INSERT_PADDING_WORDS(0x44); | ||
| 583 | 604 | ||
| 584 | struct { | 605 | struct { |
| 585 | union { | 606 | union { |
| @@ -1088,6 +1109,7 @@ public: | |||
| 1088 | } regs{}; | 1109 | } regs{}; |
| 1089 | 1110 | ||
| 1090 | static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), "Maxwell3D Regs has wrong size"); | 1111 | static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), "Maxwell3D Regs has wrong size"); |
| 1112 | static_assert(std::is_trivially_copyable_v<Regs>, "Maxwell3D Regs must be trivially copyable"); | ||
| 1091 | 1113 | ||
| 1092 | struct State { | 1114 | struct State { |
| 1093 | struct ConstBufferInfo { | 1115 | struct ConstBufferInfo { |
| @@ -1175,6 +1197,8 @@ private: | |||
| 1175 | /// Interpreter for the macro codes uploaded to the GPU. | 1197 | /// Interpreter for the macro codes uploaded to the GPU. |
| 1176 | MacroInterpreter macro_interpreter; | 1198 | MacroInterpreter macro_interpreter; |
| 1177 | 1199 | ||
| 1200 | Upload::State upload_state; | ||
| 1201 | |||
| 1178 | /// Retrieves information about a specific TIC entry from the TIC buffer. | 1202 | /// Retrieves information about a specific TIC entry from the TIC buffer. |
| 1179 | Texture::TICEntry GetTICEntry(u32 tic_index) const; | 1203 | Texture::TICEntry GetTICEntry(u32 tic_index) const; |
| 1180 | 1204 | ||
| @@ -1218,6 +1242,9 @@ private: | |||
| 1218 | "Field " #field_name " has invalid position") | 1242 | "Field " #field_name " has invalid position") |
| 1219 | 1243 | ||
| 1220 | ASSERT_REG_POSITION(macros, 0x45); | 1244 | ASSERT_REG_POSITION(macros, 0x45); |
| 1245 | ASSERT_REG_POSITION(upload, 0x60); | ||
| 1246 | ASSERT_REG_POSITION(exec_upload, 0x6C); | ||
| 1247 | ASSERT_REG_POSITION(data_upload, 0x6D); | ||
| 1221 | ASSERT_REG_POSITION(sync_info, 0xB2); | 1248 | ASSERT_REG_POSITION(sync_info, 0xB2); |
| 1222 | ASSERT_REG_POSITION(tfb_enabled, 0x1D1); | 1249 | ASSERT_REG_POSITION(tfb_enabled, 0x1D1); |
| 1223 | ASSERT_REG_POSITION(rt, 0x200); | 1250 | ASSERT_REG_POSITION(rt, 0x200); |
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index 2426d0067..3a5dfef0c 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp | |||
| @@ -83,57 +83,66 @@ void MaxwellDMA::HandleCopy() { | |||
| 83 | 83 | ||
| 84 | ASSERT(regs.exec.enable_2d == 1); | 84 | ASSERT(regs.exec.enable_2d == 1); |
| 85 | 85 | ||
| 86 | const std::size_t copy_size = regs.x_count * regs.y_count; | 86 | if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) { |
| 87 | ASSERT(regs.src_params.size_z == 1); | ||
| 88 | // If the input is tiled and the output is linear, deswizzle the input and copy it over. | ||
| 89 | const u32 src_bytes_per_pixel = regs.src_pitch / regs.src_params.size_x; | ||
| 90 | const std::size_t src_size = Texture::CalculateSize( | ||
| 91 | true, src_bytes_per_pixel, regs.src_params.size_x, regs.src_params.size_y, | ||
| 92 | regs.src_params.size_z, regs.src_params.BlockHeight(), regs.src_params.BlockDepth()); | ||
| 87 | 93 | ||
| 88 | auto source_ptr{memory_manager.GetPointer(source)}; | 94 | const std::size_t dst_size = regs.dst_pitch * regs.y_count; |
| 89 | auto dst_ptr{memory_manager.GetPointer(dest)}; | ||
| 90 | 95 | ||
| 91 | if (!source_ptr) { | 96 | if (read_buffer.size() < src_size) { |
| 92 | LOG_ERROR(HW_GPU, "source_ptr is invalid"); | 97 | read_buffer.resize(src_size); |
| 93 | return; | 98 | } |
| 94 | } | ||
| 95 | 99 | ||
| 96 | if (!dst_ptr) { | 100 | if (write_buffer.size() < dst_size) { |
| 97 | LOG_ERROR(HW_GPU, "dst_ptr is invalid"); | 101 | write_buffer.resize(dst_size); |
| 98 | return; | 102 | } |
| 99 | } | ||
| 100 | 103 | ||
| 101 | const auto FlushAndInvalidate = [&](u32 src_size, u64 dst_size) { | 104 | memory_manager.ReadBlock(source, read_buffer.data(), src_size); |
| 102 | // TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated | 105 | memory_manager.ReadBlock(dest, write_buffer.data(), dst_size); |
| 103 | // copying. | ||
| 104 | rasterizer.FlushRegion(ToCacheAddr(source_ptr), src_size); | ||
| 105 | 106 | ||
| 106 | // We have to invalidate the destination region to evict any outdated surfaces from the | 107 | Texture::UnswizzleSubrect(regs.x_count, regs.y_count, regs.dst_pitch, |
| 107 | // cache. We do this before actually writing the new data because the destination address | 108 | regs.src_params.size_x, src_bytes_per_pixel, read_buffer.data(), |
| 108 | // might contain a dirty surface that will have to be written back to memory. | 109 | write_buffer.data(), regs.src_params.BlockHeight(), |
| 109 | rasterizer.InvalidateRegion(ToCacheAddr(dst_ptr), dst_size); | 110 | regs.src_params.pos_x, regs.src_params.pos_y); |
| 110 | }; | ||
| 111 | 111 | ||
| 112 | if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) { | 112 | memory_manager.WriteBlock(dest, write_buffer.data(), dst_size); |
| 113 | ASSERT(regs.src_params.size_z == 1); | 113 | } else { |
| 114 | // If the input is tiled and the output is linear, deswizzle the input and copy it over. | 114 | ASSERT(regs.dst_params.BlockDepth() == 1); |
| 115 | 115 | ||
| 116 | const u32 src_bytes_per_pixel = regs.src_pitch / regs.src_params.size_x; | 116 | const u32 src_bytes_per_pixel = regs.src_pitch / regs.x_count; |
| 117 | 117 | ||
| 118 | FlushAndInvalidate(regs.src_pitch * regs.src_params.size_y, | 118 | const std::size_t dst_size = Texture::CalculateSize( |
| 119 | copy_size * src_bytes_per_pixel); | 119 | true, src_bytes_per_pixel, regs.dst_params.size_x, regs.dst_params.size_y, |
| 120 | regs.dst_params.size_z, regs.dst_params.BlockHeight(), regs.dst_params.BlockDepth()); | ||
| 120 | 121 | ||
| 121 | Texture::UnswizzleSubrect(regs.x_count, regs.y_count, regs.dst_pitch, | 122 | const std::size_t dst_layer_size = Texture::CalculateSize( |
| 122 | regs.src_params.size_x, src_bytes_per_pixel, source_ptr, dst_ptr, | 123 | true, src_bytes_per_pixel, regs.dst_params.size_x, regs.dst_params.size_y, 1, |
| 123 | regs.src_params.BlockHeight(), regs.src_params.pos_x, | 124 | regs.dst_params.BlockHeight(), regs.dst_params.BlockDepth()); |
| 124 | regs.src_params.pos_y); | ||
| 125 | } else { | ||
| 126 | ASSERT(regs.dst_params.size_z == 1); | ||
| 127 | ASSERT(regs.src_pitch == regs.x_count); | ||
| 128 | 125 | ||
| 129 | const u32 src_bpp = regs.src_pitch / regs.x_count; | 126 | const std::size_t src_size = regs.src_pitch * regs.y_count; |
| 130 | 127 | ||
| 131 | FlushAndInvalidate(regs.src_pitch * regs.y_count, | 128 | if (read_buffer.size() < src_size) { |
| 132 | regs.dst_params.size_x * regs.dst_params.size_y * src_bpp); | 129 | read_buffer.resize(src_size); |
| 130 | } | ||
| 131 | |||
| 132 | if (write_buffer.size() < dst_size) { | ||
| 133 | write_buffer.resize(dst_size); | ||
| 134 | } | ||
| 135 | |||
| 136 | memory_manager.ReadBlock(source, read_buffer.data(), src_size); | ||
| 137 | memory_manager.ReadBlock(dest, write_buffer.data(), dst_size); | ||
| 133 | 138 | ||
| 134 | // If the input is linear and the output is tiled, swizzle the input and copy it over. | 139 | // If the input is linear and the output is tiled, swizzle the input and copy it over. |
| 135 | Texture::SwizzleSubrect(regs.x_count, regs.y_count, regs.src_pitch, regs.dst_params.size_x, | 140 | Texture::SwizzleSubrect(regs.x_count, regs.y_count, regs.src_pitch, regs.dst_params.size_x, |
| 136 | src_bpp, dst_ptr, source_ptr, regs.dst_params.BlockHeight()); | 141 | src_bytes_per_pixel, |
| 142 | write_buffer.data() + dst_layer_size * regs.dst_params.pos_z, | ||
| 143 | read_buffer.data(), regs.dst_params.BlockHeight()); | ||
| 144 | |||
| 145 | memory_manager.WriteBlock(dest, write_buffer.data(), dst_size); | ||
| 137 | } | 146 | } |
| 138 | } | 147 | } |
| 139 | 148 | ||
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h index c6b649842..e5942f671 100644 --- a/src/video_core/engines/maxwell_dma.h +++ b/src/video_core/engines/maxwell_dma.h | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <cstddef> | 8 | #include <cstddef> |
| 9 | #include <vector> | ||
| 9 | #include "common/bit_field.h" | 10 | #include "common/bit_field.h" |
| 10 | #include "common/common_funcs.h" | 11 | #include "common/common_funcs.h" |
| 11 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| @@ -25,6 +26,11 @@ class RasterizerInterface; | |||
| 25 | 26 | ||
| 26 | namespace Tegra::Engines { | 27 | namespace Tegra::Engines { |
| 27 | 28 | ||
| 29 | /** | ||
| 30 | * This Engine is known as GK104_Copy. Documentation can be found in: | ||
| 31 | * https://github.com/envytools/envytools/blob/master/rnndb/fifo/gk104_copy.xml | ||
| 32 | */ | ||
| 33 | |||
| 28 | class MaxwellDMA final { | 34 | class MaxwellDMA final { |
| 29 | public: | 35 | public: |
| 30 | explicit MaxwellDMA(Core::System& system, VideoCore::RasterizerInterface& rasterizer, | 36 | explicit MaxwellDMA(Core::System& system, VideoCore::RasterizerInterface& rasterizer, |
| @@ -63,6 +69,16 @@ public: | |||
| 63 | 69 | ||
| 64 | static_assert(sizeof(Parameters) == 24, "Parameters has wrong size"); | 70 | static_assert(sizeof(Parameters) == 24, "Parameters has wrong size"); |
| 65 | 71 | ||
| 72 | enum class ComponentMode : u32 { | ||
| 73 | Src0 = 0, | ||
| 74 | Src1 = 1, | ||
| 75 | Src2 = 2, | ||
| 76 | Src3 = 3, | ||
| 77 | Const0 = 4, | ||
| 78 | Const1 = 5, | ||
| 79 | Zero = 6, | ||
| 80 | }; | ||
| 81 | |||
| 66 | enum class CopyMode : u32 { | 82 | enum class CopyMode : u32 { |
| 67 | None = 0, | 83 | None = 0, |
| 68 | Unk1 = 1, | 84 | Unk1 = 1, |
| @@ -128,7 +144,26 @@ public: | |||
| 128 | u32 x_count; | 144 | u32 x_count; |
| 129 | u32 y_count; | 145 | u32 y_count; |
| 130 | 146 | ||
| 131 | INSERT_PADDING_WORDS(0xBB); | 147 | INSERT_PADDING_WORDS(0xB8); |
| 148 | |||
| 149 | u32 const0; | ||
| 150 | u32 const1; | ||
| 151 | union { | ||
| 152 | BitField<0, 4, ComponentMode> component0; | ||
| 153 | BitField<4, 4, ComponentMode> component1; | ||
| 154 | BitField<8, 4, ComponentMode> component2; | ||
| 155 | BitField<12, 4, ComponentMode> component3; | ||
| 156 | BitField<16, 2, u32> component_size; | ||
| 157 | BitField<20, 3, u32> src_num_components; | ||
| 158 | BitField<24, 3, u32> dst_num_components; | ||
| 159 | |||
| 160 | u32 SrcBytePerPixel() const { | ||
| 161 | return src_num_components.Value() * component_size.Value(); | ||
| 162 | } | ||
| 163 | u32 DstBytePerPixel() const { | ||
| 164 | return dst_num_components.Value() * component_size.Value(); | ||
| 165 | } | ||
| 166 | } swizzle_config; | ||
| 132 | 167 | ||
| 133 | Parameters dst_params; | 168 | Parameters dst_params; |
| 134 | 169 | ||
| @@ -149,6 +184,9 @@ private: | |||
| 149 | 184 | ||
| 150 | MemoryManager& memory_manager; | 185 | MemoryManager& memory_manager; |
| 151 | 186 | ||
| 187 | std::vector<u8> read_buffer; | ||
| 188 | std::vector<u8> write_buffer; | ||
| 189 | |||
| 152 | /// Performs the copy from the source buffer to the destination buffer as configured in the | 190 | /// Performs the copy from the source buffer to the destination buffer as configured in the |
| 153 | /// registers. | 191 | /// registers. |
| 154 | void HandleCopy(); | 192 | void HandleCopy(); |
| @@ -165,6 +203,9 @@ ASSERT_REG_POSITION(src_pitch, 0x104); | |||
| 165 | ASSERT_REG_POSITION(dst_pitch, 0x105); | 203 | ASSERT_REG_POSITION(dst_pitch, 0x105); |
| 166 | ASSERT_REG_POSITION(x_count, 0x106); | 204 | ASSERT_REG_POSITION(x_count, 0x106); |
| 167 | ASSERT_REG_POSITION(y_count, 0x107); | 205 | ASSERT_REG_POSITION(y_count, 0x107); |
| 206 | ASSERT_REG_POSITION(const0, 0x1C0); | ||
| 207 | ASSERT_REG_POSITION(const1, 0x1C1); | ||
| 208 | ASSERT_REG_POSITION(swizzle_config, 0x1C2); | ||
| 168 | ASSERT_REG_POSITION(dst_params, 0x1C3); | 209 | ASSERT_REG_POSITION(dst_params, 0x1C3); |
| 169 | ASSERT_REG_POSITION(src_params, 0x1CA); | 210 | ASSERT_REG_POSITION(src_params, 0x1CA); |
| 170 | 211 | ||
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index e5b4eadea..e83f25fa1 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h | |||
| @@ -98,6 +98,10 @@ union Attribute { | |||
| 98 | BitField<22, 2, u64> element; | 98 | BitField<22, 2, u64> element; |
| 99 | BitField<24, 6, Index> index; | 99 | BitField<24, 6, Index> index; |
| 100 | BitField<47, 3, AttributeSize> size; | 100 | BitField<47, 3, AttributeSize> size; |
| 101 | |||
| 102 | bool IsPhysical() const { | ||
| 103 | return element == 0 && static_cast<u64>(index.Value()) == 0; | ||
| 104 | } | ||
| 101 | } fmt20; | 105 | } fmt20; |
| 102 | 106 | ||
| 103 | union { | 107 | union { |
| @@ -499,6 +503,11 @@ enum class SystemVariable : u64 { | |||
| 499 | CircularQueueEntryAddressHigh = 0x63, | 503 | CircularQueueEntryAddressHigh = 0x63, |
| 500 | }; | 504 | }; |
| 501 | 505 | ||
| 506 | enum class PhysicalAttributeDirection : u64 { | ||
| 507 | Input = 0, | ||
| 508 | Output = 1, | ||
| 509 | }; | ||
| 510 | |||
| 502 | union Instruction { | 511 | union Instruction { |
| 503 | Instruction& operator=(const Instruction& instr) { | 512 | Instruction& operator=(const Instruction& instr) { |
| 504 | value = instr.value; | 513 | value = instr.value; |
| @@ -521,6 +530,11 @@ union Instruction { | |||
| 521 | BitField<48, 16, u64> opcode; | 530 | BitField<48, 16, u64> opcode; |
| 522 | 531 | ||
| 523 | union { | 532 | union { |
| 533 | BitField<8, 8, Register> gpr; | ||
| 534 | BitField<20, 24, s64> offset; | ||
| 535 | } gmem; | ||
| 536 | |||
| 537 | union { | ||
| 524 | BitField<20, 16, u64> imm20_16; | 538 | BitField<20, 16, u64> imm20_16; |
| 525 | BitField<20, 19, u64> imm20_19; | 539 | BitField<20, 19, u64> imm20_19; |
| 526 | BitField<20, 32, s64> imm20_32; | 540 | BitField<20, 32, s64> imm20_32; |
| @@ -587,6 +601,7 @@ union Instruction { | |||
| 587 | } alu; | 601 | } alu; |
| 588 | 602 | ||
| 589 | union { | 603 | union { |
| 604 | BitField<38, 1, u64> idx; | ||
| 590 | BitField<51, 1, u64> saturate; | 605 | BitField<51, 1, u64> saturate; |
| 591 | BitField<52, 2, IpaSampleMode> sample_mode; | 606 | BitField<52, 2, IpaSampleMode> sample_mode; |
| 592 | BitField<54, 2, IpaInterpMode> interp_mode; | 607 | BitField<54, 2, IpaInterpMode> interp_mode; |
| @@ -802,16 +817,25 @@ union Instruction { | |||
| 802 | union { | 817 | union { |
| 803 | BitField<48, 3, UniformType> type; | 818 | BitField<48, 3, UniformType> type; |
| 804 | BitField<46, 2, u64> cache_mode; | 819 | BitField<46, 2, u64> cache_mode; |
| 805 | BitField<20, 24, s64> immediate_offset; | ||
| 806 | } ldg; | 820 | } ldg; |
| 807 | 821 | ||
| 808 | union { | 822 | union { |
| 809 | BitField<48, 3, UniformType> type; | 823 | BitField<48, 3, UniformType> type; |
| 810 | BitField<46, 2, u64> cache_mode; | 824 | BitField<46, 2, u64> cache_mode; |
| 811 | BitField<20, 24, s64> immediate_offset; | ||
| 812 | } stg; | 825 | } stg; |
| 813 | 826 | ||
| 814 | union { | 827 | union { |
| 828 | BitField<32, 1, PhysicalAttributeDirection> direction; | ||
| 829 | BitField<47, 3, AttributeSize> size; | ||
| 830 | BitField<20, 11, u64> address; | ||
| 831 | } al2p; | ||
| 832 | |||
| 833 | union { | ||
| 834 | BitField<53, 3, UniformType> type; | ||
| 835 | BitField<52, 1, u64> extended; | ||
| 836 | } generic; | ||
| 837 | |||
| 838 | union { | ||
| 815 | BitField<0, 3, u64> pred0; | 839 | BitField<0, 3, u64> pred0; |
| 816 | BitField<3, 3, u64> pred3; | 840 | BitField<3, 3, u64> pred3; |
| 817 | BitField<7, 1, u64> abs_a; | 841 | BitField<7, 1, u64> abs_a; |
| @@ -1371,11 +1395,14 @@ public: | |||
| 1371 | LD_L, | 1395 | LD_L, |
| 1372 | LD_S, | 1396 | LD_S, |
| 1373 | LD_C, | 1397 | LD_C, |
| 1398 | LD, // Load from generic memory | ||
| 1399 | LDG, // Load from global memory | ||
| 1374 | ST_A, | 1400 | ST_A, |
| 1375 | ST_L, | 1401 | ST_L, |
| 1376 | ST_S, | 1402 | ST_S, |
| 1377 | LDG, // Load from global memory | 1403 | ST, // Store in generic memory |
| 1378 | STG, // Store in global memory | 1404 | STG, // Store in global memory |
| 1405 | AL2P, // Transforms attribute memory into physical memory | ||
| 1379 | TEX, | 1406 | TEX, |
| 1380 | TEX_B, // Texture Load Bindless | 1407 | TEX_B, // Texture Load Bindless |
| 1381 | TXQ, // Texture Query | 1408 | TXQ, // Texture Query |
| @@ -1641,11 +1668,14 @@ private: | |||
| 1641 | INST("1110111101001---", Id::LD_S, Type::Memory, "LD_S"), | 1668 | INST("1110111101001---", Id::LD_S, Type::Memory, "LD_S"), |
| 1642 | INST("1110111101000---", Id::LD_L, Type::Memory, "LD_L"), | 1669 | INST("1110111101000---", Id::LD_L, Type::Memory, "LD_L"), |
| 1643 | INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"), | 1670 | INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"), |
| 1671 | INST("100-------------", Id::LD, Type::Memory, "LD"), | ||
| 1672 | INST("1110111011010---", Id::LDG, Type::Memory, "LDG"), | ||
| 1644 | INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"), | 1673 | INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"), |
| 1645 | INST("1110111101011---", Id::ST_S, Type::Memory, "ST_S"), | 1674 | INST("1110111101011---", Id::ST_S, Type::Memory, "ST_S"), |
| 1646 | INST("1110111101010---", Id::ST_L, Type::Memory, "ST_L"), | 1675 | INST("1110111101010---", Id::ST_L, Type::Memory, "ST_L"), |
| 1647 | INST("1110111011010---", Id::LDG, Type::Memory, "LDG"), | 1676 | INST("101-------------", Id::ST, Type::Memory, "ST"), |
| 1648 | INST("1110111011011---", Id::STG, Type::Memory, "STG"), | 1677 | INST("1110111011011---", Id::STG, Type::Memory, "STG"), |
| 1678 | INST("1110111110100---", Id::AL2P, Type::Memory, "AL2P"), | ||
| 1649 | INST("110000----111---", Id::TEX, Type::Texture, "TEX"), | 1679 | INST("110000----111---", Id::TEX, Type::Texture, "TEX"), |
| 1650 | INST("1101111010111---", Id::TEX_B, Type::Texture, "TEX_B"), | 1680 | INST("1101111010111---", Id::TEX_B, Type::Texture, "TEX_B"), |
| 1651 | INST("1101111101001---", Id::TXQ, Type::Texture, "TXQ"), | 1681 | INST("1101111101001---", Id::TXQ, Type::Texture, "TXQ"), |
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 4461083ff..52706505b 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -35,9 +35,9 @@ GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer) : renderer{ren | |||
| 35 | dma_pusher = std::make_unique<Tegra::DmaPusher>(*this); | 35 | dma_pusher = std::make_unique<Tegra::DmaPusher>(*this); |
| 36 | maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager); | 36 | maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager); |
| 37 | fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer, *memory_manager); | 37 | fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer, *memory_manager); |
| 38 | kepler_compute = std::make_unique<Engines::KeplerCompute>(*memory_manager); | 38 | kepler_compute = std::make_unique<Engines::KeplerCompute>(system, rasterizer, *memory_manager); |
| 39 | maxwell_dma = std::make_unique<Engines::MaxwellDMA>(system, rasterizer, *memory_manager); | 39 | maxwell_dma = std::make_unique<Engines::MaxwellDMA>(system, rasterizer, *memory_manager); |
| 40 | kepler_memory = std::make_unique<Engines::KeplerMemory>(system, rasterizer, *memory_manager); | 40 | kepler_memory = std::make_unique<Engines::KeplerMemory>(system, *memory_manager); |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | GPU::~GPU() = default; | 43 | GPU::~GPU() = default; |
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index c9a2077de..1e2ff46b0 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp | |||
| @@ -44,7 +44,7 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p | |||
| 44 | renderer.Rasterizer().FlushRegion(data->addr, data->size); | 44 | renderer.Rasterizer().FlushRegion(data->addr, data->size); |
| 45 | } else if (const auto data = std::get_if<InvalidateRegionCommand>(&next.data)) { | 45 | } else if (const auto data = std::get_if<InvalidateRegionCommand>(&next.data)) { |
| 46 | renderer.Rasterizer().InvalidateRegion(data->addr, data->size); | 46 | renderer.Rasterizer().InvalidateRegion(data->addr, data->size); |
| 47 | } else if (const auto data = std::get_if<EndProcessingCommand>(&next.data)) { | 47 | } else if (std::holds_alternative<EndProcessingCommand>(next.data)) { |
| 48 | return; | 48 | return; |
| 49 | } else { | 49 | } else { |
| 50 | UNREACHABLE(); | 50 | UNREACHABLE(); |
| @@ -118,7 +118,7 @@ void SynchState::WaitForSynchronization(u64 fence) { | |||
| 118 | // Wait for the GPU to be idle (all commands to be executed) | 118 | // Wait for the GPU to be idle (all commands to be executed) |
| 119 | { | 119 | { |
| 120 | MICROPROFILE_SCOPE(GPU_wait); | 120 | MICROPROFILE_SCOPE(GPU_wait); |
| 121 | std::unique_lock<std::mutex> lock{synchronization_mutex}; | 121 | std::unique_lock lock{synchronization_mutex}; |
| 122 | synchronization_condition.wait(lock, [this, fence] { return signaled_fence >= fence; }); | 122 | synchronization_condition.wait(lock, [this, fence] { return signaled_fence >= fence; }); |
| 123 | } | 123 | } |
| 124 | } | 124 | } |
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h index cc14527c7..05a168a72 100644 --- a/src/video_core/gpu_thread.h +++ b/src/video_core/gpu_thread.h | |||
| @@ -81,12 +81,6 @@ struct CommandDataContainer { | |||
| 81 | CommandDataContainer(CommandData&& data, u64 next_fence) | 81 | CommandDataContainer(CommandData&& data, u64 next_fence) |
| 82 | : data{std::move(data)}, fence{next_fence} {} | 82 | : data{std::move(data)}, fence{next_fence} {} |
| 83 | 83 | ||
| 84 | CommandDataContainer& operator=(const CommandDataContainer& t) { | ||
| 85 | data = std::move(t.data); | ||
| 86 | fence = t.fence; | ||
| 87 | return *this; | ||
| 88 | } | ||
| 89 | |||
| 90 | CommandData data; | 84 | CommandData data; |
| 91 | u64 fence{}; | 85 | u64 fence{}; |
| 92 | }; | 86 | }; |
| @@ -109,7 +103,7 @@ struct SynchState final { | |||
| 109 | 103 | ||
| 110 | void TrySynchronize() { | 104 | void TrySynchronize() { |
| 111 | if (IsSynchronized()) { | 105 | if (IsSynchronized()) { |
| 112 | std::lock_guard<std::mutex> lock{synchronization_mutex}; | 106 | std::lock_guard lock{synchronization_mutex}; |
| 113 | synchronization_condition.notify_one(); | 107 | synchronization_condition.notify_one(); |
| 114 | } | 108 | } |
| 115 | } | 109 | } |
diff --git a/src/video_core/macro_interpreter.cpp b/src/video_core/macro_interpreter.cpp index 524d9ea5a..c766ed692 100644 --- a/src/video_core/macro_interpreter.cpp +++ b/src/video_core/macro_interpreter.cpp | |||
| @@ -118,10 +118,12 @@ bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) { | |||
| 118 | static_cast<u32>(opcode.operation.Value())); | 118 | static_cast<u32>(opcode.operation.Value())); |
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | // An instruction with the Exit flag will not actually | ||
| 122 | // cause an exit if it's executed inside a delay slot. | ||
| 123 | // TODO(Blinkhawk): Reversed to always exit. The behavior explained above requires further | ||
| 124 | // testing on the MME code. | ||
| 121 | if (opcode.is_exit) { | 125 | if (opcode.is_exit) { |
| 122 | // Exit has a delay slot, execute the next instruction | 126 | // Exit has a delay slot, execute the next instruction |
| 123 | // Note: Executing an exit during a branch delay slot will cause the instruction at the | ||
| 124 | // branch target to be executed before exiting. | ||
| 125 | Step(offset, true); | 127 | Step(offset, true); |
| 126 | return false; | 128 | return false; |
| 127 | } | 129 | } |
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index 6c98c6701..5d8d126c1 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp | |||
| @@ -25,6 +25,8 @@ MemoryManager::MemoryManager(VideoCore::RasterizerInterface& rasterizer) : raste | |||
| 25 | UpdatePageTableForVMA(initial_vma); | 25 | UpdatePageTableForVMA(initial_vma); |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | MemoryManager::~MemoryManager() = default; | ||
| 29 | |||
| 28 | GPUVAddr MemoryManager::AllocateSpace(u64 size, u64 align) { | 30 | GPUVAddr MemoryManager::AllocateSpace(u64 size, u64 align) { |
| 29 | const u64 aligned_size{Common::AlignUp(size, page_size)}; | 31 | const u64 aligned_size{Common::AlignUp(size, page_size)}; |
| 30 | const GPUVAddr gpu_addr{FindFreeRegion(address_space_base, aligned_size)}; | 32 | const GPUVAddr gpu_addr{FindFreeRegion(address_space_base, aligned_size)}; |
| @@ -199,11 +201,11 @@ const u8* MemoryManager::GetPointer(GPUVAddr addr) const { | |||
| 199 | return {}; | 201 | return {}; |
| 200 | } | 202 | } |
| 201 | 203 | ||
| 202 | bool MemoryManager::IsBlockContinous(const GPUVAddr start, const std::size_t size) { | 204 | bool MemoryManager::IsBlockContinuous(const GPUVAddr start, const std::size_t size) const { |
| 203 | const GPUVAddr end = start + size; | 205 | const GPUVAddr end = start + size; |
| 204 | const auto host_ptr_start = reinterpret_cast<std::uintptr_t>(GetPointer(start)); | 206 | const auto host_ptr_start = reinterpret_cast<std::uintptr_t>(GetPointer(start)); |
| 205 | const auto host_ptr_end = reinterpret_cast<std::uintptr_t>(GetPointer(end)); | 207 | const auto host_ptr_end = reinterpret_cast<std::uintptr_t>(GetPointer(end)); |
| 206 | const std::size_t range = static_cast<std::size_t>(host_ptr_end - host_ptr_start); | 208 | const auto range = static_cast<std::size_t>(host_ptr_end - host_ptr_start); |
| 207 | return range == size; | 209 | return range == size; |
| 208 | } | 210 | } |
| 209 | 211 | ||
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index e4f0c4bd6..113f9d8f3 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h | |||
| @@ -47,7 +47,8 @@ struct VirtualMemoryArea { | |||
| 47 | 47 | ||
| 48 | class MemoryManager final { | 48 | class MemoryManager final { |
| 49 | public: | 49 | public: |
| 50 | MemoryManager(VideoCore::RasterizerInterface& rasterizer); | 50 | explicit MemoryManager(VideoCore::RasterizerInterface& rasterizer); |
| 51 | ~MemoryManager(); | ||
| 51 | 52 | ||
| 52 | GPUVAddr AllocateSpace(u64 size, u64 align); | 53 | GPUVAddr AllocateSpace(u64 size, u64 align); |
| 53 | GPUVAddr AllocateSpace(GPUVAddr addr, u64 size, u64 align); | 54 | GPUVAddr AllocateSpace(GPUVAddr addr, u64 size, u64 align); |
| @@ -65,18 +66,18 @@ public: | |||
| 65 | u8* GetPointer(GPUVAddr addr); | 66 | u8* GetPointer(GPUVAddr addr); |
| 66 | const u8* GetPointer(GPUVAddr addr) const; | 67 | const u8* GetPointer(GPUVAddr addr) const; |
| 67 | 68 | ||
| 68 | // Returns true if the block is continous in host memory, false otherwise | 69 | /// Returns true if the block is continuous in host memory, false otherwise |
| 69 | bool IsBlockContinous(const GPUVAddr start, const std::size_t size); | 70 | bool IsBlockContinuous(GPUVAddr start, std::size_t size) const; |
| 70 | 71 | ||
| 71 | /** | 72 | /** |
| 72 | * ReadBlock and WriteBlock are full read and write operations over virtual | 73 | * ReadBlock and WriteBlock are full read and write operations over virtual |
| 73 | * GPU Memory. It's important to use these when GPU memory may not be continous | 74 | * GPU Memory. It's important to use these when GPU memory may not be continuous |
| 74 | * in the Host Memory counterpart. Note: This functions cause Host GPU Memory | 75 | * in the Host Memory counterpart. Note: This functions cause Host GPU Memory |
| 75 | * Flushes and Invalidations, respectively to each operation. | 76 | * Flushes and Invalidations, respectively to each operation. |
| 76 | */ | 77 | */ |
| 77 | void ReadBlock(GPUVAddr src_addr, void* dest_buffer, const std::size_t size) const; | 78 | void ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) const; |
| 78 | void WriteBlock(GPUVAddr dest_addr, const void* src_buffer, const std::size_t size); | 79 | void WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size); |
| 79 | void CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, const std::size_t size); | 80 | void CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size); |
| 80 | 81 | ||
| 81 | /** | 82 | /** |
| 82 | * ReadBlockUnsafe and WriteBlockUnsafe are special versions of ReadBlock and | 83 | * ReadBlockUnsafe and WriteBlockUnsafe are special versions of ReadBlock and |
| @@ -88,9 +89,9 @@ public: | |||
| 88 | * WriteBlockUnsafe instead of WriteBlock since it shouldn't invalidate the texture | 89 | * WriteBlockUnsafe instead of WriteBlock since it shouldn't invalidate the texture |
| 89 | * being flushed. | 90 | * being flushed. |
| 90 | */ | 91 | */ |
| 91 | void ReadBlockUnsafe(GPUVAddr src_addr, void* dest_buffer, const std::size_t size) const; | 92 | void ReadBlockUnsafe(GPUVAddr src_addr, void* dest_buffer, std::size_t size) const; |
| 92 | void WriteBlockUnsafe(GPUVAddr dest_addr, const void* src_buffer, const std::size_t size); | 93 | void WriteBlockUnsafe(GPUVAddr dest_addr, const void* src_buffer, std::size_t size); |
| 93 | void CopyBlockUnsafe(GPUVAddr dest_addr, GPUVAddr src_addr, const std::size_t size); | 94 | void CopyBlockUnsafe(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size); |
| 94 | 95 | ||
| 95 | private: | 96 | private: |
| 96 | using VMAMap = std::map<GPUVAddr, VirtualMemoryArea>; | 97 | using VMAMap = std::map<GPUVAddr, VirtualMemoryArea>; |
| @@ -111,10 +112,10 @@ private: | |||
| 111 | /** | 112 | /** |
| 112 | * Maps an unmanaged host memory pointer at a given address. | 113 | * Maps an unmanaged host memory pointer at a given address. |
| 113 | * | 114 | * |
| 114 | * @param target The guest address to start the mapping at. | 115 | * @param target The guest address to start the mapping at. |
| 115 | * @param memory The memory to be mapped. | 116 | * @param memory The memory to be mapped. |
| 116 | * @param size Size of the mapping. | 117 | * @param size Size of the mapping in bytes. |
| 117 | * @param state MemoryState tag to attach to the VMA. | 118 | * @param backing_addr The base address of the range to back this mapping. |
| 118 | */ | 119 | */ |
| 119 | VMAHandle MapBackingMemory(GPUVAddr target, u8* memory, u64 size, VAddr backing_addr); | 120 | VMAHandle MapBackingMemory(GPUVAddr target, u8* memory, u64 size, VAddr backing_addr); |
| 120 | 121 | ||
| @@ -124,7 +125,7 @@ private: | |||
| 124 | /// Converts a VMAHandle to a mutable VMAIter. | 125 | /// Converts a VMAHandle to a mutable VMAIter. |
| 125 | VMAIter StripIterConstness(const VMAHandle& iter); | 126 | VMAIter StripIterConstness(const VMAHandle& iter); |
| 126 | 127 | ||
| 127 | /// Marks as the specfied VMA as allocated. | 128 | /// Marks as the specified VMA as allocated. |
| 128 | VMAIter Allocate(VMAIter vma); | 129 | VMAIter Allocate(VMAIter vma); |
| 129 | 130 | ||
| 130 | /** | 131 | /** |
diff --git a/src/video_core/rasterizer_cache.h b/src/video_core/rasterizer_cache.h index 291772186..0c4ea1494 100644 --- a/src/video_core/rasterizer_cache.h +++ b/src/video_core/rasterizer_cache.h | |||
| @@ -37,9 +37,6 @@ public: | |||
| 37 | /// Gets the size of the shader in guest memory, required for cache management | 37 | /// Gets the size of the shader in guest memory, required for cache management |
| 38 | virtual std::size_t GetSizeInBytes() const = 0; | 38 | virtual std::size_t GetSizeInBytes() const = 0; |
| 39 | 39 | ||
| 40 | /// Wriets any cached resources back to memory | ||
| 41 | virtual void Flush() = 0; | ||
| 42 | |||
| 43 | /// Sets whether the cached object should be considered registered | 40 | /// Sets whether the cached object should be considered registered |
| 44 | void SetIsRegistered(bool registered) { | 41 | void SetIsRegistered(bool registered) { |
| 45 | is_registered = registered; | 42 | is_registered = registered; |
| @@ -147,8 +144,9 @@ protected: | |||
| 147 | 144 | ||
| 148 | object->SetIsRegistered(false); | 145 | object->SetIsRegistered(false); |
| 149 | rasterizer.UpdatePagesCachedCount(object->GetCpuAddr(), object->GetSizeInBytes(), -1); | 146 | rasterizer.UpdatePagesCachedCount(object->GetCpuAddr(), object->GetSizeInBytes(), -1); |
| 147 | const CacheAddr addr = object->GetCacheAddr(); | ||
| 150 | interval_cache.subtract({GetInterval(object), ObjectSet{object}}); | 148 | interval_cache.subtract({GetInterval(object), ObjectSet{object}}); |
| 151 | map_cache.erase(object->GetCacheAddr()); | 149 | map_cache.erase(addr); |
| 152 | } | 150 | } |
| 153 | 151 | ||
| 154 | /// Returns a ticks counter used for tracking when cached objects were last modified | 152 | /// Returns a ticks counter used for tracking when cached objects were last modified |
| @@ -158,6 +156,8 @@ protected: | |||
| 158 | return ++modified_ticks; | 156 | return ++modified_ticks; |
| 159 | } | 157 | } |
| 160 | 158 | ||
| 159 | virtual void FlushObjectInner(const T& object) = 0; | ||
| 160 | |||
| 161 | /// Flushes the specified object, updating appropriate cache state as needed | 161 | /// Flushes the specified object, updating appropriate cache state as needed |
| 162 | void FlushObject(const T& object) { | 162 | void FlushObject(const T& object) { |
| 163 | std::lock_guard lock{mutex}; | 163 | std::lock_guard lock{mutex}; |
| @@ -165,7 +165,7 @@ protected: | |||
| 165 | if (!object->IsDirty()) { | 165 | if (!object->IsDirty()) { |
| 166 | return; | 166 | return; |
| 167 | } | 167 | } |
| 168 | object->Flush(); | 168 | FlushObjectInner(object); |
| 169 | object->MarkAsModified(false, *this); | 169 | object->MarkAsModified(false, *this); |
| 170 | } | 170 | } |
| 171 | 171 | ||
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h index fc33aa433..f9247a40e 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.h +++ b/src/video_core/renderer_opengl/gl_buffer_cache.h | |||
| @@ -42,9 +42,6 @@ public: | |||
| 42 | return alignment; | 42 | return alignment; |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | // We do not have to flush this cache as things in it are never modified by us. | ||
| 46 | void Flush() override {} | ||
| 47 | |||
| 48 | private: | 45 | private: |
| 49 | VAddr cpu_addr{}; | 46 | VAddr cpu_addr{}; |
| 50 | std::size_t size{}; | 47 | std::size_t size{}; |
| @@ -75,6 +72,9 @@ public: | |||
| 75 | protected: | 72 | protected: |
| 76 | void AlignBuffer(std::size_t alignment); | 73 | void AlignBuffer(std::size_t alignment); |
| 77 | 74 | ||
| 75 | // We do not have to flush this cache as things in it are never modified by us. | ||
| 76 | void FlushObjectInner(const std::shared_ptr<CachedBufferEntry>& object) override {} | ||
| 77 | |||
| 78 | private: | 78 | private: |
| 79 | OGLStreamBuffer stream_buffer; | 79 | OGLStreamBuffer stream_buffer; |
| 80 | 80 | ||
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index b6d9e0ddb..1d1581f49 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp | |||
| @@ -21,11 +21,21 @@ T GetInteger(GLenum pname) { | |||
| 21 | 21 | ||
| 22 | Device::Device() { | 22 | Device::Device() { |
| 23 | uniform_buffer_alignment = GetInteger<std::size_t>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT); | 23 | uniform_buffer_alignment = GetInteger<std::size_t>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT); |
| 24 | max_vertex_attributes = GetInteger<u32>(GL_MAX_VERTEX_ATTRIBS); | ||
| 25 | max_varyings = GetInteger<u32>(GL_MAX_VARYING_VECTORS); | ||
| 24 | has_variable_aoffi = TestVariableAoffi(); | 26 | has_variable_aoffi = TestVariableAoffi(); |
| 25 | } | 27 | } |
| 26 | 28 | ||
| 29 | Device::Device(std::nullptr_t) { | ||
| 30 | uniform_buffer_alignment = 0; | ||
| 31 | max_vertex_attributes = 16; | ||
| 32 | max_varyings = 15; | ||
| 33 | has_variable_aoffi = true; | ||
| 34 | } | ||
| 35 | |||
| 27 | bool Device::TestVariableAoffi() { | 36 | bool Device::TestVariableAoffi() { |
| 28 | const GLchar* AOFFI_TEST = R"(#version 430 core | 37 | const GLchar* AOFFI_TEST = R"(#version 430 core |
| 38 | // This is a unit test, please ignore me on apitrace bug reports. | ||
| 29 | uniform sampler2D tex; | 39 | uniform sampler2D tex; |
| 30 | uniform ivec2 variable_offset; | 40 | uniform ivec2 variable_offset; |
| 31 | void main() { | 41 | void main() { |
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h index 78ff5ee58..de8490682 100644 --- a/src/video_core/renderer_opengl/gl_device.h +++ b/src/video_core/renderer_opengl/gl_device.h | |||
| @@ -5,17 +5,27 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <cstddef> | 7 | #include <cstddef> |
| 8 | #include "common/common_types.h" | ||
| 8 | 9 | ||
| 9 | namespace OpenGL { | 10 | namespace OpenGL { |
| 10 | 11 | ||
| 11 | class Device { | 12 | class Device { |
| 12 | public: | 13 | public: |
| 13 | Device(); | 14 | explicit Device(); |
| 15 | explicit Device(std::nullptr_t); | ||
| 14 | 16 | ||
| 15 | std::size_t GetUniformBufferAlignment() const { | 17 | std::size_t GetUniformBufferAlignment() const { |
| 16 | return uniform_buffer_alignment; | 18 | return uniform_buffer_alignment; |
| 17 | } | 19 | } |
| 18 | 20 | ||
| 21 | u32 GetMaxVertexAttributes() const { | ||
| 22 | return max_vertex_attributes; | ||
| 23 | } | ||
| 24 | |||
| 25 | u32 GetMaxVaryings() const { | ||
| 26 | return max_varyings; | ||
| 27 | } | ||
| 28 | |||
| 19 | bool HasVariableAoffi() const { | 29 | bool HasVariableAoffi() const { |
| 20 | return has_variable_aoffi; | 30 | return has_variable_aoffi; |
| 21 | } | 31 | } |
| @@ -24,6 +34,8 @@ private: | |||
| 24 | static bool TestVariableAoffi(); | 34 | static bool TestVariableAoffi(); |
| 25 | 35 | ||
| 26 | std::size_t uniform_buffer_alignment{}; | 36 | std::size_t uniform_buffer_alignment{}; |
| 37 | u32 max_vertex_attributes{}; | ||
| 38 | u32 max_varyings{}; | ||
| 27 | bool has_variable_aoffi{}; | 39 | bool has_variable_aoffi{}; |
| 28 | }; | 40 | }; |
| 29 | 41 | ||
diff --git a/src/video_core/renderer_opengl/gl_global_cache.h b/src/video_core/renderer_opengl/gl_global_cache.h index 196e6e278..2d467a240 100644 --- a/src/video_core/renderer_opengl/gl_global_cache.h +++ b/src/video_core/renderer_opengl/gl_global_cache.h | |||
| @@ -46,7 +46,7 @@ public: | |||
| 46 | /// Reloads the global region from guest memory | 46 | /// Reloads the global region from guest memory |
| 47 | void Reload(u32 size_); | 47 | void Reload(u32 size_); |
| 48 | 48 | ||
| 49 | void Flush() override; | 49 | void Flush(); |
| 50 | 50 | ||
| 51 | private: | 51 | private: |
| 52 | VAddr cpu_addr{}; | 52 | VAddr cpu_addr{}; |
| @@ -65,6 +65,11 @@ public: | |||
| 65 | GlobalRegion GetGlobalRegion(const GLShader::GlobalMemoryEntry& descriptor, | 65 | GlobalRegion GetGlobalRegion(const GLShader::GlobalMemoryEntry& descriptor, |
| 66 | Tegra::Engines::Maxwell3D::Regs::ShaderStage stage); | 66 | Tegra::Engines::Maxwell3D::Regs::ShaderStage stage); |
| 67 | 67 | ||
| 68 | protected: | ||
| 69 | void FlushObjectInner(const GlobalRegion& object) override { | ||
| 70 | object->Flush(); | ||
| 71 | } | ||
| 72 | |||
| 68 | private: | 73 | private: |
| 69 | GlobalRegion TryGetReservedGlobalRegion(CacheAddr addr, u32 size) const; | 74 | GlobalRegion TryGetReservedGlobalRegion(CacheAddr addr, u32 size) const; |
| 70 | GlobalRegion GetUncachedGlobalRegion(GPUVAddr addr, u8* host_ptr, u32 size); | 75 | GlobalRegion GetUncachedGlobalRegion(GPUVAddr addr, u8* host_ptr, u32 size); |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 9a088a503..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 { | |||
| 98 | } | 98 | } |
| 99 | }; | 99 | }; |
| 100 | 100 | ||
| 101 | RasterizerOpenGL::RasterizerOpenGL(Core::System& system, ScreenInfo& info) | 101 | RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window, |
| 102 | : res_cache{*this}, shader_cache{*this, system, device}, global_cache{*this}, system{system}, | 102 | ScreenInfo& info) |
| 103 | screen_info{info}, buffer_cache(*this, STREAM_BUFFER_SIZE) { | 103 | : res_cache{*this}, shader_cache{*this, system, emu_window, device}, |
| 104 | global_cache{*this}, system{system}, screen_info{info}, | ||
| 105 | buffer_cache(*this, STREAM_BUFFER_SIZE) { | ||
| 104 | OpenGLState::ApplyDefaultState(); | 106 | OpenGLState::ApplyDefaultState(); |
| 105 | 107 | ||
| 106 | shader_program_manager = std::make_unique<GLShader::ProgramManager>(); | 108 | shader_program_manager = std::make_unique<GLShader::ProgramManager>(); |
| @@ -261,8 +263,8 @@ DrawParameters RasterizerOpenGL::SetupDraw() { | |||
| 261 | // MakeQuadArray always generates u32 indexes | 263 | // MakeQuadArray always generates u32 indexes |
| 262 | params.index_format = GL_UNSIGNED_INT; | 264 | params.index_format = GL_UNSIGNED_INT; |
| 263 | params.count = (regs.vertex_buffer.count / 4) * 6; | 265 | params.count = (regs.vertex_buffer.count / 4) * 6; |
| 264 | params.index_buffer_offset = | 266 | params.index_buffer_offset = primitive_assembler.MakeQuadArray( |
| 265 | primitive_assembler.MakeQuadArray(regs.vertex_buffer.first, params.count); | 267 | regs.vertex_buffer.first, regs.vertex_buffer.count); |
| 266 | } | 268 | } |
| 267 | return params; | 269 | return params; |
| 268 | } | 270 | } |
| @@ -305,6 +307,8 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { | |||
| 305 | case Maxwell::ShaderProgram::Geometry: | 307 | case Maxwell::ShaderProgram::Geometry: |
| 306 | shader_program_manager->UseTrivialGeometryShader(); | 308 | shader_program_manager->UseTrivialGeometryShader(); |
| 307 | break; | 309 | break; |
| 310 | default: | ||
| 311 | break; | ||
| 308 | } | 312 | } |
| 309 | continue; | 313 | continue; |
| 310 | } | 314 | } |
| @@ -920,8 +924,8 @@ void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) { | |||
| 920 | viewport.y = viewport_rect.bottom; | 924 | viewport.y = viewport_rect.bottom; |
| 921 | viewport.width = viewport_rect.GetWidth(); | 925 | viewport.width = viewport_rect.GetWidth(); |
| 922 | viewport.height = viewport_rect.GetHeight(); | 926 | viewport.height = viewport_rect.GetHeight(); |
| 923 | viewport.depth_range_far = regs.viewports[i].depth_range_far; | 927 | viewport.depth_range_far = src.depth_range_far; |
| 924 | viewport.depth_range_near = regs.viewports[i].depth_range_near; | 928 | viewport.depth_range_near = src.depth_range_near; |
| 925 | } | 929 | } |
| 926 | state.depth_clamp.far_plane = regs.view_volume_clip_control.depth_clamp_far != 0; | 930 | state.depth_clamp.far_plane = regs.view_volume_clip_control.depth_clamp_far != 0; |
| 927 | state.depth_clamp.near_plane = regs.view_volume_clip_control.depth_clamp_near != 0; | 931 | state.depth_clamp.near_plane = regs.view_volume_clip_control.depth_clamp_near != 0; |
| @@ -1133,7 +1137,9 @@ void RasterizerOpenGL::SyncTransformFeedback() { | |||
| 1133 | 1137 | ||
| 1134 | void RasterizerOpenGL::SyncPointState() { | 1138 | void RasterizerOpenGL::SyncPointState() { |
| 1135 | const auto& regs = system.GPU().Maxwell3D().regs; | 1139 | const auto& regs = system.GPU().Maxwell3D().regs; |
| 1136 | state.point.size = regs.point_size; | 1140 | // Limit the point size to 1 since nouveau sometimes sets a point size of 0 (and that's invalid |
| 1141 | // in OpenGL). | ||
| 1142 | state.point.size = std::max(1.0f, regs.point_size); | ||
| 1137 | } | 1143 | } |
| 1138 | 1144 | ||
| 1139 | void RasterizerOpenGL::SyncPolygonOffset() { | 1145 | void RasterizerOpenGL::SyncPolygonOffset() { |
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; | |||
| 48 | 48 | ||
| 49 | class RasterizerOpenGL : public VideoCore::RasterizerInterface { | 49 | class RasterizerOpenGL : public VideoCore::RasterizerInterface { |
| 50 | public: | 50 | public: |
| 51 | explicit RasterizerOpenGL(Core::System& system, ScreenInfo& info); | 51 | explicit RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window, |
| 52 | ScreenInfo& info); | ||
| 52 | ~RasterizerOpenGL() override; | 53 | ~RasterizerOpenGL() override; |
| 53 | 54 | ||
| 54 | void DrawArrays() override; | 55 | void DrawArrays() override; |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 5a25f5b37..a7681902e 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | |||
| @@ -628,9 +628,11 @@ CachedSurface::CachedSurface(const SurfaceParams& params) | |||
| 628 | } | 628 | } |
| 629 | 629 | ||
| 630 | MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 192, 64)); | 630 | MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 192, 64)); |
| 631 | void CachedSurface::LoadGLBuffer() { | 631 | void CachedSurface::LoadGLBuffer(RasterizerTemporaryMemory& res_cache_tmp_mem) { |
| 632 | MICROPROFILE_SCOPE(OpenGL_SurfaceLoad); | 632 | MICROPROFILE_SCOPE(OpenGL_SurfaceLoad); |
| 633 | gl_buffer.resize(params.max_mip_level); | 633 | auto& gl_buffer = res_cache_tmp_mem.gl_buffer; |
| 634 | if (gl_buffer.size() < params.max_mip_level) | ||
| 635 | gl_buffer.resize(params.max_mip_level); | ||
| 634 | for (u32 i = 0; i < params.max_mip_level; i++) | 636 | for (u32 i = 0; i < params.max_mip_level; i++) |
| 635 | gl_buffer[i].resize(params.GetMipmapSizeGL(i)); | 637 | gl_buffer[i].resize(params.GetMipmapSizeGL(i)); |
| 636 | if (params.is_tiled) { | 638 | if (params.is_tiled) { |
| @@ -671,13 +673,13 @@ void CachedSurface::LoadGLBuffer() { | |||
| 671 | } | 673 | } |
| 672 | 674 | ||
| 673 | MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64)); | 675 | MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64)); |
| 674 | void CachedSurface::FlushGLBuffer() { | 676 | void CachedSurface::FlushGLBuffer(RasterizerTemporaryMemory& res_cache_tmp_mem) { |
| 675 | MICROPROFILE_SCOPE(OpenGL_SurfaceFlush); | 677 | MICROPROFILE_SCOPE(OpenGL_SurfaceFlush); |
| 676 | 678 | ||
| 677 | ASSERT_MSG(!IsPixelFormatASTC(params.pixel_format), "Unimplemented"); | 679 | ASSERT_MSG(!IsPixelFormatASTC(params.pixel_format), "Unimplemented"); |
| 678 | 680 | ||
| 681 | auto& gl_buffer = res_cache_tmp_mem.gl_buffer; | ||
| 679 | // OpenGL temporary buffer needs to be big enough to store raw texture size | 682 | // OpenGL temporary buffer needs to be big enough to store raw texture size |
| 680 | gl_buffer.resize(1); | ||
| 681 | gl_buffer[0].resize(GetSizeInBytes()); | 683 | gl_buffer[0].resize(GetSizeInBytes()); |
| 682 | 684 | ||
| 683 | const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); | 685 | const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); |
| @@ -713,10 +715,12 @@ void CachedSurface::FlushGLBuffer() { | |||
| 713 | } | 715 | } |
| 714 | } | 716 | } |
| 715 | 717 | ||
| 716 | void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle, | 718 | void CachedSurface::UploadGLMipmapTexture(RasterizerTemporaryMemory& res_cache_tmp_mem, u32 mip_map, |
| 717 | GLuint draw_fb_handle) { | 719 | GLuint read_fb_handle, GLuint draw_fb_handle) { |
| 718 | const auto& rect{params.GetRect(mip_map)}; | 720 | const auto& rect{params.GetRect(mip_map)}; |
| 719 | 721 | ||
| 722 | auto& gl_buffer = res_cache_tmp_mem.gl_buffer; | ||
| 723 | |||
| 720 | // Load data from memory to the surface | 724 | // Load data from memory to the surface |
| 721 | const auto x0 = static_cast<GLint>(rect.left); | 725 | const auto x0 = static_cast<GLint>(rect.left); |
| 722 | const auto y0 = static_cast<GLint>(rect.bottom); | 726 | const auto y0 = static_cast<GLint>(rect.bottom); |
| @@ -801,7 +805,6 @@ void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle, | |||
| 801 | tuple.type, &gl_buffer[mip_map][buffer_offset]); | 805 | tuple.type, &gl_buffer[mip_map][buffer_offset]); |
| 802 | break; | 806 | break; |
| 803 | case SurfaceTarget::TextureCubemap: { | 807 | case SurfaceTarget::TextureCubemap: { |
| 804 | std::size_t start = buffer_offset; | ||
| 805 | for (std::size_t face = 0; face < params.depth; ++face) { | 808 | for (std::size_t face = 0; face < params.depth; ++face) { |
| 806 | glTextureSubImage3D(texture.handle, mip_map, x0, y0, static_cast<GLint>(face), | 809 | glTextureSubImage3D(texture.handle, mip_map, x0, y0, static_cast<GLint>(face), |
| 807 | static_cast<GLsizei>(rect.GetWidth()), | 810 | static_cast<GLsizei>(rect.GetWidth()), |
| @@ -845,11 +848,12 @@ void CachedSurface::EnsureTextureDiscrepantView() { | |||
| 845 | } | 848 | } |
| 846 | 849 | ||
| 847 | MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 192, 64)); | 850 | MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 192, 64)); |
| 848 | void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle) { | 851 | void CachedSurface::UploadGLTexture(RasterizerTemporaryMemory& res_cache_tmp_mem, |
| 852 | GLuint read_fb_handle, GLuint draw_fb_handle) { | ||
| 849 | MICROPROFILE_SCOPE(OpenGL_TextureUL); | 853 | MICROPROFILE_SCOPE(OpenGL_TextureUL); |
| 850 | 854 | ||
| 851 | for (u32 i = 0; i < params.max_mip_level; i++) | 855 | for (u32 i = 0; i < params.max_mip_level; i++) |
| 852 | UploadGLMipmapTexture(i, read_fb_handle, draw_fb_handle); | 856 | UploadGLMipmapTexture(res_cache_tmp_mem, i, read_fb_handle, draw_fb_handle); |
| 853 | } | 857 | } |
| 854 | 858 | ||
| 855 | void CachedSurface::UpdateSwizzle(Tegra::Texture::SwizzleSource swizzle_x, | 859 | void CachedSurface::UpdateSwizzle(Tegra::Texture::SwizzleSource swizzle_x, |
| @@ -929,8 +933,8 @@ Surface RasterizerCacheOpenGL::GetColorBufferSurface(std::size_t index, bool pre | |||
| 929 | } | 933 | } |
| 930 | 934 | ||
| 931 | void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) { | 935 | void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) { |
| 932 | surface->LoadGLBuffer(); | 936 | surface->LoadGLBuffer(temporal_memory); |
| 933 | surface->UploadGLTexture(read_framebuffer.handle, draw_framebuffer.handle); | 937 | surface->UploadGLTexture(temporal_memory, read_framebuffer.handle, draw_framebuffer.handle); |
| 934 | surface->MarkAsModified(false, *this); | 938 | surface->MarkAsModified(false, *this); |
| 935 | surface->MarkForReload(false); | 939 | surface->MarkForReload(false); |
| 936 | } | 940 | } |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index db280dbb3..6263ef3e7 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h | |||
| @@ -355,6 +355,12 @@ namespace OpenGL { | |||
| 355 | 355 | ||
| 356 | class RasterizerOpenGL; | 356 | class RasterizerOpenGL; |
| 357 | 357 | ||
| 358 | // This is used to store temporary big buffers, | ||
| 359 | // instead of creating/destroying all the time | ||
| 360 | struct RasterizerTemporaryMemory { | ||
| 361 | std::vector<std::vector<u8>> gl_buffer; | ||
| 362 | }; | ||
| 363 | |||
| 358 | class CachedSurface final : public RasterizerCacheObject { | 364 | class CachedSurface final : public RasterizerCacheObject { |
| 359 | public: | 365 | public: |
| 360 | explicit CachedSurface(const SurfaceParams& params); | 366 | explicit CachedSurface(const SurfaceParams& params); |
| @@ -371,10 +377,6 @@ public: | |||
| 371 | return memory_size; | 377 | return memory_size; |
| 372 | } | 378 | } |
| 373 | 379 | ||
| 374 | void Flush() override { | ||
| 375 | FlushGLBuffer(); | ||
| 376 | } | ||
| 377 | |||
| 378 | const OGLTexture& Texture() const { | 380 | const OGLTexture& Texture() const { |
| 379 | return texture; | 381 | return texture; |
| 380 | } | 382 | } |
| @@ -397,11 +399,12 @@ public: | |||
| 397 | } | 399 | } |
| 398 | 400 | ||
| 399 | // Read/Write data in Switch memory to/from gl_buffer | 401 | // Read/Write data in Switch memory to/from gl_buffer |
| 400 | void LoadGLBuffer(); | 402 | void LoadGLBuffer(RasterizerTemporaryMemory& res_cache_tmp_mem); |
| 401 | void FlushGLBuffer(); | 403 | void FlushGLBuffer(RasterizerTemporaryMemory& res_cache_tmp_mem); |
| 402 | 404 | ||
| 403 | // Upload data in gl_buffer to this surface's texture | 405 | // Upload data in gl_buffer to this surface's texture |
| 404 | void UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle); | 406 | void UploadGLTexture(RasterizerTemporaryMemory& res_cache_tmp_mem, GLuint read_fb_handle, |
| 407 | GLuint draw_fb_handle); | ||
| 405 | 408 | ||
| 406 | void UpdateSwizzle(Tegra::Texture::SwizzleSource swizzle_x, | 409 | void UpdateSwizzle(Tegra::Texture::SwizzleSource swizzle_x, |
| 407 | Tegra::Texture::SwizzleSource swizzle_y, | 410 | Tegra::Texture::SwizzleSource swizzle_y, |
| @@ -429,13 +432,13 @@ public: | |||
| 429 | } | 432 | } |
| 430 | 433 | ||
| 431 | private: | 434 | private: |
| 432 | void UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle, GLuint draw_fb_handle); | 435 | void UploadGLMipmapTexture(RasterizerTemporaryMemory& res_cache_tmp_mem, u32 mip_map, |
| 436 | GLuint read_fb_handle, GLuint draw_fb_handle); | ||
| 433 | 437 | ||
| 434 | void EnsureTextureDiscrepantView(); | 438 | void EnsureTextureDiscrepantView(); |
| 435 | 439 | ||
| 436 | OGLTexture texture; | 440 | OGLTexture texture; |
| 437 | OGLTexture discrepant_view; | 441 | OGLTexture discrepant_view; |
| 438 | std::vector<std::vector<u8>> gl_buffer; | ||
| 439 | SurfaceParams params{}; | 442 | SurfaceParams params{}; |
| 440 | GLenum gl_target{}; | 443 | GLenum gl_target{}; |
| 441 | GLenum gl_internal_format{}; | 444 | GLenum gl_internal_format{}; |
| @@ -473,6 +476,11 @@ public: | |||
| 473 | void SignalPreDrawCall(); | 476 | void SignalPreDrawCall(); |
| 474 | void SignalPostDrawCall(); | 477 | void SignalPostDrawCall(); |
| 475 | 478 | ||
| 479 | protected: | ||
| 480 | void FlushObjectInner(const Surface& object) override { | ||
| 481 | object->FlushGLBuffer(temporal_memory); | ||
| 482 | } | ||
| 483 | |||
| 476 | private: | 484 | private: |
| 477 | void LoadSurface(const Surface& surface); | 485 | void LoadSurface(const Surface& surface); |
| 478 | Surface GetSurface(const SurfaceParams& params, bool preserve_contents = true); | 486 | Surface GetSurface(const SurfaceParams& params, bool preserve_contents = true); |
| @@ -519,6 +527,8 @@ private: | |||
| 519 | std::array<Surface, Maxwell::NumRenderTargets> current_color_buffers; | 527 | std::array<Surface, Maxwell::NumRenderTargets> current_color_buffers; |
| 520 | Surface last_depth_buffer; | 528 | Surface last_depth_buffer; |
| 521 | 529 | ||
| 530 | RasterizerTemporaryMemory temporal_memory; | ||
| 531 | |||
| 522 | using SurfaceIntervalCache = boost::icl::interval_map<CacheAddr, Surface>; | 532 | using SurfaceIntervalCache = boost::icl::interval_map<CacheAddr, Surface>; |
| 523 | using SurfaceInterval = typename SurfaceIntervalCache::interval_type; | 533 | using SurfaceInterval = typename SurfaceIntervalCache::interval_type; |
| 524 | 534 | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 2a81b1169..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 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <mutex> | ||
| 6 | #include <thread> | ||
| 5 | #include <boost/functional/hash.hpp> | 7 | #include <boost/functional/hash.hpp> |
| 6 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| 7 | #include "common/hash.h" | 9 | #include "common/hash.h" |
| 10 | #include "common/scope_exit.h" | ||
| 8 | #include "core/core.h" | 11 | #include "core/core.h" |
| 12 | #include "core/frontend/emu_window.h" | ||
| 9 | #include "video_core/engines/maxwell_3d.h" | 13 | #include "video_core/engines/maxwell_3d.h" |
| 10 | #include "video_core/memory_manager.h" | 14 | #include "video_core/memory_manager.h" |
| 11 | #include "video_core/renderer_opengl/gl_rasterizer.h" | 15 | #include "video_core/renderer_opengl/gl_rasterizer.h" |
| @@ -166,7 +170,8 @@ GLShader::ProgramResult CreateProgram(const Device& device, Maxwell::ShaderProgr | |||
| 166 | CachedProgram SpecializeShader(const std::string& code, const GLShader::ShaderEntries& entries, | 170 | CachedProgram SpecializeShader(const std::string& code, const GLShader::ShaderEntries& entries, |
| 167 | Maxwell::ShaderProgram program_type, BaseBindings base_bindings, | 171 | Maxwell::ShaderProgram program_type, BaseBindings base_bindings, |
| 168 | GLenum primitive_mode, bool hint_retrievable = false) { | 172 | GLenum primitive_mode, bool hint_retrievable = false) { |
| 169 | std::string source = "#version 430 core\n"; | 173 | std::string source = "#version 430 core\n" |
| 174 | "#extension GL_ARB_separate_shader_objects : enable\n\n"; | ||
| 170 | source += fmt::format("#define EMULATION_UBO_BINDING {}\n", base_bindings.cbuf++); | 175 | source += fmt::format("#define EMULATION_UBO_BINDING {}\n", base_bindings.cbuf++); |
| 171 | 176 | ||
| 172 | for (const auto& cbuf : entries.const_buffers) { | 177 | for (const auto& cbuf : entries.const_buffers) { |
| @@ -344,8 +349,8 @@ ShaderDiskCacheUsage CachedShader::GetUsage(GLenum primitive_mode, | |||
| 344 | } | 349 | } |
| 345 | 350 | ||
| 346 | ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system, | 351 | ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system, |
| 347 | const Device& device) | 352 | Core::Frontend::EmuWindow& emu_window, const Device& device) |
| 348 | : RasterizerCache{rasterizer}, disk_cache{system}, device{device} {} | 353 | : RasterizerCache{rasterizer}, emu_window{emu_window}, device{device}, disk_cache{system} {} |
| 349 | 354 | ||
| 350 | void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading, | 355 | void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading, |
| 351 | const VideoCore::DiskResourceLoadCallback& callback) { | 356 | const VideoCore::DiskResourceLoadCallback& callback) { |
| @@ -353,60 +358,115 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading, | |||
| 353 | if (!transferable) { | 358 | if (!transferable) { |
| 354 | return; | 359 | return; |
| 355 | } | 360 | } |
| 356 | const auto [raws, usages] = *transferable; | 361 | const auto [raws, shader_usages] = *transferable; |
| 357 | 362 | ||
| 358 | auto [decompiled, dumps] = disk_cache.LoadPrecompiled(); | 363 | auto [decompiled, dumps] = disk_cache.LoadPrecompiled(); |
| 359 | 364 | ||
| 360 | const auto supported_formats{GetSupportedFormats()}; | 365 | const auto supported_formats{GetSupportedFormats()}; |
| 361 | const auto unspecialized{ | 366 | const auto unspecialized_shaders{ |
| 362 | GenerateUnspecializedShaders(stop_loading, callback, raws, decompiled)}; | 367 | GenerateUnspecializedShaders(stop_loading, callback, raws, decompiled)}; |
| 363 | if (stop_loading) | 368 | if (stop_loading) { |
| 364 | return; | 369 | return; |
| 370 | } | ||
| 365 | 371 | ||
| 366 | // Build shaders | 372 | // Track if precompiled cache was altered during loading to know if we have to serialize the |
| 367 | if (callback) | 373 | // virtual precompiled cache file back to the hard drive |
| 368 | callback(VideoCore::LoadCallbackStage::Build, 0, usages.size()); | 374 | bool precompiled_cache_altered = false; |
| 369 | for (std::size_t i = 0; i < usages.size(); ++i) { | ||
| 370 | if (stop_loading) | ||
| 371 | return; | ||
| 372 | 375 | ||
| 373 | const auto& usage{usages[i]}; | 376 | // Inform the frontend about shader build initialization |
| 374 | LOG_INFO(Render_OpenGL, "Building shader {:016x} ({} of {})", usage.unique_identifier, | 377 | if (callback) { |
| 375 | i + 1, usages.size()); | 378 | callback(VideoCore::LoadCallbackStage::Build, 0, shader_usages.size()); |
| 379 | } | ||
| 376 | 380 | ||
| 377 | const auto& unspec{unspecialized.at(usage.unique_identifier)}; | 381 | std::mutex mutex; |
| 378 | const auto dump_it = dumps.find(usage); | 382 | std::size_t built_shaders = 0; // It doesn't have be atomic since it's used behind a mutex |
| 383 | std::atomic_bool compilation_failed = false; | ||
| 379 | 384 | ||
| 380 | CachedProgram shader; | 385 | const auto Worker = [&](Core::Frontend::GraphicsContext* context, std::size_t begin, |
| 381 | if (dump_it != dumps.end()) { | 386 | std::size_t end, const std::vector<ShaderDiskCacheUsage>& shader_usages, |
| 382 | // If the shader is dumped, attempt to load it with | 387 | const ShaderDumpsMap& dumps) { |
| 383 | shader = GeneratePrecompiledProgram(dump_it->second, supported_formats); | 388 | context->MakeCurrent(); |
| 389 | SCOPE_EXIT({ return context->DoneCurrent(); }); | ||
| 390 | |||
| 391 | for (std::size_t i = begin; i < end; ++i) { | ||
| 392 | if (stop_loading || compilation_failed) { | ||
| 393 | return; | ||
| 394 | } | ||
| 395 | const auto& usage{shader_usages[i]}; | ||
| 396 | LOG_INFO(Render_OpenGL, "Building shader {:016x} (index {} of {})", | ||
| 397 | usage.unique_identifier, i, shader_usages.size()); | ||
| 398 | |||
| 399 | const auto& unspecialized{unspecialized_shaders.at(usage.unique_identifier)}; | ||
| 400 | const auto dump{dumps.find(usage)}; | ||
| 401 | |||
| 402 | CachedProgram shader; | ||
| 403 | if (dump != dumps.end()) { | ||
| 404 | // If the shader is dumped, attempt to load it with | ||
| 405 | shader = GeneratePrecompiledProgram(dump->second, supported_formats); | ||
| 406 | if (!shader) { | ||
| 407 | compilation_failed = true; | ||
| 408 | return; | ||
| 409 | } | ||
| 410 | } | ||
| 384 | if (!shader) { | 411 | if (!shader) { |
| 385 | // Invalidate the precompiled cache if a shader dumped shader was rejected | 412 | shader = SpecializeShader(unspecialized.code, unspecialized.entries, |
| 386 | disk_cache.InvalidatePrecompiled(); | 413 | unspecialized.program_type, usage.bindings, |
| 387 | dumps.clear(); | 414 | usage.primitive, true); |
| 388 | } | 415 | } |
| 416 | |||
| 417 | std::scoped_lock lock(mutex); | ||
| 418 | if (callback) { | ||
| 419 | callback(VideoCore::LoadCallbackStage::Build, ++built_shaders, | ||
| 420 | shader_usages.size()); | ||
| 421 | } | ||
| 422 | |||
| 423 | precompiled_programs.emplace(usage, std::move(shader)); | ||
| 389 | } | 424 | } |
| 390 | if (!shader) { | 425 | }; |
| 391 | shader = SpecializeShader(unspec.code, unspec.entries, unspec.program_type, | 426 | |
| 392 | usage.bindings, usage.primitive, true); | 427 | const auto num_workers{static_cast<std::size_t>(std::thread::hardware_concurrency() + 1)}; |
| 393 | } | 428 | const std::size_t bucket_size{shader_usages.size() / num_workers}; |
| 394 | precompiled_programs.insert({usage, std::move(shader)}); | 429 | std::vector<std::unique_ptr<Core::Frontend::GraphicsContext>> contexts(num_workers); |
| 430 | std::vector<std::thread> threads(num_workers); | ||
| 431 | for (std::size_t i = 0; i < num_workers; ++i) { | ||
| 432 | const bool is_last_worker = i + 1 == num_workers; | ||
| 433 | const std::size_t start{bucket_size * i}; | ||
| 434 | const std::size_t end{is_last_worker ? shader_usages.size() : start + bucket_size}; | ||
| 435 | |||
| 436 | // On some platforms the shared context has to be created from the GUI thread | ||
| 437 | contexts[i] = emu_window.CreateSharedContext(); | ||
| 438 | threads[i] = std::thread(Worker, contexts[i].get(), start, end, shader_usages, dumps); | ||
| 439 | } | ||
| 440 | for (auto& thread : threads) { | ||
| 441 | thread.join(); | ||
| 442 | } | ||
| 395 | 443 | ||
| 396 | if (callback) | 444 | if (compilation_failed) { |
| 397 | callback(VideoCore::LoadCallbackStage::Build, i + 1, usages.size()); | 445 | // Invalidate the precompiled cache if a shader dumped shader was rejected |
| 446 | disk_cache.InvalidatePrecompiled(); | ||
| 447 | dumps.clear(); | ||
| 448 | precompiled_cache_altered = true; | ||
| 449 | return; | ||
| 450 | } | ||
| 451 | if (stop_loading) { | ||
| 452 | return; | ||
| 398 | } | 453 | } |
| 399 | 454 | ||
| 400 | // TODO(Rodrigo): Do state tracking for transferable shaders and do a dummy draw before | 455 | // TODO(Rodrigo): Do state tracking for transferable shaders and do a dummy draw before |
| 401 | // precompiling them | 456 | // precompiling them |
| 402 | 457 | ||
| 403 | for (std::size_t i = 0; i < usages.size(); ++i) { | 458 | for (std::size_t i = 0; i < shader_usages.size(); ++i) { |
| 404 | const auto& usage{usages[i]}; | 459 | const auto& usage{shader_usages[i]}; |
| 405 | if (dumps.find(usage) == dumps.end()) { | 460 | if (dumps.find(usage) == dumps.end()) { |
| 406 | const auto& program = precompiled_programs.at(usage); | 461 | const auto& program{precompiled_programs.at(usage)}; |
| 407 | disk_cache.SaveDump(usage, program->handle); | 462 | disk_cache.SaveDump(usage, program->handle); |
| 463 | precompiled_cache_altered = true; | ||
| 408 | } | 464 | } |
| 409 | } | 465 | } |
| 466 | |||
| 467 | if (precompiled_cache_altered) { | ||
| 468 | disk_cache.SaveVirtualPrecompiledFile(); | ||
| 469 | } | ||
| 410 | } | 470 | } |
| 411 | 471 | ||
| 412 | CachedProgram ShaderCacheOpenGL::GeneratePrecompiledProgram( | 472 | CachedProgram ShaderCacheOpenGL::GeneratePrecompiledProgram( |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h index a332087f8..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 @@ | |||
| 22 | 22 | ||
| 23 | namespace Core { | 23 | namespace Core { |
| 24 | class System; | 24 | class System; |
| 25 | } // namespace Core | 25 | } |
| 26 | |||
| 27 | namespace Core::Frontend { | ||
| 28 | class EmuWindow; | ||
| 29 | } | ||
| 26 | 30 | ||
| 27 | namespace OpenGL { | 31 | namespace OpenGL { |
| 28 | 32 | ||
| @@ -57,9 +61,6 @@ public: | |||
| 57 | return shader_length; | 61 | return shader_length; |
| 58 | } | 62 | } |
| 59 | 63 | ||
| 60 | // We do not have to flush this cache as things in it are never modified by us. | ||
| 61 | void Flush() override {} | ||
| 62 | |||
| 63 | /// Gets the shader entries for the shader | 64 | /// Gets the shader entries for the shader |
| 64 | const GLShader::ShaderEntries& GetShaderEntries() const { | 65 | const GLShader::ShaderEntries& GetShaderEntries() const { |
| 65 | return entries; | 66 | return entries; |
| @@ -114,7 +115,7 @@ private: | |||
| 114 | class ShaderCacheOpenGL final : public RasterizerCache<Shader> { | 115 | class ShaderCacheOpenGL final : public RasterizerCache<Shader> { |
| 115 | public: | 116 | public: |
| 116 | explicit ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system, | 117 | explicit ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system, |
| 117 | const Device& device); | 118 | Core::Frontend::EmuWindow& emu_window, const Device& device); |
| 118 | 119 | ||
| 119 | /// Loads disk cache for the current game | 120 | /// Loads disk cache for the current game |
| 120 | void LoadDiskCache(const std::atomic_bool& stop_loading, | 121 | void LoadDiskCache(const std::atomic_bool& stop_loading, |
| @@ -123,6 +124,10 @@ public: | |||
| 123 | /// Gets the current specified shader stage program | 124 | /// Gets the current specified shader stage program |
| 124 | Shader GetStageProgram(Maxwell::ShaderProgram program); | 125 | Shader GetStageProgram(Maxwell::ShaderProgram program); |
| 125 | 126 | ||
| 127 | protected: | ||
| 128 | // We do not have to flush this cache as things in it are never modified by us. | ||
| 129 | void FlushObjectInner(const Shader& object) override {} | ||
| 130 | |||
| 126 | private: | 131 | private: |
| 127 | std::unordered_map<u64, UnspecializedShader> GenerateUnspecializedShaders( | 132 | std::unordered_map<u64, UnspecializedShader> GenerateUnspecializedShaders( |
| 128 | const std::atomic_bool& stop_loading, const VideoCore::DiskResourceLoadCallback& callback, | 133 | const std::atomic_bool& stop_loading, const VideoCore::DiskResourceLoadCallback& callback, |
| @@ -132,13 +137,13 @@ private: | |||
| 132 | CachedProgram GeneratePrecompiledProgram(const ShaderDiskCacheDump& dump, | 137 | CachedProgram GeneratePrecompiledProgram(const ShaderDiskCacheDump& dump, |
| 133 | const std::set<GLenum>& supported_formats); | 138 | const std::set<GLenum>& supported_formats); |
| 134 | 139 | ||
| 140 | Core::Frontend::EmuWindow& emu_window; | ||
| 135 | const Device& device; | 141 | const Device& device; |
| 136 | |||
| 137 | std::array<Shader, Maxwell::MaxShaderProgram> last_shaders; | ||
| 138 | |||
| 139 | ShaderDiskCacheOpenGL disk_cache; | 142 | ShaderDiskCacheOpenGL disk_cache; |
| 143 | |||
| 140 | PrecompiledShaders precompiled_shaders; | 144 | PrecompiledShaders precompiled_shaders; |
| 141 | PrecompiledPrograms precompiled_programs; | 145 | PrecompiledPrograms precompiled_programs; |
| 146 | std::array<Shader, Maxwell::MaxShaderProgram> last_shaders; | ||
| 142 | }; | 147 | }; |
| 143 | 148 | ||
| 144 | } // namespace OpenGL | 149 | } // 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 ef1a1995f..e9f8d40db 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp | |||
| @@ -31,6 +31,8 @@ using Tegra::Shader::IpaInterpMode; | |||
| 31 | using Tegra::Shader::IpaMode; | 31 | using Tegra::Shader::IpaMode; |
| 32 | using Tegra::Shader::IpaSampleMode; | 32 | using Tegra::Shader::IpaSampleMode; |
| 33 | using Tegra::Shader::Register; | 33 | using Tegra::Shader::Register; |
| 34 | |||
| 35 | using namespace std::string_literals; | ||
| 34 | using namespace VideoCommon::Shader; | 36 | using namespace VideoCommon::Shader; |
| 35 | 37 | ||
| 36 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; | 38 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; |
| @@ -57,15 +59,14 @@ public: | |||
| 57 | shader_source += text; | 59 | shader_source += text; |
| 58 | } | 60 | } |
| 59 | 61 | ||
| 60 | void AddLine(std::string_view text) { | 62 | // Forwards all arguments directly to libfmt. |
| 61 | AddExpression(text); | 63 | // Note that all formatting requirements for fmt must be |
| 62 | AddNewLine(); | 64 | // obeyed when using this function. (e.g. {{ must be used |
| 63 | } | 65 | // printing the character '{' is desirable. Ditto for }} and '}', |
| 64 | 66 | // etc). | |
| 65 | void AddLine(char character) { | 67 | template <typename... Args> |
| 66 | DEBUG_ASSERT(scope >= 0); | 68 | void AddLine(std::string_view text, Args&&... args) { |
| 67 | AppendIndentation(); | 69 | AddExpression(fmt::format(text, std::forward<Args>(args)...)); |
| 68 | shader_source += character; | ||
| 69 | AddNewLine(); | 70 | AddNewLine(); |
| 70 | } | 71 | } |
| 71 | 72 | ||
| @@ -75,9 +76,7 @@ public: | |||
| 75 | } | 76 | } |
| 76 | 77 | ||
| 77 | std::string GenerateTemporary() { | 78 | std::string GenerateTemporary() { |
| 78 | std::string temporary = "tmp"; | 79 | return fmt::format("tmp{}", temporary_index++); |
| 79 | temporary += std::to_string(temporary_index++); | ||
| 80 | return temporary; | ||
| 81 | } | 80 | } |
| 82 | 81 | ||
| 83 | std::string GetResult() { | 82 | std::string GetResult() { |
| @@ -96,11 +95,9 @@ private: | |||
| 96 | }; | 95 | }; |
| 97 | 96 | ||
| 98 | /// Generates code to use for a swizzle operation. | 97 | /// Generates code to use for a swizzle operation. |
| 99 | std::string GetSwizzle(u32 elem) { | 98 | constexpr const char* GetSwizzle(u32 element) { |
| 100 | ASSERT(elem <= 3); | 99 | constexpr std::array<const char*, 4> swizzle = {".x", ".y", ".z", ".w"}; |
| 101 | std::string swizzle = "."; | 100 | return swizzle.at(element); |
| 102 | swizzle += "xyzw"[elem]; | ||
| 103 | return swizzle; | ||
| 104 | } | 101 | } |
| 105 | 102 | ||
| 106 | /// Translate topology | 103 | /// Translate topology |
| @@ -134,6 +131,19 @@ bool IsPrecise(Node node) { | |||
| 134 | return false; | 131 | return false; |
| 135 | } | 132 | } |
| 136 | 133 | ||
| 134 | constexpr bool IsGenericAttribute(Attribute::Index index) { | ||
| 135 | return index >= Attribute::Index::Attribute_0 && index <= Attribute::Index::Attribute_31; | ||
| 136 | } | ||
| 137 | |||
| 138 | constexpr Attribute::Index ToGenericAttribute(u32 value) { | ||
| 139 | return static_cast<Attribute::Index>(value + static_cast<u32>(Attribute::Index::Attribute_0)); | ||
| 140 | } | ||
| 141 | |||
| 142 | u32 GetGenericAttributeIndex(Attribute::Index index) { | ||
| 143 | ASSERT(IsGenericAttribute(index)); | ||
| 144 | return static_cast<u32>(index) - static_cast<u32>(Attribute::Index::Attribute_0); | ||
| 145 | } | ||
| 146 | |||
| 137 | class GLSLDecompiler final { | 147 | class GLSLDecompiler final { |
| 138 | public: | 148 | public: |
| 139 | explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, ShaderStage stage, | 149 | explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, ShaderStage stage, |
| @@ -152,42 +162,43 @@ public: | |||
| 152 | DeclareConstantBuffers(); | 162 | DeclareConstantBuffers(); |
| 153 | DeclareGlobalMemory(); | 163 | DeclareGlobalMemory(); |
| 154 | DeclareSamplers(); | 164 | DeclareSamplers(); |
| 165 | DeclarePhysicalAttributeReader(); | ||
| 155 | 166 | ||
| 156 | code.AddLine("void execute_" + suffix + "() {"); | 167 | code.AddLine("void execute_{}() {{", suffix); |
| 157 | ++code.scope; | 168 | ++code.scope; |
| 158 | 169 | ||
| 159 | // VM's program counter | 170 | // VM's program counter |
| 160 | const auto first_address = ir.GetBasicBlocks().begin()->first; | 171 | const auto first_address = ir.GetBasicBlocks().begin()->first; |
| 161 | code.AddLine("uint jmp_to = " + std::to_string(first_address) + "u;"); | 172 | code.AddLine("uint jmp_to = {}u;", first_address); |
| 162 | 173 | ||
| 163 | // TODO(Subv): Figure out the actual depth of the flow stack, for now it seems | 174 | // TODO(Subv): Figure out the actual depth of the flow stack, for now it seems |
| 164 | // unlikely that shaders will use 20 nested SSYs and PBKs. | 175 | // unlikely that shaders will use 20 nested SSYs and PBKs. |
| 165 | constexpr u32 FLOW_STACK_SIZE = 20; | 176 | constexpr u32 FLOW_STACK_SIZE = 20; |
| 166 | code.AddLine(fmt::format("uint flow_stack[{}];", FLOW_STACK_SIZE)); | 177 | code.AddLine("uint flow_stack[{}];", FLOW_STACK_SIZE); |
| 167 | code.AddLine("uint flow_stack_top = 0u;"); | 178 | code.AddLine("uint flow_stack_top = 0u;"); |
| 168 | 179 | ||
| 169 | code.AddLine("while (true) {"); | 180 | code.AddLine("while (true) {{"); |
| 170 | ++code.scope; | 181 | ++code.scope; |
| 171 | 182 | ||
| 172 | code.AddLine("switch (jmp_to) {"); | 183 | code.AddLine("switch (jmp_to) {{"); |
| 173 | 184 | ||
| 174 | for (const auto& pair : ir.GetBasicBlocks()) { | 185 | for (const auto& pair : ir.GetBasicBlocks()) { |
| 175 | const auto [address, bb] = pair; | 186 | const auto [address, bb] = pair; |
| 176 | code.AddLine(fmt::format("case 0x{:x}u: {{", address)); | 187 | code.AddLine("case 0x{:x}u: {{", address); |
| 177 | ++code.scope; | 188 | ++code.scope; |
| 178 | 189 | ||
| 179 | VisitBlock(bb); | 190 | VisitBlock(bb); |
| 180 | 191 | ||
| 181 | --code.scope; | 192 | --code.scope; |
| 182 | code.AddLine('}'); | 193 | code.AddLine("}}"); |
| 183 | } | 194 | } |
| 184 | 195 | ||
| 185 | code.AddLine("default: return;"); | 196 | code.AddLine("default: return;"); |
| 186 | code.AddLine('}'); | 197 | code.AddLine("}}"); |
| 187 | 198 | ||
| 188 | for (std::size_t i = 0; i < 2; ++i) { | 199 | for (std::size_t i = 0; i < 2; ++i) { |
| 189 | --code.scope; | 200 | --code.scope; |
| 190 | code.AddLine('}'); | 201 | code.AddLine("}}"); |
| 191 | } | 202 | } |
| 192 | } | 203 | } |
| 193 | 204 | ||
| @@ -227,12 +238,13 @@ private: | |||
| 227 | } | 238 | } |
| 228 | 239 | ||
| 229 | void DeclareGeometry() { | 240 | void DeclareGeometry() { |
| 230 | if (stage != ShaderStage::Geometry) | 241 | if (stage != ShaderStage::Geometry) { |
| 231 | return; | 242 | return; |
| 243 | } | ||
| 232 | 244 | ||
| 233 | const auto topology = GetTopologyName(header.common3.output_topology); | 245 | const auto topology = GetTopologyName(header.common3.output_topology); |
| 234 | const auto max_vertices = std::to_string(header.common4.max_output_vertices); | 246 | const auto max_vertices = header.common4.max_output_vertices.Value(); |
| 235 | code.AddLine("layout (" + topology + ", max_vertices = " + max_vertices + ") out;"); | 247 | code.AddLine("layout ({}, max_vertices = {}) out;", topology, max_vertices); |
| 236 | code.AddNewLine(); | 248 | code.AddNewLine(); |
| 237 | 249 | ||
| 238 | DeclareVertexRedeclarations(); | 250 | DeclareVertexRedeclarations(); |
| @@ -241,7 +253,7 @@ private: | |||
| 241 | void DeclareVertexRedeclarations() { | 253 | void DeclareVertexRedeclarations() { |
| 242 | bool clip_distances_declared = false; | 254 | bool clip_distances_declared = false; |
| 243 | 255 | ||
| 244 | code.AddLine("out gl_PerVertex {"); | 256 | code.AddLine("out gl_PerVertex {{"); |
| 245 | ++code.scope; | 257 | ++code.scope; |
| 246 | 258 | ||
| 247 | code.AddLine("vec4 gl_Position;"); | 259 | code.AddLine("vec4 gl_Position;"); |
| @@ -257,122 +269,143 @@ private: | |||
| 257 | } | 269 | } |
| 258 | 270 | ||
| 259 | --code.scope; | 271 | --code.scope; |
| 260 | code.AddLine("};"); | 272 | code.AddLine("}};"); |
| 261 | code.AddNewLine(); | 273 | code.AddNewLine(); |
| 262 | } | 274 | } |
| 263 | 275 | ||
| 264 | void DeclareRegisters() { | 276 | void DeclareRegisters() { |
| 265 | const auto& registers = ir.GetRegisters(); | 277 | const auto& registers = ir.GetRegisters(); |
| 266 | for (const u32 gpr : registers) { | 278 | for (const u32 gpr : registers) { |
| 267 | code.AddLine("float " + GetRegister(gpr) + " = 0;"); | 279 | code.AddLine("float {} = 0;", GetRegister(gpr)); |
| 268 | } | 280 | } |
| 269 | if (!registers.empty()) | 281 | if (!registers.empty()) { |
| 270 | code.AddNewLine(); | 282 | code.AddNewLine(); |
| 283 | } | ||
| 271 | } | 284 | } |
| 272 | 285 | ||
| 273 | void DeclarePredicates() { | 286 | void DeclarePredicates() { |
| 274 | const auto& predicates = ir.GetPredicates(); | 287 | const auto& predicates = ir.GetPredicates(); |
| 275 | for (const auto pred : predicates) { | 288 | for (const auto pred : predicates) { |
| 276 | code.AddLine("bool " + GetPredicate(pred) + " = false;"); | 289 | code.AddLine("bool {} = false;", GetPredicate(pred)); |
| 277 | } | 290 | } |
| 278 | if (!predicates.empty()) | 291 | if (!predicates.empty()) { |
| 279 | code.AddNewLine(); | 292 | code.AddNewLine(); |
| 293 | } | ||
| 280 | } | 294 | } |
| 281 | 295 | ||
| 282 | void DeclareLocalMemory() { | 296 | void DeclareLocalMemory() { |
| 283 | if (const u64 local_memory_size = header.GetLocalMemorySize(); local_memory_size > 0) { | 297 | if (const u64 local_memory_size = header.GetLocalMemorySize(); local_memory_size > 0) { |
| 284 | const auto element_count = Common::AlignUp(local_memory_size, 4) / 4; | 298 | const auto element_count = Common::AlignUp(local_memory_size, 4) / 4; |
| 285 | code.AddLine("float " + GetLocalMemory() + '[' + std::to_string(element_count) + "];"); | 299 | code.AddLine("float {}[{}];", GetLocalMemory(), element_count); |
| 286 | code.AddNewLine(); | 300 | code.AddNewLine(); |
| 287 | } | 301 | } |
| 288 | } | 302 | } |
| 289 | 303 | ||
| 290 | void DeclareInternalFlags() { | 304 | void DeclareInternalFlags() { |
| 291 | for (u32 flag = 0; flag < static_cast<u32>(InternalFlag::Amount); flag++) { | 305 | for (u32 flag = 0; flag < static_cast<u32>(InternalFlag::Amount); flag++) { |
| 292 | const InternalFlag flag_code = static_cast<InternalFlag>(flag); | 306 | const auto flag_code = static_cast<InternalFlag>(flag); |
| 293 | code.AddLine("bool " + GetInternalFlag(flag_code) + " = false;"); | 307 | code.AddLine("bool {} = false;", GetInternalFlag(flag_code)); |
| 294 | } | 308 | } |
| 295 | code.AddNewLine(); | 309 | code.AddNewLine(); |
| 296 | } | 310 | } |
| 297 | 311 | ||
| 298 | std::string GetInputFlags(AttributeUse attribute) { | 312 | std::string GetInputFlags(AttributeUse attribute) { |
| 299 | std::string out; | ||
| 300 | |||
| 301 | switch (attribute) { | 313 | switch (attribute) { |
| 302 | case AttributeUse::Constant: | ||
| 303 | out += "flat "; | ||
| 304 | break; | ||
| 305 | case AttributeUse::ScreenLinear: | ||
| 306 | out += "noperspective "; | ||
| 307 | break; | ||
| 308 | case AttributeUse::Perspective: | 314 | case AttributeUse::Perspective: |
| 309 | // Default, Smooth | 315 | // Default, Smooth |
| 310 | break; | 316 | return {}; |
| 317 | case AttributeUse::Constant: | ||
| 318 | return "flat "; | ||
| 319 | case AttributeUse::ScreenLinear: | ||
| 320 | return "noperspective "; | ||
| 311 | default: | 321 | default: |
| 312 | LOG_CRITICAL(HW_GPU, "Unused attribute being fetched"); | 322 | case AttributeUse::Unused: |
| 313 | UNREACHABLE(); | 323 | UNREACHABLE_MSG("Unused attribute being fetched"); |
| 324 | return {}; | ||
| 325 | UNIMPLEMENTED_MSG("Unknown attribute usage index={}", static_cast<u32>(attribute)); | ||
| 326 | return {}; | ||
| 314 | } | 327 | } |
| 315 | return out; | ||
| 316 | } | 328 | } |
| 317 | 329 | ||
| 318 | void DeclareInputAttributes() { | 330 | void DeclareInputAttributes() { |
| 319 | const auto& attributes = ir.GetInputAttributes(); | 331 | if (ir.HasPhysicalAttributes()) { |
| 320 | for (const auto element : attributes) { | 332 | const u32 num_inputs{GetNumPhysicalInputAttributes()}; |
| 321 | const Attribute::Index index = element.first; | 333 | for (u32 i = 0; i < num_inputs; ++i) { |
| 322 | if (index < Attribute::Index::Attribute_0 || index > Attribute::Index::Attribute_31) { | 334 | DeclareInputAttribute(ToGenericAttribute(i), true); |
| 323 | // Skip when it's not a generic attribute | ||
| 324 | continue; | ||
| 325 | } | 335 | } |
| 336 | code.AddNewLine(); | ||
| 337 | return; | ||
| 338 | } | ||
| 326 | 339 | ||
| 327 | // TODO(bunnei): Use proper number of elements for these | 340 | const auto& attributes = ir.GetInputAttributes(); |
| 328 | u32 idx = static_cast<u32>(index) - static_cast<u32>(Attribute::Index::Attribute_0); | 341 | for (const auto index : attributes) { |
| 329 | if (stage != ShaderStage::Vertex) { | 342 | if (IsGenericAttribute(index)) { |
| 330 | // If inputs are varyings, add an offset | 343 | DeclareInputAttribute(index, false); |
| 331 | idx += GENERIC_VARYING_START_LOCATION; | ||
| 332 | } | 344 | } |
| 345 | } | ||
| 346 | if (!attributes.empty()) { | ||
| 347 | code.AddNewLine(); | ||
| 348 | } | ||
| 349 | } | ||
| 333 | 350 | ||
| 334 | std::string attr = GetInputAttribute(index); | 351 | void DeclareInputAttribute(Attribute::Index index, bool skip_unused) { |
| 335 | if (stage == ShaderStage::Geometry) { | 352 | const u32 generic_index{GetGenericAttributeIndex(index)}; |
| 336 | attr = "gs_" + attr + "[]"; | 353 | |
| 337 | } | 354 | std::string name{GetInputAttribute(index)}; |
| 338 | std::string suffix; | 355 | if (stage == ShaderStage::Geometry) { |
| 339 | if (stage == ShaderStage::Fragment) { | 356 | name = "gs_" + name + "[]"; |
| 340 | const auto input_mode = | 357 | } |
| 341 | header.ps.GetAttributeUse(idx - GENERIC_VARYING_START_LOCATION); | 358 | |
| 342 | suffix = GetInputFlags(input_mode); | 359 | std::string suffix; |
| 360 | if (stage == ShaderStage::Fragment) { | ||
| 361 | const auto input_mode{header.ps.GetAttributeUse(generic_index)}; | ||
| 362 | if (skip_unused && input_mode == AttributeUse::Unused) { | ||
| 363 | return; | ||
| 343 | } | 364 | } |
| 344 | code.AddLine("layout (location = " + std::to_string(idx) + ") " + suffix + "in vec4 " + | 365 | suffix = GetInputFlags(input_mode); |
| 345 | attr + ';'); | ||
| 346 | } | 366 | } |
| 347 | if (!attributes.empty()) | 367 | |
| 348 | code.AddNewLine(); | 368 | u32 location = generic_index; |
| 369 | if (stage != ShaderStage::Vertex) { | ||
| 370 | // If inputs are varyings, add an offset | ||
| 371 | location += GENERIC_VARYING_START_LOCATION; | ||
| 372 | } | ||
| 373 | |||
| 374 | code.AddLine("layout (location = {}) {} in vec4 {};", location, suffix, name); | ||
| 349 | } | 375 | } |
| 350 | 376 | ||
| 351 | void DeclareOutputAttributes() { | 377 | void DeclareOutputAttributes() { |
| 378 | if (ir.HasPhysicalAttributes() && stage != ShaderStage::Fragment) { | ||
| 379 | for (u32 i = 0; i < GetNumPhysicalVaryings(); ++i) { | ||
| 380 | DeclareOutputAttribute(ToGenericAttribute(i)); | ||
| 381 | } | ||
| 382 | code.AddNewLine(); | ||
| 383 | return; | ||
| 384 | } | ||
| 385 | |||
| 352 | const auto& attributes = ir.GetOutputAttributes(); | 386 | const auto& attributes = ir.GetOutputAttributes(); |
| 353 | for (const auto index : attributes) { | 387 | for (const auto index : attributes) { |
| 354 | if (index < Attribute::Index::Attribute_0 || index > Attribute::Index::Attribute_31) { | 388 | if (IsGenericAttribute(index)) { |
| 355 | // Skip when it's not a generic attribute | 389 | DeclareOutputAttribute(index); |
| 356 | continue; | ||
| 357 | } | 390 | } |
| 358 | // TODO(bunnei): Use proper number of elements for these | 391 | } |
| 359 | const auto idx = static_cast<u32>(index) - | 392 | if (!attributes.empty()) { |
| 360 | static_cast<u32>(Attribute::Index::Attribute_0) + | ||
| 361 | GENERIC_VARYING_START_LOCATION; | ||
| 362 | code.AddLine("layout (location = " + std::to_string(idx) + ") out vec4 " + | ||
| 363 | GetOutputAttribute(index) + ';'); | ||
| 364 | } | ||
| 365 | if (!attributes.empty()) | ||
| 366 | code.AddNewLine(); | 393 | code.AddNewLine(); |
| 394 | } | ||
| 395 | } | ||
| 396 | |||
| 397 | void DeclareOutputAttribute(Attribute::Index index) { | ||
| 398 | const u32 location{GetGenericAttributeIndex(index) + GENERIC_VARYING_START_LOCATION}; | ||
| 399 | code.AddLine("layout (location = {}) out vec4 {};", location, GetOutputAttribute(index)); | ||
| 367 | } | 400 | } |
| 368 | 401 | ||
| 369 | void DeclareConstantBuffers() { | 402 | void DeclareConstantBuffers() { |
| 370 | for (const auto& entry : ir.GetConstantBuffers()) { | 403 | for (const auto& entry : ir.GetConstantBuffers()) { |
| 371 | const auto [index, size] = entry; | 404 | const auto [index, size] = entry; |
| 372 | code.AddLine("layout (std140, binding = CBUF_BINDING_" + std::to_string(index) + | 405 | code.AddLine("layout (std140, binding = CBUF_BINDING_{}) uniform {} {{", index, |
| 373 | ") uniform " + GetConstBufferBlock(index) + " {"); | 406 | GetConstBufferBlock(index)); |
| 374 | code.AddLine(" vec4 " + GetConstBuffer(index) + "[MAX_CONSTBUFFER_ELEMENTS];"); | 407 | code.AddLine(" vec4 {}[MAX_CONSTBUFFER_ELEMENTS];", GetConstBuffer(index)); |
| 375 | code.AddLine("};"); | 408 | code.AddLine("}};"); |
| 376 | code.AddNewLine(); | 409 | code.AddNewLine(); |
| 377 | } | 410 | } |
| 378 | } | 411 | } |
| @@ -384,17 +417,16 @@ private: | |||
| 384 | // Since we don't know how the shader will use the shader, hint the driver to disable as | 417 | // Since we don't know how the shader will use the shader, hint the driver to disable as |
| 385 | // much optimizations as possible | 418 | // much optimizations as possible |
| 386 | std::string qualifier = "coherent volatile"; | 419 | std::string qualifier = "coherent volatile"; |
| 387 | if (usage.is_read && !usage.is_written) | 420 | if (usage.is_read && !usage.is_written) { |
| 388 | qualifier += " readonly"; | 421 | qualifier += " readonly"; |
| 389 | else if (usage.is_written && !usage.is_read) | 422 | } else if (usage.is_written && !usage.is_read) { |
| 390 | qualifier += " writeonly"; | 423 | qualifier += " writeonly"; |
| 424 | } | ||
| 391 | 425 | ||
| 392 | const std::string binding = | 426 | code.AddLine("layout (std430, binding = GMEM_BINDING_{}_{}) {} buffer {} {{", |
| 393 | fmt::format("GMEM_BINDING_{}_{}", base.cbuf_index, base.cbuf_offset); | 427 | base.cbuf_index, base.cbuf_offset, qualifier, GetGlobalMemoryBlock(base)); |
| 394 | code.AddLine("layout (std430, binding = " + binding + ") " + qualifier + " buffer " + | 428 | code.AddLine(" float {}[];", GetGlobalMemory(base)); |
| 395 | GetGlobalMemoryBlock(base) + " {"); | 429 | code.AddLine("}};"); |
| 396 | code.AddLine(" float " + GetGlobalMemory(base) + "[];"); | ||
| 397 | code.AddLine("};"); | ||
| 398 | code.AddNewLine(); | 430 | code.AddNewLine(); |
| 399 | } | 431 | } |
| 400 | } | 432 | } |
| @@ -402,7 +434,7 @@ private: | |||
| 402 | void DeclareSamplers() { | 434 | void DeclareSamplers() { |
| 403 | const auto& samplers = ir.GetSamplers(); | 435 | const auto& samplers = ir.GetSamplers(); |
| 404 | for (const auto& sampler : samplers) { | 436 | for (const auto& sampler : samplers) { |
| 405 | std::string sampler_type = [&]() { | 437 | std::string sampler_type = [&sampler] { |
| 406 | switch (sampler.GetType()) { | 438 | switch (sampler.GetType()) { |
| 407 | case Tegra::Shader::TextureType::Texture1D: | 439 | case Tegra::Shader::TextureType::Texture1D: |
| 408 | return "sampler1D"; | 440 | return "sampler1D"; |
| @@ -417,16 +449,52 @@ private: | |||
| 417 | return "sampler2D"; | 449 | return "sampler2D"; |
| 418 | } | 450 | } |
| 419 | }(); | 451 | }(); |
| 420 | if (sampler.IsArray()) | 452 | if (sampler.IsArray()) { |
| 421 | sampler_type += "Array"; | 453 | sampler_type += "Array"; |
| 422 | if (sampler.IsShadow()) | 454 | } |
| 455 | if (sampler.IsShadow()) { | ||
| 423 | sampler_type += "Shadow"; | 456 | sampler_type += "Shadow"; |
| 457 | } | ||
| 424 | 458 | ||
| 425 | code.AddLine("layout (binding = SAMPLER_BINDING_" + std::to_string(sampler.GetIndex()) + | 459 | code.AddLine("layout (binding = SAMPLER_BINDING_{}) uniform {} {};", sampler.GetIndex(), |
| 426 | ") uniform " + sampler_type + ' ' + GetSampler(sampler) + ';'); | 460 | sampler_type, GetSampler(sampler)); |
| 427 | } | 461 | } |
| 428 | if (!samplers.empty()) | 462 | if (!samplers.empty()) { |
| 429 | code.AddNewLine(); | 463 | code.AddNewLine(); |
| 464 | } | ||
| 465 | } | ||
| 466 | |||
| 467 | void DeclarePhysicalAttributeReader() { | ||
| 468 | if (!ir.HasPhysicalAttributes()) { | ||
| 469 | return; | ||
| 470 | } | ||
| 471 | code.AddLine("float readPhysicalAttribute(uint physical_address) {{"); | ||
| 472 | ++code.scope; | ||
| 473 | code.AddLine("switch (physical_address) {{"); | ||
| 474 | |||
| 475 | // Just declare generic attributes for now. | ||
| 476 | const auto num_attributes{static_cast<u32>(GetNumPhysicalInputAttributes())}; | ||
| 477 | for (u32 index = 0; index < num_attributes; ++index) { | ||
| 478 | const auto attribute{ToGenericAttribute(index)}; | ||
| 479 | for (u32 element = 0; element < 4; ++element) { | ||
| 480 | constexpr u32 generic_base{0x80}; | ||
| 481 | constexpr u32 generic_stride{16}; | ||
| 482 | constexpr u32 element_stride{4}; | ||
| 483 | const u32 address{generic_base + index * generic_stride + element * element_stride}; | ||
| 484 | |||
| 485 | const bool declared{stage != ShaderStage::Fragment || | ||
| 486 | header.ps.GetAttributeUse(index) != AttributeUse::Unused}; | ||
| 487 | const std::string value{declared ? ReadAttribute(attribute, element) : "0"}; | ||
| 488 | code.AddLine("case 0x{:x}: return {};", address, value); | ||
| 489 | } | ||
| 490 | } | ||
| 491 | |||
| 492 | code.AddLine("default: return 0;"); | ||
| 493 | |||
| 494 | code.AddLine("}}"); | ||
| 495 | --code.scope; | ||
| 496 | code.AddLine("}}"); | ||
| 497 | code.AddNewLine(); | ||
| 430 | } | 498 | } |
| 431 | 499 | ||
| 432 | void VisitBlock(const NodeBlock& bb) { | 500 | void VisitBlock(const NodeBlock& bb) { |
| @@ -450,23 +518,26 @@ private: | |||
| 450 | return {}; | 518 | return {}; |
| 451 | } | 519 | } |
| 452 | return (this->*decompiler)(*operation); | 520 | return (this->*decompiler)(*operation); |
| 521 | } | ||
| 453 | 522 | ||
| 454 | } else if (const auto gpr = std::get_if<GprNode>(node)) { | 523 | if (const auto gpr = std::get_if<GprNode>(node)) { |
| 455 | const u32 index = gpr->GetIndex(); | 524 | const u32 index = gpr->GetIndex(); |
| 456 | if (index == Register::ZeroIndex) { | 525 | if (index == Register::ZeroIndex) { |
| 457 | return "0"; | 526 | return "0"; |
| 458 | } | 527 | } |
| 459 | return GetRegister(index); | 528 | return GetRegister(index); |
| 529 | } | ||
| 460 | 530 | ||
| 461 | } else if (const auto immediate = std::get_if<ImmediateNode>(node)) { | 531 | if (const auto immediate = std::get_if<ImmediateNode>(node)) { |
| 462 | const u32 value = immediate->GetValue(); | 532 | const u32 value = immediate->GetValue(); |
| 463 | if (value < 10) { | 533 | if (value < 10) { |
| 464 | // For eyecandy avoid using hex numbers on single digits | 534 | // For eyecandy avoid using hex numbers on single digits |
| 465 | return fmt::format("utof({}u)", immediate->GetValue()); | 535 | return fmt::format("utof({}u)", immediate->GetValue()); |
| 466 | } | 536 | } |
| 467 | return fmt::format("utof(0x{:x}u)", immediate->GetValue()); | 537 | return fmt::format("utof(0x{:x}u)", immediate->GetValue()); |
| 538 | } | ||
| 468 | 539 | ||
| 469 | } else if (const auto predicate = std::get_if<PredicateNode>(node)) { | 540 | if (const auto predicate = std::get_if<PredicateNode>(node)) { |
| 470 | const auto value = [&]() -> std::string { | 541 | const auto value = [&]() -> std::string { |
| 471 | switch (const auto index = predicate->GetIndex(); index) { | 542 | switch (const auto index = predicate->GetIndex(); index) { |
| 472 | case Tegra::Shader::Pred::UnusedIndex: | 543 | case Tegra::Shader::Pred::UnusedIndex: |
| @@ -478,77 +549,22 @@ private: | |||
| 478 | } | 549 | } |
| 479 | }(); | 550 | }(); |
| 480 | if (predicate->IsNegated()) { | 551 | if (predicate->IsNegated()) { |
| 481 | return "!(" + value + ')'; | 552 | return fmt::format("!({})", value); |
| 482 | } | 553 | } |
| 483 | return value; | 554 | return value; |
| 555 | } | ||
| 484 | 556 | ||
| 485 | } else if (const auto abuf = std::get_if<AbufNode>(node)) { | 557 | if (const auto abuf = std::get_if<AbufNode>(node)) { |
| 486 | const auto attribute = abuf->GetIndex(); | 558 | UNIMPLEMENTED_IF_MSG(abuf->IsPhysicalBuffer() && stage == ShaderStage::Geometry, |
| 487 | const auto element = abuf->GetElement(); | 559 | "Physical attributes in geometry shaders are not implemented"); |
| 488 | 560 | if (abuf->IsPhysicalBuffer()) { | |
| 489 | const auto GeometryPass = [&](const std::string& name) { | 561 | return fmt::format("readPhysicalAttribute(ftou({}))", |
| 490 | if (stage == ShaderStage::Geometry && abuf->GetBuffer()) { | 562 | Visit(abuf->GetPhysicalAddress())); |
| 491 | // TODO(Rodrigo): Guard geometry inputs against out of bound reads. Some games | ||
| 492 | // set an 0x80000000 index for those and the shader fails to build. Find out why | ||
| 493 | // this happens and what's its intent. | ||
| 494 | return "gs_" + name + "[ftou(" + Visit(abuf->GetBuffer()) + | ||
| 495 | ") % MAX_VERTEX_INPUT]"; | ||
| 496 | } | ||
| 497 | return name; | ||
| 498 | }; | ||
| 499 | |||
| 500 | switch (attribute) { | ||
| 501 | case Attribute::Index::Position: | ||
| 502 | if (stage != ShaderStage::Fragment) { | ||
| 503 | return GeometryPass("position") + GetSwizzle(element); | ||
| 504 | } else { | ||
| 505 | return element == 3 ? "1.0f" : "gl_FragCoord" + GetSwizzle(element); | ||
| 506 | } | ||
| 507 | case Attribute::Index::PointCoord: | ||
| 508 | switch (element) { | ||
| 509 | case 0: | ||
| 510 | return "gl_PointCoord.x"; | ||
| 511 | case 1: | ||
| 512 | return "gl_PointCoord.y"; | ||
| 513 | case 2: | ||
| 514 | case 3: | ||
| 515 | return "0"; | ||
| 516 | } | ||
| 517 | UNREACHABLE(); | ||
| 518 | return "0"; | ||
| 519 | case Attribute::Index::TessCoordInstanceIDVertexID: | ||
| 520 | // TODO(Subv): Find out what the values are for the first two elements when inside a | ||
| 521 | // vertex shader, and what's the value of the fourth element when inside a Tess Eval | ||
| 522 | // shader. | ||
| 523 | ASSERT(stage == ShaderStage::Vertex); | ||
| 524 | switch (element) { | ||
| 525 | case 2: | ||
| 526 | // Config pack's first value is instance_id. | ||
| 527 | return "uintBitsToFloat(config_pack[0])"; | ||
| 528 | case 3: | ||
| 529 | return "uintBitsToFloat(gl_VertexID)"; | ||
| 530 | } | ||
| 531 | UNIMPLEMENTED_MSG("Unmanaged TessCoordInstanceIDVertexID element={}", element); | ||
| 532 | return "0"; | ||
| 533 | case Attribute::Index::FrontFacing: | ||
| 534 | // TODO(Subv): Find out what the values are for the other elements. | ||
| 535 | ASSERT(stage == ShaderStage::Fragment); | ||
| 536 | switch (element) { | ||
| 537 | case 3: | ||
| 538 | return "itof(gl_FrontFacing ? -1 : 0)"; | ||
| 539 | } | ||
| 540 | UNIMPLEMENTED_MSG("Unmanaged FrontFacing element={}", element); | ||
| 541 | return "0"; | ||
| 542 | default: | ||
| 543 | if (attribute >= Attribute::Index::Attribute_0 && | ||
| 544 | attribute <= Attribute::Index::Attribute_31) { | ||
| 545 | return GeometryPass(GetInputAttribute(attribute)) + GetSwizzle(element); | ||
| 546 | } | ||
| 547 | break; | ||
| 548 | } | 563 | } |
| 549 | UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute)); | 564 | return ReadAttribute(abuf->GetIndex(), abuf->GetElement(), abuf->GetBuffer()); |
| 565 | } | ||
| 550 | 566 | ||
| 551 | } else if (const auto cbuf = std::get_if<CbufNode>(node)) { | 567 | if (const auto cbuf = std::get_if<CbufNode>(node)) { |
| 552 | const Node offset = cbuf->GetOffset(); | 568 | const Node offset = cbuf->GetOffset(); |
| 553 | if (const auto immediate = std::get_if<ImmediateNode>(offset)) { | 569 | if (const auto immediate = std::get_if<ImmediateNode>(offset)) { |
| 554 | // Direct access | 570 | // Direct access |
| @@ -556,48 +572,117 @@ private: | |||
| 556 | ASSERT_MSG(offset_imm % 4 == 0, "Unaligned cbuf direct access"); | 572 | ASSERT_MSG(offset_imm % 4 == 0, "Unaligned cbuf direct access"); |
| 557 | return fmt::format("{}[{}][{}]", GetConstBuffer(cbuf->GetIndex()), | 573 | return fmt::format("{}[{}][{}]", GetConstBuffer(cbuf->GetIndex()), |
| 558 | offset_imm / (4 * 4), (offset_imm / 4) % 4); | 574 | offset_imm / (4 * 4), (offset_imm / 4) % 4); |
| 575 | } | ||
| 559 | 576 | ||
| 560 | } else if (std::holds_alternative<OperationNode>(*offset)) { | 577 | if (std::holds_alternative<OperationNode>(*offset)) { |
| 561 | // Indirect access | 578 | // Indirect access |
| 562 | const std::string final_offset = code.GenerateTemporary(); | 579 | const std::string final_offset = code.GenerateTemporary(); |
| 563 | code.AddLine("uint " + final_offset + " = (ftou(" + Visit(offset) + ") / 4);"); | 580 | code.AddLine("uint {} = (ftou({}) / 4);", final_offset, Visit(offset)); |
| 564 | return fmt::format("{}[{} / 4][{} % 4]", GetConstBuffer(cbuf->GetIndex()), | 581 | return fmt::format("{}[{} / 4][{} % 4]", GetConstBuffer(cbuf->GetIndex()), |
| 565 | final_offset, final_offset); | 582 | final_offset, final_offset); |
| 566 | |||
| 567 | } else { | ||
| 568 | UNREACHABLE_MSG("Unmanaged offset node type"); | ||
| 569 | } | 583 | } |
| 570 | 584 | ||
| 571 | } else if (const auto gmem = std::get_if<GmemNode>(node)) { | 585 | UNREACHABLE_MSG("Unmanaged offset node type"); |
| 586 | } | ||
| 587 | |||
| 588 | if (const auto gmem = std::get_if<GmemNode>(node)) { | ||
| 572 | const std::string real = Visit(gmem->GetRealAddress()); | 589 | const std::string real = Visit(gmem->GetRealAddress()); |
| 573 | const std::string base = Visit(gmem->GetBaseAddress()); | 590 | const std::string base = Visit(gmem->GetBaseAddress()); |
| 574 | const std::string final_offset = "(ftou(" + real + ") - ftou(" + base + ")) / 4"; | 591 | const std::string final_offset = fmt::format("(ftou({}) - ftou({})) / 4", real, base); |
| 575 | return fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset); | 592 | return fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset); |
| 593 | } | ||
| 576 | 594 | ||
| 577 | } else if (const auto lmem = std::get_if<LmemNode>(node)) { | 595 | if (const auto lmem = std::get_if<LmemNode>(node)) { |
| 578 | return fmt::format("{}[ftou({}) / 4]", GetLocalMemory(), Visit(lmem->GetAddress())); | 596 | return fmt::format("{}[ftou({}) / 4]", GetLocalMemory(), Visit(lmem->GetAddress())); |
| 597 | } | ||
| 579 | 598 | ||
| 580 | } else if (const auto internal_flag = std::get_if<InternalFlagNode>(node)) { | 599 | if (const auto internal_flag = std::get_if<InternalFlagNode>(node)) { |
| 581 | return GetInternalFlag(internal_flag->GetFlag()); | 600 | return GetInternalFlag(internal_flag->GetFlag()); |
| 601 | } | ||
| 582 | 602 | ||
| 583 | } else if (const auto conditional = std::get_if<ConditionalNode>(node)) { | 603 | if (const auto conditional = std::get_if<ConditionalNode>(node)) { |
| 584 | // It's invalid to call conditional on nested nodes, use an operation instead | 604 | // It's invalid to call conditional on nested nodes, use an operation instead |
| 585 | code.AddLine("if (" + Visit(conditional->GetCondition()) + ") {"); | 605 | code.AddLine("if ({}) {{", Visit(conditional->GetCondition())); |
| 586 | ++code.scope; | 606 | ++code.scope; |
| 587 | 607 | ||
| 588 | VisitBlock(conditional->GetCode()); | 608 | VisitBlock(conditional->GetCode()); |
| 589 | 609 | ||
| 590 | --code.scope; | 610 | --code.scope; |
| 591 | code.AddLine('}'); | 611 | code.AddLine("}}"); |
| 592 | return {}; | 612 | return {}; |
| 613 | } | ||
| 593 | 614 | ||
| 594 | } else if (const auto comment = std::get_if<CommentNode>(node)) { | 615 | if (const auto comment = std::get_if<CommentNode>(node)) { |
| 595 | return "// " + comment->GetText(); | 616 | return "// " + comment->GetText(); |
| 596 | } | 617 | } |
| 618 | |||
| 597 | UNREACHABLE(); | 619 | UNREACHABLE(); |
| 598 | return {}; | 620 | return {}; |
| 599 | } | 621 | } |
| 600 | 622 | ||
| 623 | std::string ReadAttribute(Attribute::Index attribute, u32 element, Node buffer = {}) { | ||
| 624 | const auto GeometryPass = [&](std::string_view name) { | ||
| 625 | if (stage == ShaderStage::Geometry && buffer) { | ||
| 626 | // TODO(Rodrigo): Guard geometry inputs against out of bound reads. Some games | ||
| 627 | // set an 0x80000000 index for those and the shader fails to build. Find out why | ||
| 628 | // this happens and what's its intent. | ||
| 629 | return fmt::format("gs_{}[ftou({}) % MAX_VERTEX_INPUT]", name, Visit(buffer)); | ||
| 630 | } | ||
| 631 | return std::string(name); | ||
| 632 | }; | ||
| 633 | |||
| 634 | switch (attribute) { | ||
| 635 | case Attribute::Index::Position: | ||
| 636 | if (stage != ShaderStage::Fragment) { | ||
| 637 | return GeometryPass("position") + GetSwizzle(element); | ||
| 638 | } else { | ||
| 639 | return element == 3 ? "1.0f" : ("gl_FragCoord"s + GetSwizzle(element)); | ||
| 640 | } | ||
| 641 | case Attribute::Index::PointCoord: | ||
| 642 | switch (element) { | ||
| 643 | case 0: | ||
| 644 | return "gl_PointCoord.x"; | ||
| 645 | case 1: | ||
| 646 | return "gl_PointCoord.y"; | ||
| 647 | case 2: | ||
| 648 | case 3: | ||
| 649 | return "0"; | ||
| 650 | } | ||
| 651 | UNREACHABLE(); | ||
| 652 | return "0"; | ||
| 653 | case Attribute::Index::TessCoordInstanceIDVertexID: | ||
| 654 | // TODO(Subv): Find out what the values are for the first two elements when inside a | ||
| 655 | // vertex shader, and what's the value of the fourth element when inside a Tess Eval | ||
| 656 | // shader. | ||
| 657 | ASSERT(stage == ShaderStage::Vertex); | ||
| 658 | switch (element) { | ||
| 659 | case 2: | ||
| 660 | // Config pack's first value is instance_id. | ||
| 661 | return "uintBitsToFloat(config_pack[0])"; | ||
| 662 | case 3: | ||
| 663 | return "uintBitsToFloat(gl_VertexID)"; | ||
| 664 | } | ||
| 665 | UNIMPLEMENTED_MSG("Unmanaged TessCoordInstanceIDVertexID element={}", element); | ||
| 666 | return "0"; | ||
| 667 | case Attribute::Index::FrontFacing: | ||
| 668 | // TODO(Subv): Find out what the values are for the other elements. | ||
| 669 | ASSERT(stage == ShaderStage::Fragment); | ||
| 670 | switch (element) { | ||
| 671 | case 3: | ||
| 672 | return "itof(gl_FrontFacing ? -1 : 0)"; | ||
| 673 | } | ||
| 674 | UNIMPLEMENTED_MSG("Unmanaged FrontFacing element={}", element); | ||
| 675 | return "0"; | ||
| 676 | default: | ||
| 677 | if (IsGenericAttribute(attribute)) { | ||
| 678 | return GeometryPass(GetInputAttribute(attribute)) + GetSwizzle(element); | ||
| 679 | } | ||
| 680 | break; | ||
| 681 | } | ||
| 682 | UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute)); | ||
| 683 | return "0"; | ||
| 684 | } | ||
| 685 | |||
| 601 | std::string ApplyPrecise(Operation operation, const std::string& value) { | 686 | std::string ApplyPrecise(Operation operation, const std::string& value) { |
| 602 | if (!IsPrecise(operation)) { | 687 | if (!IsPrecise(operation)) { |
| 603 | return value; | 688 | return value; |
| @@ -606,7 +691,7 @@ private: | |||
| 606 | const std::string precise = stage != ShaderStage::Fragment ? "precise " : ""; | 691 | const std::string precise = stage != ShaderStage::Fragment ? "precise " : ""; |
| 607 | 692 | ||
| 608 | const std::string temporary = code.GenerateTemporary(); | 693 | const std::string temporary = code.GenerateTemporary(); |
| 609 | code.AddLine(precise + "float " + temporary + " = " + value + ';'); | 694 | code.AddLine("{}float {} = {};", precise, temporary, value); |
| 610 | return temporary; | 695 | return temporary; |
| 611 | } | 696 | } |
| 612 | 697 | ||
| @@ -620,7 +705,7 @@ private: | |||
| 620 | } | 705 | } |
| 621 | 706 | ||
| 622 | const std::string temporary = code.GenerateTemporary(); | 707 | const std::string temporary = code.GenerateTemporary(); |
| 623 | code.AddLine("float " + temporary + " = " + Visit(operand) + ';'); | 708 | code.AddLine("float {} = {};", temporary, Visit(operand)); |
| 624 | return temporary; | 709 | return temporary; |
| 625 | } | 710 | } |
| 626 | 711 | ||
| @@ -635,31 +720,32 @@ private: | |||
| 635 | case Type::Float: | 720 | case Type::Float: |
| 636 | return value; | 721 | return value; |
| 637 | case Type::Int: | 722 | case Type::Int: |
| 638 | return "ftoi(" + value + ')'; | 723 | return fmt::format("ftoi({})", value); |
| 639 | case Type::Uint: | 724 | case Type::Uint: |
| 640 | return "ftou(" + value + ')'; | 725 | return fmt::format("ftou({})", value); |
| 641 | case Type::HalfFloat: | 726 | case Type::HalfFloat: |
| 642 | return "toHalf2(" + value + ')'; | 727 | return fmt::format("toHalf2({})", value); |
| 643 | } | 728 | } |
| 644 | UNREACHABLE(); | 729 | UNREACHABLE(); |
| 645 | return value; | 730 | return value; |
| 646 | } | 731 | } |
| 647 | 732 | ||
| 648 | std::string BitwiseCastResult(std::string value, Type type, bool needs_parenthesis = false) { | 733 | std::string BitwiseCastResult(const std::string& value, Type type, |
| 734 | bool needs_parenthesis = false) { | ||
| 649 | switch (type) { | 735 | switch (type) { |
| 650 | case Type::Bool: | 736 | case Type::Bool: |
| 651 | case Type::Bool2: | 737 | case Type::Bool2: |
| 652 | case Type::Float: | 738 | case Type::Float: |
| 653 | if (needs_parenthesis) { | 739 | if (needs_parenthesis) { |
| 654 | return '(' + value + ')'; | 740 | return fmt::format("({})", value); |
| 655 | } | 741 | } |
| 656 | return value; | 742 | return value; |
| 657 | case Type::Int: | 743 | case Type::Int: |
| 658 | return "itof(" + value + ')'; | 744 | return fmt::format("itof({})", value); |
| 659 | case Type::Uint: | 745 | case Type::Uint: |
| 660 | return "utof(" + value + ')'; | 746 | return fmt::format("utof({})", value); |
| 661 | case Type::HalfFloat: | 747 | case Type::HalfFloat: |
| 662 | return "fromHalf2(" + value + ')'; | 748 | return fmt::format("fromHalf2({})", value); |
| 663 | } | 749 | } |
| 664 | UNREACHABLE(); | 750 | UNREACHABLE(); |
| 665 | return value; | 751 | return value; |
| @@ -667,27 +753,27 @@ private: | |||
| 667 | 753 | ||
| 668 | std::string GenerateUnary(Operation operation, const std::string& func, Type result_type, | 754 | std::string GenerateUnary(Operation operation, const std::string& func, Type result_type, |
| 669 | Type type_a, bool needs_parenthesis = true) { | 755 | Type type_a, bool needs_parenthesis = true) { |
| 670 | return ApplyPrecise(operation, | 756 | const std::string op_str = fmt::format("{}({})", func, VisitOperand(operation, 0, type_a)); |
| 671 | BitwiseCastResult(func + '(' + VisitOperand(operation, 0, type_a) + ')', | 757 | |
| 672 | result_type, needs_parenthesis)); | 758 | return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type, needs_parenthesis)); |
| 673 | } | 759 | } |
| 674 | 760 | ||
| 675 | std::string GenerateBinaryInfix(Operation operation, const std::string& func, Type result_type, | 761 | std::string GenerateBinaryInfix(Operation operation, const std::string& func, Type result_type, |
| 676 | Type type_a, Type type_b) { | 762 | Type type_a, Type type_b) { |
| 677 | const std::string op_a = VisitOperand(operation, 0, type_a); | 763 | const std::string op_a = VisitOperand(operation, 0, type_a); |
| 678 | const std::string op_b = VisitOperand(operation, 1, type_b); | 764 | const std::string op_b = VisitOperand(operation, 1, type_b); |
| 765 | const std::string op_str = fmt::format("({} {} {})", op_a, func, op_b); | ||
| 679 | 766 | ||
| 680 | return ApplyPrecise( | 767 | return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type)); |
| 681 | operation, BitwiseCastResult('(' + op_a + ' ' + func + ' ' + op_b + ')', result_type)); | ||
| 682 | } | 768 | } |
| 683 | 769 | ||
| 684 | std::string GenerateBinaryCall(Operation operation, const std::string& func, Type result_type, | 770 | std::string GenerateBinaryCall(Operation operation, const std::string& func, Type result_type, |
| 685 | Type type_a, Type type_b) { | 771 | Type type_a, Type type_b) { |
| 686 | const std::string op_a = VisitOperand(operation, 0, type_a); | 772 | const std::string op_a = VisitOperand(operation, 0, type_a); |
| 687 | const std::string op_b = VisitOperand(operation, 1, type_b); | 773 | const std::string op_b = VisitOperand(operation, 1, type_b); |
| 774 | const std::string op_str = fmt::format("{}({}, {})", func, op_a, op_b); | ||
| 688 | 775 | ||
| 689 | return ApplyPrecise(operation, | 776 | return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type)); |
| 690 | BitwiseCastResult(func + '(' + op_a + ", " + op_b + ')', result_type)); | ||
| 691 | } | 777 | } |
| 692 | 778 | ||
| 693 | std::string GenerateTernary(Operation operation, const std::string& func, Type result_type, | 779 | std::string GenerateTernary(Operation operation, const std::string& func, Type result_type, |
| @@ -695,10 +781,9 @@ private: | |||
| 695 | const std::string op_a = VisitOperand(operation, 0, type_a); | 781 | const std::string op_a = VisitOperand(operation, 0, type_a); |
| 696 | const std::string op_b = VisitOperand(operation, 1, type_b); | 782 | const std::string op_b = VisitOperand(operation, 1, type_b); |
| 697 | const std::string op_c = VisitOperand(operation, 2, type_c); | 783 | const std::string op_c = VisitOperand(operation, 2, type_c); |
| 784 | const std::string op_str = fmt::format("{}({}, {}, {})", func, op_a, op_b, op_c); | ||
| 698 | 785 | ||
| 699 | return ApplyPrecise( | 786 | return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type)); |
| 700 | operation, | ||
| 701 | BitwiseCastResult(func + '(' + op_a + ", " + op_b + ", " + op_c + ')', result_type)); | ||
| 702 | } | 787 | } |
| 703 | 788 | ||
| 704 | std::string GenerateQuaternary(Operation operation, const std::string& func, Type result_type, | 789 | std::string GenerateQuaternary(Operation operation, const std::string& func, Type result_type, |
| @@ -707,10 +792,9 @@ private: | |||
| 707 | const std::string op_b = VisitOperand(operation, 1, type_b); | 792 | const std::string op_b = VisitOperand(operation, 1, type_b); |
| 708 | const std::string op_c = VisitOperand(operation, 2, type_c); | 793 | const std::string op_c = VisitOperand(operation, 2, type_c); |
| 709 | const std::string op_d = VisitOperand(operation, 3, type_d); | 794 | const std::string op_d = VisitOperand(operation, 3, type_d); |
| 795 | const std::string op_str = fmt::format("{}({}, {}, {}, {})", func, op_a, op_b, op_c, op_d); | ||
| 710 | 796 | ||
| 711 | return ApplyPrecise(operation, BitwiseCastResult(func + '(' + op_a + ", " + op_b + ", " + | 797 | return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type)); |
| 712 | op_c + ", " + op_d + ')', | ||
| 713 | result_type)); | ||
| 714 | } | 798 | } |
| 715 | 799 | ||
| 716 | std::string GenerateTexture(Operation operation, const std::string& function_suffix, | 800 | std::string GenerateTexture(Operation operation, const std::string& function_suffix, |
| @@ -773,7 +857,7 @@ private: | |||
| 773 | // required to be constant) | 857 | // required to be constant) |
| 774 | expr += std::to_string(static_cast<s32>(immediate->GetValue())); | 858 | expr += std::to_string(static_cast<s32>(immediate->GetValue())); |
| 775 | } else { | 859 | } else { |
| 776 | expr += "ftoi(" + Visit(operand) + ')'; | 860 | expr += fmt::format("ftoi({})", Visit(operand)); |
| 777 | } | 861 | } |
| 778 | break; | 862 | break; |
| 779 | case Type::Float: | 863 | case Type::Float: |
| @@ -806,7 +890,7 @@ private: | |||
| 806 | expr += std::to_string(static_cast<s32>(immediate->GetValue())); | 890 | expr += std::to_string(static_cast<s32>(immediate->GetValue())); |
| 807 | } else if (device.HasVariableAoffi()) { | 891 | } else if (device.HasVariableAoffi()) { |
| 808 | // Avoid using variable AOFFI on unsupported devices. | 892 | // Avoid using variable AOFFI on unsupported devices. |
| 809 | expr += "ftoi(" + Visit(operand) + ')'; | 893 | expr += fmt::format("ftoi({})", Visit(operand)); |
| 810 | } else { | 894 | } else { |
| 811 | // Insert 0 on devices not supporting variable AOFFI. | 895 | // Insert 0 on devices not supporting variable AOFFI. |
| 812 | expr += '0'; | 896 | expr += '0'; |
| @@ -831,21 +915,21 @@ private: | |||
| 831 | return {}; | 915 | return {}; |
| 832 | } | 916 | } |
| 833 | target = GetRegister(gpr->GetIndex()); | 917 | target = GetRegister(gpr->GetIndex()); |
| 834 | |||
| 835 | } else if (const auto abuf = std::get_if<AbufNode>(dest)) { | 918 | } else if (const auto abuf = std::get_if<AbufNode>(dest)) { |
| 919 | UNIMPLEMENTED_IF(abuf->IsPhysicalBuffer()); | ||
| 920 | |||
| 836 | target = [&]() -> std::string { | 921 | target = [&]() -> std::string { |
| 837 | switch (const auto attribute = abuf->GetIndex(); abuf->GetIndex()) { | 922 | switch (const auto attribute = abuf->GetIndex(); abuf->GetIndex()) { |
| 838 | case Attribute::Index::Position: | 923 | case Attribute::Index::Position: |
| 839 | return "position" + GetSwizzle(abuf->GetElement()); | 924 | return "position"s + GetSwizzle(abuf->GetElement()); |
| 840 | case Attribute::Index::PointSize: | 925 | case Attribute::Index::PointSize: |
| 841 | return "gl_PointSize"; | 926 | return "gl_PointSize"; |
| 842 | case Attribute::Index::ClipDistances0123: | 927 | case Attribute::Index::ClipDistances0123: |
| 843 | return "gl_ClipDistance[" + std::to_string(abuf->GetElement()) + ']'; | 928 | return fmt::format("gl_ClipDistance[{}]", abuf->GetElement()); |
| 844 | case Attribute::Index::ClipDistances4567: | 929 | case Attribute::Index::ClipDistances4567: |
| 845 | return "gl_ClipDistance[" + std::to_string(abuf->GetElement() + 4) + ']'; | 930 | return fmt::format("gl_ClipDistance[{}]", abuf->GetElement() + 4); |
| 846 | default: | 931 | default: |
| 847 | if (attribute >= Attribute::Index::Attribute_0 && | 932 | if (IsGenericAttribute(attribute)) { |
| 848 | attribute <= Attribute::Index::Attribute_31) { | ||
| 849 | return GetOutputAttribute(attribute) + GetSwizzle(abuf->GetElement()); | 933 | return GetOutputAttribute(attribute) + GetSwizzle(abuf->GetElement()); |
| 850 | } | 934 | } |
| 851 | UNIMPLEMENTED_MSG("Unhandled output attribute: {}", | 935 | UNIMPLEMENTED_MSG("Unhandled output attribute: {}", |
| @@ -853,35 +937,21 @@ private: | |||
| 853 | return "0"; | 937 | return "0"; |
| 854 | } | 938 | } |
| 855 | }(); | 939 | }(); |
| 856 | |||
| 857 | } else if (const auto lmem = std::get_if<LmemNode>(dest)) { | 940 | } else if (const auto lmem = std::get_if<LmemNode>(dest)) { |
| 858 | target = GetLocalMemory() + "[ftou(" + Visit(lmem->GetAddress()) + ") / 4]"; | 941 | target = fmt::format("{}[ftou({}) / 4]", GetLocalMemory(), Visit(lmem->GetAddress())); |
| 859 | |||
| 860 | } else if (const auto gmem = std::get_if<GmemNode>(dest)) { | 942 | } else if (const auto gmem = std::get_if<GmemNode>(dest)) { |
| 861 | const std::string real = Visit(gmem->GetRealAddress()); | 943 | const std::string real = Visit(gmem->GetRealAddress()); |
| 862 | const std::string base = Visit(gmem->GetBaseAddress()); | 944 | const std::string base = Visit(gmem->GetBaseAddress()); |
| 863 | const std::string final_offset = "(ftou(" + real + ") - ftou(" + base + ")) / 4"; | 945 | const std::string final_offset = fmt::format("(ftou({}) - ftou({})) / 4", real, base); |
| 864 | target = fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset); | 946 | target = fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset); |
| 865 | |||
| 866 | } else { | 947 | } else { |
| 867 | UNREACHABLE_MSG("Assign called without a proper target"); | 948 | UNREACHABLE_MSG("Assign called without a proper target"); |
| 868 | } | 949 | } |
| 869 | 950 | ||
| 870 | code.AddLine(target + " = " + Visit(src) + ';'); | 951 | code.AddLine("{} = {};", target, Visit(src)); |
| 871 | return {}; | 952 | return {}; |
| 872 | } | 953 | } |
| 873 | 954 | ||
| 874 | std::string Composite(Operation operation) { | ||
| 875 | std::string value = "vec4("; | ||
| 876 | for (std::size_t i = 0; i < 4; ++i) { | ||
| 877 | value += Visit(operation[i]); | ||
| 878 | if (i < 3) | ||
| 879 | value += ", "; | ||
| 880 | } | ||
| 881 | value += ')'; | ||
| 882 | return value; | ||
| 883 | } | ||
| 884 | |||
| 885 | template <Type type> | 955 | template <Type type> |
| 886 | std::string Add(Operation operation) { | 956 | std::string Add(Operation operation) { |
| 887 | return GenerateBinaryInfix(operation, "+", type, type, type); | 957 | return GenerateBinaryInfix(operation, "+", type, type, type); |
| @@ -931,8 +1001,9 @@ private: | |||
| 931 | const std::string condition = Visit(operation[0]); | 1001 | const std::string condition = Visit(operation[0]); |
| 932 | const std::string true_case = Visit(operation[1]); | 1002 | const std::string true_case = Visit(operation[1]); |
| 933 | const std::string false_case = Visit(operation[2]); | 1003 | const std::string false_case = Visit(operation[2]); |
| 934 | return ApplyPrecise(operation, | 1004 | const std::string op_str = fmt::format("({} ? {} : {})", condition, true_case, false_case); |
| 935 | '(' + condition + " ? " + true_case + " : " + false_case + ')'); | 1005 | |
| 1006 | return ApplyPrecise(operation, op_str); | ||
| 936 | } | 1007 | } |
| 937 | 1008 | ||
| 938 | std::string FCos(Operation operation) { | 1009 | std::string FCos(Operation operation) { |
| @@ -996,9 +1067,9 @@ private: | |||
| 996 | std::string ILogicalShiftRight(Operation operation) { | 1067 | std::string ILogicalShiftRight(Operation operation) { |
| 997 | const std::string op_a = VisitOperand(operation, 0, Type::Uint); | 1068 | const std::string op_a = VisitOperand(operation, 0, Type::Uint); |
| 998 | const std::string op_b = VisitOperand(operation, 1, Type::Uint); | 1069 | const std::string op_b = VisitOperand(operation, 1, Type::Uint); |
| 1070 | const std::string op_str = fmt::format("int({} >> {})", op_a, op_b); | ||
| 999 | 1071 | ||
| 1000 | return ApplyPrecise(operation, | 1072 | return ApplyPrecise(operation, BitwiseCastResult(op_str, Type::Int)); |
| 1001 | BitwiseCastResult("int(" + op_a + " >> " + op_b + ')', Type::Int)); | ||
| 1002 | } | 1073 | } |
| 1003 | 1074 | ||
| 1004 | std::string IArithmeticShiftRight(Operation operation) { | 1075 | std::string IArithmeticShiftRight(Operation operation) { |
| @@ -1054,11 +1125,12 @@ private: | |||
| 1054 | } | 1125 | } |
| 1055 | 1126 | ||
| 1056 | std::string HNegate(Operation operation) { | 1127 | std::string HNegate(Operation operation) { |
| 1057 | const auto GetNegate = [&](std::size_t index) -> std::string { | 1128 | const auto GetNegate = [&](std::size_t index) { |
| 1058 | return VisitOperand(operation, index, Type::Bool) + " ? -1 : 1"; | 1129 | return VisitOperand(operation, index, Type::Bool) + " ? -1 : 1"; |
| 1059 | }; | 1130 | }; |
| 1060 | const std::string value = '(' + VisitOperand(operation, 0, Type::HalfFloat) + " * vec2(" + | 1131 | const std::string value = |
| 1061 | GetNegate(1) + ", " + GetNegate(2) + "))"; | 1132 | fmt::format("({} * vec2({}, {}))", VisitOperand(operation, 0, Type::HalfFloat), |
| 1133 | GetNegate(1), GetNegate(2)); | ||
| 1062 | return BitwiseCastResult(value, Type::HalfFloat); | 1134 | return BitwiseCastResult(value, Type::HalfFloat); |
| 1063 | } | 1135 | } |
| 1064 | 1136 | ||
| @@ -1066,7 +1138,8 @@ private: | |||
| 1066 | const std::string value = VisitOperand(operation, 0, Type::HalfFloat); | 1138 | const std::string value = VisitOperand(operation, 0, Type::HalfFloat); |
| 1067 | const std::string min = VisitOperand(operation, 1, Type::Float); | 1139 | const std::string min = VisitOperand(operation, 1, Type::Float); |
| 1068 | const std::string max = VisitOperand(operation, 2, Type::Float); | 1140 | const std::string max = VisitOperand(operation, 2, Type::Float); |
| 1069 | const std::string clamped = "clamp(" + value + ", vec2(" + min + "), vec2(" + max + "))"; | 1141 | const std::string clamped = fmt::format("clamp({}, vec2({}), vec2({}))", value, min, max); |
| 1142 | |||
| 1070 | return ApplyPrecise(operation, BitwiseCastResult(clamped, Type::HalfFloat)); | 1143 | return ApplyPrecise(operation, BitwiseCastResult(clamped, Type::HalfFloat)); |
| 1071 | } | 1144 | } |
| 1072 | 1145 | ||
| @@ -1077,34 +1150,35 @@ private: | |||
| 1077 | case Tegra::Shader::HalfType::H0_H1: | 1150 | case Tegra::Shader::HalfType::H0_H1: |
| 1078 | return operand; | 1151 | return operand; |
| 1079 | case Tegra::Shader::HalfType::F32: | 1152 | case Tegra::Shader::HalfType::F32: |
| 1080 | return "vec2(fromHalf2(" + operand + "))"; | 1153 | return fmt::format("vec2(fromHalf2({}))", operand); |
| 1081 | case Tegra::Shader::HalfType::H0_H0: | 1154 | case Tegra::Shader::HalfType::H0_H0: |
| 1082 | return "vec2(" + operand + "[0])"; | 1155 | return fmt::format("vec2({}[0])", operand); |
| 1083 | case Tegra::Shader::HalfType::H1_H1: | 1156 | case Tegra::Shader::HalfType::H1_H1: |
| 1084 | return "vec2(" + operand + "[1])"; | 1157 | return fmt::format("vec2({}[1])", operand); |
| 1085 | } | 1158 | } |
| 1086 | UNREACHABLE(); | 1159 | UNREACHABLE(); |
| 1087 | return "0"; | 1160 | return "0"; |
| 1088 | }(); | 1161 | }(); |
| 1089 | return "fromHalf2(" + value + ')'; | 1162 | return fmt::format("fromHalf2({})", value); |
| 1090 | } | 1163 | } |
| 1091 | 1164 | ||
| 1092 | std::string HMergeF32(Operation operation) { | 1165 | std::string HMergeF32(Operation operation) { |
| 1093 | return "float(toHalf2(" + Visit(operation[0]) + ")[0])"; | 1166 | return fmt::format("float(toHalf2({})[0])", Visit(operation[0])); |
| 1094 | } | 1167 | } |
| 1095 | 1168 | ||
| 1096 | std::string HMergeH0(Operation operation) { | 1169 | std::string HMergeH0(Operation operation) { |
| 1097 | return "fromHalf2(vec2(toHalf2(" + Visit(operation[1]) + ")[0], toHalf2(" + | 1170 | return fmt::format("fromHalf2(vec2(toHalf2({})[0], toHalf2({})[1]))", Visit(operation[1]), |
| 1098 | Visit(operation[0]) + ")[1]))"; | 1171 | Visit(operation[0])); |
| 1099 | } | 1172 | } |
| 1100 | 1173 | ||
| 1101 | std::string HMergeH1(Operation operation) { | 1174 | std::string HMergeH1(Operation operation) { |
| 1102 | return "fromHalf2(vec2(toHalf2(" + Visit(operation[0]) + ")[0], toHalf2(" + | 1175 | return fmt::format("fromHalf2(vec2(toHalf2({})[0], toHalf2({})[1]))", Visit(operation[0]), |
| 1103 | Visit(operation[1]) + ")[1]))"; | 1176 | Visit(operation[1])); |
| 1104 | } | 1177 | } |
| 1105 | 1178 | ||
| 1106 | std::string HPack2(Operation operation) { | 1179 | std::string HPack2(Operation operation) { |
| 1107 | return "utof(packHalf2x16(vec2(" + Visit(operation[0]) + ", " + Visit(operation[1]) + ")))"; | 1180 | return fmt::format("utof(packHalf2x16(vec2({}, {})))", Visit(operation[0]), |
| 1181 | Visit(operation[1])); | ||
| 1108 | } | 1182 | } |
| 1109 | 1183 | ||
| 1110 | template <Type type> | 1184 | template <Type type> |
| @@ -1162,7 +1236,7 @@ private: | |||
| 1162 | target = GetInternalFlag(flag->GetFlag()); | 1236 | target = GetInternalFlag(flag->GetFlag()); |
| 1163 | } | 1237 | } |
| 1164 | 1238 | ||
| 1165 | code.AddLine(target + " = " + Visit(src) + ';'); | 1239 | code.AddLine("{} = {};", target, Visit(src)); |
| 1166 | return {}; | 1240 | return {}; |
| 1167 | } | 1241 | } |
| 1168 | 1242 | ||
| @@ -1184,7 +1258,7 @@ private: | |||
| 1184 | 1258 | ||
| 1185 | std::string LogicalPick2(Operation operation) { | 1259 | std::string LogicalPick2(Operation operation) { |
| 1186 | const std::string pair = VisitOperand(operation, 0, Type::Bool2); | 1260 | const std::string pair = VisitOperand(operation, 0, Type::Bool2); |
| 1187 | return pair + '[' + VisitOperand(operation, 1, Type::Uint) + ']'; | 1261 | return fmt::format("{}[{}]", pair, VisitOperand(operation, 1, Type::Uint)); |
| 1188 | } | 1262 | } |
| 1189 | 1263 | ||
| 1190 | std::string LogicalAll2(Operation operation) { | 1264 | std::string LogicalAll2(Operation operation) { |
| @@ -1196,15 +1270,15 @@ private: | |||
| 1196 | } | 1270 | } |
| 1197 | 1271 | ||
| 1198 | template <bool with_nan> | 1272 | template <bool with_nan> |
| 1199 | std::string GenerateHalfComparison(Operation operation, std::string compare_op) { | 1273 | std::string GenerateHalfComparison(Operation operation, const std::string& compare_op) { |
| 1200 | std::string comparison{GenerateBinaryCall(operation, compare_op, Type::Bool2, | 1274 | const std::string comparison{GenerateBinaryCall(operation, compare_op, Type::Bool2, |
| 1201 | Type::HalfFloat, Type::HalfFloat)}; | 1275 | Type::HalfFloat, Type::HalfFloat)}; |
| 1202 | if constexpr (!with_nan) { | 1276 | if constexpr (!with_nan) { |
| 1203 | return comparison; | 1277 | return comparison; |
| 1204 | } | 1278 | } |
| 1205 | return "halfFloatNanComparison(" + comparison + ", " + | 1279 | return fmt::format("halfFloatNanComparison({}, {}, {})", comparison, |
| 1206 | VisitOperand(operation, 0, Type::HalfFloat) + ", " + | 1280 | VisitOperand(operation, 0, Type::HalfFloat), |
| 1207 | VisitOperand(operation, 1, Type::HalfFloat) + ')'; | 1281 | VisitOperand(operation, 1, Type::HalfFloat)); |
| 1208 | } | 1282 | } |
| 1209 | 1283 | ||
| 1210 | template <bool with_nan> | 1284 | template <bool with_nan> |
| @@ -1281,12 +1355,12 @@ private: | |||
| 1281 | switch (meta->element) { | 1355 | switch (meta->element) { |
| 1282 | case 0: | 1356 | case 0: |
| 1283 | case 1: | 1357 | case 1: |
| 1284 | return "itof(int(textureSize(" + sampler + ", " + lod + ')' + | 1358 | return fmt::format("itof(int(textureSize({}, {}){}))", sampler, lod, |
| 1285 | GetSwizzle(meta->element) + "))"; | 1359 | GetSwizzle(meta->element)); |
| 1286 | case 2: | 1360 | case 2: |
| 1287 | return "0"; | 1361 | return "0"; |
| 1288 | case 3: | 1362 | case 3: |
| 1289 | return "itof(textureQueryLevels(" + sampler + "))"; | 1363 | return fmt::format("itof(textureQueryLevels({}))", sampler); |
| 1290 | } | 1364 | } |
| 1291 | UNREACHABLE(); | 1365 | UNREACHABLE(); |
| 1292 | return "0"; | 1366 | return "0"; |
| @@ -1297,8 +1371,9 @@ private: | |||
| 1297 | ASSERT(meta); | 1371 | ASSERT(meta); |
| 1298 | 1372 | ||
| 1299 | if (meta->element < 2) { | 1373 | if (meta->element < 2) { |
| 1300 | return "itof(int((" + GenerateTexture(operation, "QueryLod", {}) + " * vec2(256))" + | 1374 | return fmt::format("itof(int(({} * vec2(256)){}))", |
| 1301 | GetSwizzle(meta->element) + "))"; | 1375 | GenerateTexture(operation, "QueryLod", {}), |
| 1376 | GetSwizzle(meta->element)); | ||
| 1302 | } | 1377 | } |
| 1303 | return "0"; | 1378 | return "0"; |
| 1304 | } | 1379 | } |
| @@ -1337,7 +1412,7 @@ private: | |||
| 1337 | const auto target = std::get_if<ImmediateNode>(operation[0]); | 1412 | const auto target = std::get_if<ImmediateNode>(operation[0]); |
| 1338 | UNIMPLEMENTED_IF(!target); | 1413 | UNIMPLEMENTED_IF(!target); |
| 1339 | 1414 | ||
| 1340 | code.AddLine(fmt::format("jmp_to = 0x{:x}u;", target->GetValue())); | 1415 | code.AddLine("jmp_to = 0x{:x}u;", target->GetValue()); |
| 1341 | code.AddLine("break;"); | 1416 | code.AddLine("break;"); |
| 1342 | return {}; | 1417 | return {}; |
| 1343 | } | 1418 | } |
| @@ -1346,7 +1421,7 @@ private: | |||
| 1346 | const auto target = std::get_if<ImmediateNode>(operation[0]); | 1421 | const auto target = std::get_if<ImmediateNode>(operation[0]); |
| 1347 | UNIMPLEMENTED_IF(!target); | 1422 | UNIMPLEMENTED_IF(!target); |
| 1348 | 1423 | ||
| 1349 | code.AddLine(fmt::format("flow_stack[flow_stack_top++] = 0x{:x}u;", target->GetValue())); | 1424 | code.AddLine("flow_stack[flow_stack_top++] = 0x{:x}u;", target->GetValue()); |
| 1350 | return {}; | 1425 | return {}; |
| 1351 | } | 1426 | } |
| 1352 | 1427 | ||
| @@ -1372,7 +1447,7 @@ private: | |||
| 1372 | 1447 | ||
| 1373 | UNIMPLEMENTED_IF_MSG(header.ps.omap.sample_mask != 0, "Sample mask write is unimplemented"); | 1448 | UNIMPLEMENTED_IF_MSG(header.ps.omap.sample_mask != 0, "Sample mask write is unimplemented"); |
| 1374 | 1449 | ||
| 1375 | code.AddLine("if (alpha_test[0] != 0) {"); | 1450 | code.AddLine("if (alpha_test[0] != 0) {{"); |
| 1376 | ++code.scope; | 1451 | ++code.scope; |
| 1377 | // We start on the register containing the alpha value in the first RT. | 1452 | // We start on the register containing the alpha value in the first RT. |
| 1378 | u32 current_reg = 3; | 1453 | u32 current_reg = 3; |
| @@ -1383,13 +1458,12 @@ private: | |||
| 1383 | header.ps.IsColorComponentOutputEnabled(render_target, 1) || | 1458 | header.ps.IsColorComponentOutputEnabled(render_target, 1) || |
| 1384 | header.ps.IsColorComponentOutputEnabled(render_target, 2) || | 1459 | header.ps.IsColorComponentOutputEnabled(render_target, 2) || |
| 1385 | header.ps.IsColorComponentOutputEnabled(render_target, 3)) { | 1460 | header.ps.IsColorComponentOutputEnabled(render_target, 3)) { |
| 1386 | code.AddLine( | 1461 | code.AddLine("if (!AlphaFunc({})) discard;", SafeGetRegister(current_reg)); |
| 1387 | fmt::format("if (!AlphaFunc({})) discard;", SafeGetRegister(current_reg))); | ||
| 1388 | current_reg += 4; | 1462 | current_reg += 4; |
| 1389 | } | 1463 | } |
| 1390 | } | 1464 | } |
| 1391 | --code.scope; | 1465 | --code.scope; |
| 1392 | code.AddLine('}'); | 1466 | code.AddLine("}}"); |
| 1393 | 1467 | ||
| 1394 | // Write the color outputs using the data in the shader registers, disabled | 1468 | // Write the color outputs using the data in the shader registers, disabled |
| 1395 | // rendertargets/components are skipped in the register assignment. | 1469 | // rendertargets/components are skipped in the register assignment. |
| @@ -1398,8 +1472,8 @@ private: | |||
| 1398 | // TODO(Subv): Figure out how dual-source blending is configured in the Switch. | 1472 | // TODO(Subv): Figure out how dual-source blending is configured in the Switch. |
| 1399 | for (u32 component = 0; component < 4; ++component) { | 1473 | for (u32 component = 0; component < 4; ++component) { |
| 1400 | if (header.ps.IsColorComponentOutputEnabled(render_target, component)) { | 1474 | if (header.ps.IsColorComponentOutputEnabled(render_target, component)) { |
| 1401 | code.AddLine(fmt::format("FragColor{}[{}] = {};", render_target, component, | 1475 | code.AddLine("FragColor{}[{}] = {};", render_target, component, |
| 1402 | SafeGetRegister(current_reg))); | 1476 | SafeGetRegister(current_reg)); |
| 1403 | ++current_reg; | 1477 | ++current_reg; |
| 1404 | } | 1478 | } |
| 1405 | } | 1479 | } |
| @@ -1408,7 +1482,7 @@ private: | |||
| 1408 | if (header.ps.omap.depth) { | 1482 | if (header.ps.omap.depth) { |
| 1409 | // The depth output is always 2 registers after the last color output, and current_reg | 1483 | // The depth output is always 2 registers after the last color output, and current_reg |
| 1410 | // already contains one past the last color register. | 1484 | // already contains one past the last color register. |
| 1411 | code.AddLine("gl_FragDepth = " + SafeGetRegister(current_reg + 1) + ';'); | 1485 | code.AddLine("gl_FragDepth = {};", SafeGetRegister(current_reg + 1)); |
| 1412 | } | 1486 | } |
| 1413 | 1487 | ||
| 1414 | code.AddLine("return;"); | 1488 | code.AddLine("return;"); |
| @@ -1418,11 +1492,11 @@ private: | |||
| 1418 | std::string Discard(Operation operation) { | 1492 | std::string Discard(Operation operation) { |
| 1419 | // Enclose "discard" in a conditional, so that GLSL compilation does not complain | 1493 | // Enclose "discard" in a conditional, so that GLSL compilation does not complain |
| 1420 | // about unexecuted instructions that may follow this. | 1494 | // about unexecuted instructions that may follow this. |
| 1421 | code.AddLine("if (true) {"); | 1495 | code.AddLine("if (true) {{"); |
| 1422 | ++code.scope; | 1496 | ++code.scope; |
| 1423 | code.AddLine("discard;"); | 1497 | code.AddLine("discard;"); |
| 1424 | --code.scope; | 1498 | --code.scope; |
| 1425 | code.AddLine("}"); | 1499 | code.AddLine("}}"); |
| 1426 | return {}; | 1500 | return {}; |
| 1427 | } | 1501 | } |
| 1428 | 1502 | ||
| @@ -1452,6 +1526,16 @@ private: | |||
| 1452 | return "uintBitsToFloat(config_pack[2])"; | 1526 | return "uintBitsToFloat(config_pack[2])"; |
| 1453 | } | 1527 | } |
| 1454 | 1528 | ||
| 1529 | template <u32 element> | ||
| 1530 | std::string LocalInvocationId(Operation) { | ||
| 1531 | return "utof(gl_LocalInvocationID"s + GetSwizzle(element) + ')'; | ||
| 1532 | } | ||
| 1533 | |||
| 1534 | template <u32 element> | ||
| 1535 | std::string WorkGroupId(Operation) { | ||
| 1536 | return "utof(gl_WorkGroupID"s + GetSwizzle(element) + ')'; | ||
| 1537 | } | ||
| 1538 | |||
| 1455 | static constexpr OperationDecompilersArray operation_decompilers = { | 1539 | static constexpr OperationDecompilersArray operation_decompilers = { |
| 1456 | &GLSLDecompiler::Assign, | 1540 | &GLSLDecompiler::Assign, |
| 1457 | 1541 | ||
| @@ -1591,6 +1675,12 @@ private: | |||
| 1591 | &GLSLDecompiler::EndPrimitive, | 1675 | &GLSLDecompiler::EndPrimitive, |
| 1592 | 1676 | ||
| 1593 | &GLSLDecompiler::YNegate, | 1677 | &GLSLDecompiler::YNegate, |
| 1678 | &GLSLDecompiler::LocalInvocationId<0>, | ||
| 1679 | &GLSLDecompiler::LocalInvocationId<1>, | ||
| 1680 | &GLSLDecompiler::LocalInvocationId<2>, | ||
| 1681 | &GLSLDecompiler::WorkGroupId<0>, | ||
| 1682 | &GLSLDecompiler::WorkGroupId<1>, | ||
| 1683 | &GLSLDecompiler::WorkGroupId<2>, | ||
| 1594 | }; | 1684 | }; |
| 1595 | 1685 | ||
| 1596 | std::string GetRegister(u32 index) const { | 1686 | std::string GetRegister(u32 index) const { |
| @@ -1602,15 +1692,11 @@ private: | |||
| 1602 | } | 1692 | } |
| 1603 | 1693 | ||
| 1604 | std::string GetInputAttribute(Attribute::Index attribute) const { | 1694 | std::string GetInputAttribute(Attribute::Index attribute) const { |
| 1605 | const auto index{static_cast<u32>(attribute) - | 1695 | return GetDeclarationWithSuffix(GetGenericAttributeIndex(attribute), "input_attr"); |
| 1606 | static_cast<u32>(Attribute::Index::Attribute_0)}; | ||
| 1607 | return GetDeclarationWithSuffix(index, "input_attr"); | ||
| 1608 | } | 1696 | } |
| 1609 | 1697 | ||
| 1610 | std::string GetOutputAttribute(Attribute::Index attribute) const { | 1698 | std::string GetOutputAttribute(Attribute::Index attribute) const { |
| 1611 | const auto index{static_cast<u32>(attribute) - | 1699 | return GetDeclarationWithSuffix(GetGenericAttributeIndex(attribute), "output_attr"); |
| 1612 | static_cast<u32>(Attribute::Index::Attribute_0)}; | ||
| 1613 | return GetDeclarationWithSuffix(index, "output_attr"); | ||
| 1614 | } | 1700 | } |
| 1615 | 1701 | ||
| 1616 | std::string GetConstBuffer(u32 index) const { | 1702 | std::string GetConstBuffer(u32 index) const { |
| @@ -1640,7 +1726,7 @@ private: | |||
| 1640 | const auto index = static_cast<u32>(flag); | 1726 | const auto index = static_cast<u32>(flag); |
| 1641 | ASSERT(index < static_cast<u32>(InternalFlag::Amount)); | 1727 | ASSERT(index < static_cast<u32>(InternalFlag::Amount)); |
| 1642 | 1728 | ||
| 1643 | return std::string(InternalFlagNames[index]) + '_' + suffix; | 1729 | return fmt::format("{}_{}", InternalFlagNames[index], suffix); |
| 1644 | } | 1730 | } |
| 1645 | 1731 | ||
| 1646 | std::string GetSampler(const Sampler& sampler) const { | 1732 | std::string GetSampler(const Sampler& sampler) const { |
| @@ -1648,7 +1734,20 @@ private: | |||
| 1648 | } | 1734 | } |
| 1649 | 1735 | ||
| 1650 | std::string GetDeclarationWithSuffix(u32 index, const std::string& name) const { | 1736 | std::string GetDeclarationWithSuffix(u32 index, const std::string& name) const { |
| 1651 | return name + '_' + std::to_string(index) + '_' + suffix; | 1737 | return fmt::format("{}_{}_{}", name, index, suffix); |
| 1738 | } | ||
| 1739 | |||
| 1740 | u32 GetNumPhysicalInputAttributes() const { | ||
| 1741 | return stage == ShaderStage::Vertex ? GetNumPhysicalAttributes() : GetNumPhysicalVaryings(); | ||
| 1742 | } | ||
| 1743 | |||
| 1744 | u32 GetNumPhysicalAttributes() const { | ||
| 1745 | return std::min<u32>(device.GetMaxVertexAttributes(), Maxwell::NumVertexAttributes); | ||
| 1746 | } | ||
| 1747 | |||
| 1748 | u32 GetNumPhysicalVaryings() const { | ||
| 1749 | return std::min<u32>(device.GetMaxVaryings() - GENERIC_VARYING_START_LOCATION, | ||
| 1750 | Maxwell::NumVaryings); | ||
| 1652 | } | 1751 | } |
| 1653 | 1752 | ||
| 1654 | const Device& device; | 1753 | const Device& device; |
| @@ -1663,24 +1762,25 @@ private: | |||
| 1663 | } // Anonymous namespace | 1762 | } // Anonymous namespace |
| 1664 | 1763 | ||
| 1665 | std::string GetCommonDeclarations() { | 1764 | std::string GetCommonDeclarations() { |
| 1666 | const auto cbuf = std::to_string(MAX_CONSTBUFFER_ELEMENTS); | 1765 | return fmt::format( |
| 1667 | return "#define MAX_CONSTBUFFER_ELEMENTS " + cbuf + "\n" + | 1766 | "#define MAX_CONSTBUFFER_ELEMENTS {}\n" |
| 1668 | "#define ftoi floatBitsToInt\n" | 1767 | "#define ftoi floatBitsToInt\n" |
| 1669 | "#define ftou floatBitsToUint\n" | 1768 | "#define ftou floatBitsToUint\n" |
| 1670 | "#define itof intBitsToFloat\n" | 1769 | "#define itof intBitsToFloat\n" |
| 1671 | "#define utof uintBitsToFloat\n\n" | 1770 | "#define utof uintBitsToFloat\n\n" |
| 1672 | "float fromHalf2(vec2 pair) {\n" | 1771 | "float fromHalf2(vec2 pair) {{\n" |
| 1673 | " return utof(packHalf2x16(pair));\n" | 1772 | " return utof(packHalf2x16(pair));\n" |
| 1674 | "}\n\n" | 1773 | "}}\n\n" |
| 1675 | "vec2 toHalf2(float value) {\n" | 1774 | "vec2 toHalf2(float value) {{\n" |
| 1676 | " return unpackHalf2x16(ftou(value));\n" | 1775 | " return unpackHalf2x16(ftou(value));\n" |
| 1677 | "}\n\n" | 1776 | "}}\n\n" |
| 1678 | "bvec2 halfFloatNanComparison(bvec2 comparison, vec2 pair1, vec2 pair2) {\n" | 1777 | "bvec2 halfFloatNanComparison(bvec2 comparison, vec2 pair1, vec2 pair2) {{\n" |
| 1679 | " bvec2 is_nan1 = isnan(pair1);\n" | 1778 | " bvec2 is_nan1 = isnan(pair1);\n" |
| 1680 | " bvec2 is_nan2 = isnan(pair2);\n" | 1779 | " bvec2 is_nan2 = isnan(pair2);\n" |
| 1681 | " return bvec2(comparison.x || is_nan1.x || is_nan2.x, comparison.y || is_nan1.y || " | 1780 | " return bvec2(comparison.x || is_nan1.x || is_nan2.x, comparison.y || is_nan1.y || " |
| 1682 | "is_nan2.y);\n" | 1781 | "is_nan2.y);\n" |
| 1683 | "}\n"; | 1782 | "}}\n", |
| 1783 | MAX_CONSTBUFFER_ELEMENTS); | ||
| 1684 | } | 1784 | } |
| 1685 | 1785 | ||
| 1686 | ProgramResult Decompile(const Device& device, const ShaderIR& ir, Maxwell::ShaderStage stage, | 1786 | 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 53752b38d..ee4a45ca2 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp | |||
| @@ -106,6 +106,8 @@ bool ShaderDiskCacheRaw::Save(FileUtil::IOFile& file) const { | |||
| 106 | 106 | ||
| 107 | ShaderDiskCacheOpenGL::ShaderDiskCacheOpenGL(Core::System& system) : system{system} {} | 107 | ShaderDiskCacheOpenGL::ShaderDiskCacheOpenGL(Core::System& system) : system{system} {} |
| 108 | 108 | ||
| 109 | ShaderDiskCacheOpenGL::~ShaderDiskCacheOpenGL() = default; | ||
| 110 | |||
| 109 | std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>> | 111 | std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>> |
| 110 | ShaderDiskCacheOpenGL::LoadTransferable() { | 112 | ShaderDiskCacheOpenGL::LoadTransferable() { |
| 111 | // Skip games without title id | 113 | // Skip games without title id |
| @@ -177,11 +179,11 @@ ShaderDiskCacheOpenGL::LoadTransferable() { | |||
| 177 | return {}; | 179 | return {}; |
| 178 | } | 180 | } |
| 179 | } | 181 | } |
| 182 | |||
| 180 | return {{raws, usages}}; | 183 | return {{raws, usages}}; |
| 181 | } | 184 | } |
| 182 | 185 | ||
| 183 | std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, | 186 | std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, ShaderDumpsMap> |
| 184 | std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>> | ||
| 185 | ShaderDiskCacheOpenGL::LoadPrecompiled() { | 187 | ShaderDiskCacheOpenGL::LoadPrecompiled() { |
| 186 | if (!IsUsable()) | 188 | if (!IsUsable()) |
| 187 | return {}; | 189 | return {}; |
| @@ -205,62 +207,66 @@ ShaderDiskCacheOpenGL::LoadPrecompiled() { | |||
| 205 | return *result; | 207 | return *result; |
| 206 | } | 208 | } |
| 207 | 209 | ||
| 208 | std::optional<std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, | 210 | std::optional<std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, ShaderDumpsMap>> |
| 209 | std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>> | ||
| 210 | ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) { | 211 | ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) { |
| 212 | // Read compressed file from disk and decompress to virtual precompiled cache file | ||
| 213 | std::vector<u8> compressed(file.GetSize()); | ||
| 214 | file.ReadBytes(compressed.data(), compressed.size()); | ||
| 215 | const std::vector<u8> decompressed = Common::Compression::DecompressDataZSTD(compressed); | ||
| 216 | SaveArrayToPrecompiled(decompressed.data(), decompressed.size()); | ||
| 217 | precompiled_cache_virtual_file_offset = 0; | ||
| 218 | |||
| 211 | ShaderCacheVersionHash file_hash{}; | 219 | ShaderCacheVersionHash file_hash{}; |
| 212 | if (file.ReadArray(file_hash.data(), file_hash.size()) != file_hash.size()) { | 220 | if (!LoadArrayFromPrecompiled(file_hash.data(), file_hash.size())) { |
| 221 | precompiled_cache_virtual_file_offset = 0; | ||
| 213 | return {}; | 222 | return {}; |
| 214 | } | 223 | } |
| 215 | if (GetShaderCacheVersionHash() != file_hash) { | 224 | if (GetShaderCacheVersionHash() != file_hash) { |
| 216 | LOG_INFO(Render_OpenGL, "Precompiled cache is from another version of the emulator"); | 225 | LOG_INFO(Render_OpenGL, "Precompiled cache is from another version of the emulator"); |
| 226 | precompiled_cache_virtual_file_offset = 0; | ||
| 217 | return {}; | 227 | return {}; |
| 218 | } | 228 | } |
| 219 | 229 | ||
| 220 | std::unordered_map<u64, ShaderDiskCacheDecompiled> decompiled; | 230 | std::unordered_map<u64, ShaderDiskCacheDecompiled> decompiled; |
| 221 | std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump> dumps; | 231 | ShaderDumpsMap dumps; |
| 222 | while (file.Tell() < file.GetSize()) { | 232 | while (precompiled_cache_virtual_file_offset < precompiled_cache_virtual_file.GetSize()) { |
| 223 | PrecompiledEntryKind kind{}; | 233 | PrecompiledEntryKind kind{}; |
| 224 | if (file.ReadBytes(&kind, sizeof(u32)) != sizeof(u32)) { | 234 | if (!LoadObjectFromPrecompiled(kind)) { |
| 225 | return {}; | 235 | return {}; |
| 226 | } | 236 | } |
| 227 | 237 | ||
| 228 | switch (kind) { | 238 | switch (kind) { |
| 229 | case PrecompiledEntryKind::Decompiled: { | 239 | case PrecompiledEntryKind::Decompiled: { |
| 230 | u64 unique_identifier{}; | 240 | u64 unique_identifier{}; |
| 231 | if (file.ReadBytes(&unique_identifier, sizeof(u64)) != sizeof(u64)) | 241 | if (!LoadObjectFromPrecompiled(unique_identifier)) { |
| 232 | return {}; | 242 | return {}; |
| 243 | } | ||
| 233 | 244 | ||
| 234 | const auto entry = LoadDecompiledEntry(file); | 245 | auto entry = LoadDecompiledEntry(); |
| 235 | if (!entry) | 246 | if (!entry) { |
| 236 | return {}; | 247 | return {}; |
| 248 | } | ||
| 237 | decompiled.insert({unique_identifier, std::move(*entry)}); | 249 | decompiled.insert({unique_identifier, std::move(*entry)}); |
| 238 | break; | 250 | break; |
| 239 | } | 251 | } |
| 240 | case PrecompiledEntryKind::Dump: { | 252 | case PrecompiledEntryKind::Dump: { |
| 241 | ShaderDiskCacheUsage usage; | 253 | ShaderDiskCacheUsage usage; |
| 242 | if (file.ReadBytes(&usage, sizeof(usage)) != sizeof(usage)) | 254 | if (!LoadObjectFromPrecompiled(usage)) { |
| 243 | return {}; | 255 | return {}; |
| 256 | } | ||
| 244 | 257 | ||
| 245 | ShaderDiskCacheDump dump; | 258 | ShaderDiskCacheDump dump; |
| 246 | if (file.ReadBytes(&dump.binary_format, sizeof(u32)) != sizeof(u32)) | 259 | if (!LoadObjectFromPrecompiled(dump.binary_format)) { |
| 247 | return {}; | ||
| 248 | |||
| 249 | u32 binary_length{}; | ||
| 250 | u32 compressed_size{}; | ||
| 251 | if (file.ReadBytes(&binary_length, sizeof(u32)) != sizeof(u32) || | ||
| 252 | file.ReadBytes(&compressed_size, sizeof(u32)) != sizeof(u32)) { | ||
| 253 | return {}; | 260 | return {}; |
| 254 | } | 261 | } |
| 255 | 262 | ||
| 256 | std::vector<u8> compressed_binary(compressed_size); | 263 | u32 binary_length{}; |
| 257 | if (file.ReadArray(compressed_binary.data(), compressed_binary.size()) != | 264 | if (!LoadObjectFromPrecompiled(binary_length)) { |
| 258 | compressed_binary.size()) { | ||
| 259 | return {}; | 265 | return {}; |
| 260 | } | 266 | } |
| 261 | 267 | ||
| 262 | dump.binary = Common::Compression::DecompressDataZSTD(compressed_binary); | 268 | dump.binary.resize(binary_length); |
| 263 | if (dump.binary.empty()) { | 269 | if (!LoadArrayFromPrecompiled(dump.binary.data(), dump.binary.size())) { |
| 264 | return {}; | 270 | return {}; |
| 265 | } | 271 | } |
| 266 | 272 | ||
| @@ -274,154 +280,151 @@ ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) { | |||
| 274 | return {{decompiled, dumps}}; | 280 | return {{decompiled, dumps}}; |
| 275 | } | 281 | } |
| 276 | 282 | ||
| 277 | std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEntry( | 283 | std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEntry() { |
| 278 | FileUtil::IOFile& file) { | ||
| 279 | u32 code_size{}; | 284 | u32 code_size{}; |
| 280 | u32 compressed_code_size{}; | 285 | if (!LoadObjectFromPrecompiled(code_size)) { |
| 281 | if (file.ReadBytes(&code_size, sizeof(u32)) != sizeof(u32) || | ||
| 282 | file.ReadBytes(&compressed_code_size, sizeof(u32)) != sizeof(u32)) { | ||
| 283 | return {}; | 286 | return {}; |
| 284 | } | 287 | } |
| 285 | 288 | ||
| 286 | std::vector<u8> compressed_code(compressed_code_size); | 289 | std::string code(code_size, '\0'); |
| 287 | if (file.ReadArray(compressed_code.data(), compressed_code.size()) != compressed_code.size()) { | 290 | if (!LoadArrayFromPrecompiled(code.data(), code.size())) { |
| 288 | return {}; | 291 | return {}; |
| 289 | } | 292 | } |
| 290 | 293 | ||
| 291 | const std::vector<u8> code = Common::Compression::DecompressDataZSTD(compressed_code); | ||
| 292 | if (code.empty()) { | ||
| 293 | return {}; | ||
| 294 | } | ||
| 295 | ShaderDiskCacheDecompiled entry; | 294 | ShaderDiskCacheDecompiled entry; |
| 296 | entry.code = std::string(reinterpret_cast<const char*>(code.data()), code_size); | 295 | entry.code = std::move(code); |
| 297 | 296 | ||
| 298 | u32 const_buffers_count{}; | 297 | u32 const_buffers_count{}; |
| 299 | if (file.ReadBytes(&const_buffers_count, sizeof(u32)) != sizeof(u32)) | 298 | if (!LoadObjectFromPrecompiled(const_buffers_count)) { |
| 300 | return {}; | 299 | return {}; |
| 300 | } | ||
| 301 | |||
| 301 | for (u32 i = 0; i < const_buffers_count; ++i) { | 302 | for (u32 i = 0; i < const_buffers_count; ++i) { |
| 302 | u32 max_offset{}; | 303 | u32 max_offset{}; |
| 303 | u32 index{}; | 304 | u32 index{}; |
| 304 | u8 is_indirect{}; | 305 | bool is_indirect{}; |
| 305 | if (file.ReadBytes(&max_offset, sizeof(u32)) != sizeof(u32) || | 306 | if (!LoadObjectFromPrecompiled(max_offset) || !LoadObjectFromPrecompiled(index) || |
| 306 | file.ReadBytes(&index, sizeof(u32)) != sizeof(u32) || | 307 | !LoadObjectFromPrecompiled(is_indirect)) { |
| 307 | file.ReadBytes(&is_indirect, sizeof(u8)) != sizeof(u8)) { | ||
| 308 | return {}; | 308 | return {}; |
| 309 | } | 309 | } |
| 310 | entry.entries.const_buffers.emplace_back(max_offset, is_indirect != 0, index); | 310 | entry.entries.const_buffers.emplace_back(max_offset, is_indirect, index); |
| 311 | } | 311 | } |
| 312 | 312 | ||
| 313 | u32 samplers_count{}; | 313 | u32 samplers_count{}; |
| 314 | if (file.ReadBytes(&samplers_count, sizeof(u32)) != sizeof(u32)) | 314 | if (!LoadObjectFromPrecompiled(samplers_count)) { |
| 315 | return {}; | 315 | return {}; |
| 316 | } | ||
| 317 | |||
| 316 | for (u32 i = 0; i < samplers_count; ++i) { | 318 | for (u32 i = 0; i < samplers_count; ++i) { |
| 317 | u64 offset{}; | 319 | u64 offset{}; |
| 318 | u64 index{}; | 320 | u64 index{}; |
| 319 | u32 type{}; | 321 | u32 type{}; |
| 320 | u8 is_array{}; | 322 | bool is_array{}; |
| 321 | u8 is_shadow{}; | 323 | bool is_shadow{}; |
| 322 | u8 is_bindless{}; | 324 | bool is_bindless{}; |
| 323 | if (file.ReadBytes(&offset, sizeof(u64)) != sizeof(u64) || | 325 | if (!LoadObjectFromPrecompiled(offset) || !LoadObjectFromPrecompiled(index) || |
| 324 | file.ReadBytes(&index, sizeof(u64)) != sizeof(u64) || | 326 | !LoadObjectFromPrecompiled(type) || !LoadObjectFromPrecompiled(is_array) || |
| 325 | file.ReadBytes(&type, sizeof(u32)) != sizeof(u32) || | 327 | !LoadObjectFromPrecompiled(is_shadow) || !LoadObjectFromPrecompiled(is_bindless)) { |
| 326 | file.ReadBytes(&is_array, sizeof(u8)) != sizeof(u8) || | ||
| 327 | file.ReadBytes(&is_shadow, sizeof(u8)) != sizeof(u8) || | ||
| 328 | file.ReadBytes(&is_bindless, sizeof(u8)) != sizeof(u8)) { | ||
| 329 | return {}; | 328 | return {}; |
| 330 | } | 329 | } |
| 331 | entry.entries.samplers.emplace_back(static_cast<std::size_t>(offset), | 330 | entry.entries.samplers.emplace_back( |
| 332 | static_cast<std::size_t>(index), | 331 | static_cast<std::size_t>(offset), static_cast<std::size_t>(index), |
| 333 | static_cast<Tegra::Shader::TextureType>(type), | 332 | static_cast<Tegra::Shader::TextureType>(type), is_array, is_shadow, is_bindless); |
| 334 | is_array != 0, is_shadow != 0, is_bindless != 0); | ||
| 335 | } | 333 | } |
| 336 | 334 | ||
| 337 | u32 global_memory_count{}; | 335 | u32 global_memory_count{}; |
| 338 | if (file.ReadBytes(&global_memory_count, sizeof(u32)) != sizeof(u32)) | 336 | if (!LoadObjectFromPrecompiled(global_memory_count)) { |
| 339 | return {}; | 337 | return {}; |
| 338 | } | ||
| 339 | |||
| 340 | for (u32 i = 0; i < global_memory_count; ++i) { | 340 | for (u32 i = 0; i < global_memory_count; ++i) { |
| 341 | u32 cbuf_index{}; | 341 | u32 cbuf_index{}; |
| 342 | u32 cbuf_offset{}; | 342 | u32 cbuf_offset{}; |
| 343 | u8 is_read{}; | 343 | bool is_read{}; |
| 344 | u8 is_written{}; | 344 | bool is_written{}; |
| 345 | if (file.ReadBytes(&cbuf_index, sizeof(u32)) != sizeof(u32) || | 345 | if (!LoadObjectFromPrecompiled(cbuf_index) || !LoadObjectFromPrecompiled(cbuf_offset) || |
| 346 | file.ReadBytes(&cbuf_offset, sizeof(u32)) != sizeof(u32) || | 346 | !LoadObjectFromPrecompiled(is_read) || !LoadObjectFromPrecompiled(is_written)) { |
| 347 | file.ReadBytes(&is_read, sizeof(u8)) != sizeof(u8) || | ||
| 348 | file.ReadBytes(&is_written, sizeof(u8)) != sizeof(u8)) { | ||
| 349 | return {}; | 347 | return {}; |
| 350 | } | 348 | } |
| 351 | entry.entries.global_memory_entries.emplace_back(cbuf_index, cbuf_offset, is_read != 0, | 349 | entry.entries.global_memory_entries.emplace_back(cbuf_index, cbuf_offset, is_read, |
| 352 | is_written != 0); | 350 | is_written); |
| 353 | } | 351 | } |
| 354 | 352 | ||
| 355 | for (auto& clip_distance : entry.entries.clip_distances) { | 353 | for (auto& clip_distance : entry.entries.clip_distances) { |
| 356 | u8 clip_distance_raw{}; | 354 | if (!LoadObjectFromPrecompiled(clip_distance)) { |
| 357 | if (file.ReadBytes(&clip_distance_raw, sizeof(u8)) != sizeof(u8)) | ||
| 358 | return {}; | 355 | return {}; |
| 359 | clip_distance = clip_distance_raw != 0; | 356 | } |
| 360 | } | 357 | } |
| 361 | 358 | ||
| 362 | u64 shader_length{}; | 359 | u64 shader_length{}; |
| 363 | if (file.ReadBytes(&shader_length, sizeof(u64)) != sizeof(u64)) | 360 | if (!LoadObjectFromPrecompiled(shader_length)) { |
| 364 | return {}; | 361 | return {}; |
| 362 | } | ||
| 363 | |||
| 365 | entry.entries.shader_length = static_cast<std::size_t>(shader_length); | 364 | entry.entries.shader_length = static_cast<std::size_t>(shader_length); |
| 366 | 365 | ||
| 367 | return entry; | 366 | return entry; |
| 368 | } | 367 | } |
| 369 | 368 | ||
| 370 | bool ShaderDiskCacheOpenGL::SaveDecompiledFile(FileUtil::IOFile& file, u64 unique_identifier, | 369 | bool ShaderDiskCacheOpenGL::SaveDecompiledFile(u64 unique_identifier, const std::string& code, |
| 371 | const std::string& code, | ||
| 372 | const std::vector<u8>& compressed_code, | ||
| 373 | const GLShader::ShaderEntries& entries) { | 370 | const GLShader::ShaderEntries& entries) { |
| 374 | if (file.WriteObject(static_cast<u32>(PrecompiledEntryKind::Decompiled)) != 1 || | 371 | if (!SaveObjectToPrecompiled(static_cast<u32>(PrecompiledEntryKind::Decompiled)) || |
| 375 | file.WriteObject(unique_identifier) != 1 || | 372 | !SaveObjectToPrecompiled(unique_identifier) || |
| 376 | file.WriteObject(static_cast<u32>(code.size())) != 1 || | 373 | !SaveObjectToPrecompiled(static_cast<u32>(code.size())) || |
| 377 | file.WriteObject(static_cast<u32>(compressed_code.size())) != 1 || | 374 | !SaveArrayToPrecompiled(code.data(), code.size())) { |
| 378 | file.WriteArray(compressed_code.data(), compressed_code.size()) != compressed_code.size()) { | ||
| 379 | return false; | 375 | return false; |
| 380 | } | 376 | } |
| 381 | 377 | ||
| 382 | if (file.WriteObject(static_cast<u32>(entries.const_buffers.size())) != 1) | 378 | if (!SaveObjectToPrecompiled(static_cast<u32>(entries.const_buffers.size()))) { |
| 383 | return false; | 379 | return false; |
| 380 | } | ||
| 384 | for (const auto& cbuf : entries.const_buffers) { | 381 | for (const auto& cbuf : entries.const_buffers) { |
| 385 | if (file.WriteObject(static_cast<u32>(cbuf.GetMaxOffset())) != 1 || | 382 | if (!SaveObjectToPrecompiled(static_cast<u32>(cbuf.GetMaxOffset())) || |
| 386 | file.WriteObject(static_cast<u32>(cbuf.GetIndex())) != 1 || | 383 | !SaveObjectToPrecompiled(static_cast<u32>(cbuf.GetIndex())) || |
| 387 | file.WriteObject(static_cast<u8>(cbuf.IsIndirect() ? 1 : 0)) != 1) { | 384 | !SaveObjectToPrecompiled(cbuf.IsIndirect())) { |
| 388 | return false; | 385 | return false; |
| 389 | } | 386 | } |
| 390 | } | 387 | } |
| 391 | 388 | ||
| 392 | if (file.WriteObject(static_cast<u32>(entries.samplers.size())) != 1) | 389 | if (!SaveObjectToPrecompiled(static_cast<u32>(entries.samplers.size()))) { |
| 393 | return false; | 390 | return false; |
| 391 | } | ||
| 394 | for (const auto& sampler : entries.samplers) { | 392 | for (const auto& sampler : entries.samplers) { |
| 395 | if (file.WriteObject(static_cast<u64>(sampler.GetOffset())) != 1 || | 393 | if (!SaveObjectToPrecompiled(static_cast<u64>(sampler.GetOffset())) || |
| 396 | file.WriteObject(static_cast<u64>(sampler.GetIndex())) != 1 || | 394 | !SaveObjectToPrecompiled(static_cast<u64>(sampler.GetIndex())) || |
| 397 | file.WriteObject(static_cast<u32>(sampler.GetType())) != 1 || | 395 | !SaveObjectToPrecompiled(static_cast<u32>(sampler.GetType())) || |
| 398 | file.WriteObject(static_cast<u8>(sampler.IsArray() ? 1 : 0)) != 1 || | 396 | !SaveObjectToPrecompiled(sampler.IsArray()) || |
| 399 | file.WriteObject(static_cast<u8>(sampler.IsShadow() ? 1 : 0)) != 1 || | 397 | !SaveObjectToPrecompiled(sampler.IsShadow()) || |
| 400 | file.WriteObject(static_cast<u8>(sampler.IsBindless() ? 1 : 0)) != 1) { | 398 | !SaveObjectToPrecompiled(sampler.IsBindless())) { |
| 401 | return false; | 399 | return false; |
| 402 | } | 400 | } |
| 403 | } | 401 | } |
| 404 | 402 | ||
| 405 | if (file.WriteObject(static_cast<u32>(entries.global_memory_entries.size())) != 1) | 403 | if (!SaveObjectToPrecompiled(static_cast<u32>(entries.global_memory_entries.size()))) { |
| 406 | return false; | 404 | return false; |
| 405 | } | ||
| 407 | for (const auto& gmem : entries.global_memory_entries) { | 406 | for (const auto& gmem : entries.global_memory_entries) { |
| 408 | if (file.WriteObject(static_cast<u32>(gmem.GetCbufIndex())) != 1 || | 407 | if (!SaveObjectToPrecompiled(static_cast<u32>(gmem.GetCbufIndex())) || |
| 409 | file.WriteObject(static_cast<u32>(gmem.GetCbufOffset())) != 1 || | 408 | !SaveObjectToPrecompiled(static_cast<u32>(gmem.GetCbufOffset())) || |
| 410 | file.WriteObject(static_cast<u8>(gmem.IsRead() ? 1 : 0)) != 1 || | 409 | !SaveObjectToPrecompiled(gmem.IsRead()) || !SaveObjectToPrecompiled(gmem.IsWritten())) { |
| 411 | file.WriteObject(static_cast<u8>(gmem.IsWritten() ? 1 : 0)) != 1) { | ||
| 412 | return false; | 410 | return false; |
| 413 | } | 411 | } |
| 414 | } | 412 | } |
| 415 | 413 | ||
| 416 | for (const bool clip_distance : entries.clip_distances) { | 414 | for (const bool clip_distance : entries.clip_distances) { |
| 417 | if (file.WriteObject(static_cast<u8>(clip_distance ? 1 : 0)) != 1) | 415 | if (!SaveObjectToPrecompiled(clip_distance)) { |
| 418 | return false; | 416 | return false; |
| 417 | } | ||
| 419 | } | 418 | } |
| 420 | 419 | ||
| 421 | return file.WriteObject(static_cast<u64>(entries.shader_length)) == 1; | 420 | if (!SaveObjectToPrecompiled(static_cast<u64>(entries.shader_length))) { |
| 421 | return false; | ||
| 422 | } | ||
| 423 | |||
| 424 | return true; | ||
| 422 | } | 425 | } |
| 423 | 426 | ||
| 424 | void ShaderDiskCacheOpenGL::InvalidateTransferable() const { | 427 | void ShaderDiskCacheOpenGL::InvalidateTransferable() { |
| 425 | if (!FileUtil::Delete(GetTransferablePath())) { | 428 | if (!FileUtil::Delete(GetTransferablePath())) { |
| 426 | LOG_ERROR(Render_OpenGL, "Failed to invalidate transferable file={}", | 429 | LOG_ERROR(Render_OpenGL, "Failed to invalidate transferable file={}", |
| 427 | GetTransferablePath()); | 430 | GetTransferablePath()); |
| @@ -429,7 +432,10 @@ void ShaderDiskCacheOpenGL::InvalidateTransferable() const { | |||
| 429 | InvalidatePrecompiled(); | 432 | InvalidatePrecompiled(); |
| 430 | } | 433 | } |
| 431 | 434 | ||
| 432 | void ShaderDiskCacheOpenGL::InvalidatePrecompiled() const { | 435 | void ShaderDiskCacheOpenGL::InvalidatePrecompiled() { |
| 436 | // Clear virtaul precompiled cache file | ||
| 437 | precompiled_cache_virtual_file.Resize(0); | ||
| 438 | |||
| 433 | if (!FileUtil::Delete(GetPrecompiledPath())) { | 439 | if (!FileUtil::Delete(GetPrecompiledPath())) { |
| 434 | LOG_ERROR(Render_OpenGL, "Failed to invalidate precompiled file={}", GetPrecompiledPath()); | 440 | LOG_ERROR(Render_OpenGL, "Failed to invalidate precompiled file={}", GetPrecompiledPath()); |
| 435 | } | 441 | } |
| @@ -465,7 +471,10 @@ void ShaderDiskCacheOpenGL::SaveUsage(const ShaderDiskCacheUsage& usage) { | |||
| 465 | ASSERT_MSG(it != transferable.end(), "Saving shader usage without storing raw previously"); | 471 | ASSERT_MSG(it != transferable.end(), "Saving shader usage without storing raw previously"); |
| 466 | 472 | ||
| 467 | auto& usages{it->second}; | 473 | auto& usages{it->second}; |
| 468 | ASSERT(usages.find(usage) == usages.end()); | 474 | if (usages.find(usage) != usages.end()) { |
| 475 | // Skip this variant since the shader is already stored. | ||
| 476 | return; | ||
| 477 | } | ||
| 469 | usages.insert(usage); | 478 | usages.insert(usage); |
| 470 | 479 | ||
| 471 | FileUtil::IOFile file = AppendTransferableFile(); | 480 | FileUtil::IOFile file = AppendTransferableFile(); |
| @@ -485,22 +494,13 @@ void ShaderDiskCacheOpenGL::SaveDecompiled(u64 unique_identifier, const std::str | |||
| 485 | if (!IsUsable()) | 494 | if (!IsUsable()) |
| 486 | return; | 495 | return; |
| 487 | 496 | ||
| 488 | const std::vector<u8> compressed_code{Common::Compression::CompressDataZSTDDefault( | 497 | if (precompiled_cache_virtual_file.GetSize() == 0) { |
| 489 | reinterpret_cast<const u8*>(code.data()), code.size())}; | 498 | SavePrecompiledHeaderToVirtualPrecompiledCache(); |
| 490 | if (compressed_code.empty()) { | ||
| 491 | LOG_ERROR(Render_OpenGL, "Failed to compress GLSL code - skipping shader {:016x}", | ||
| 492 | unique_identifier); | ||
| 493 | return; | ||
| 494 | } | 499 | } |
| 495 | 500 | ||
| 496 | FileUtil::IOFile file = AppendPrecompiledFile(); | 501 | if (!SaveDecompiledFile(unique_identifier, code, entries)) { |
| 497 | if (!file.IsOpen()) | ||
| 498 | return; | ||
| 499 | |||
| 500 | if (!SaveDecompiledFile(file, unique_identifier, code, compressed_code, entries)) { | ||
| 501 | LOG_ERROR(Render_OpenGL, | 502 | LOG_ERROR(Render_OpenGL, |
| 502 | "Failed to save decompiled entry to the precompiled file - removing"); | 503 | "Failed to save decompiled entry to the precompiled file - removing"); |
| 503 | file.Close(); | ||
| 504 | InvalidatePrecompiled(); | 504 | InvalidatePrecompiled(); |
| 505 | } | 505 | } |
| 506 | } | 506 | } |
| @@ -516,28 +516,13 @@ void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint p | |||
| 516 | std::vector<u8> binary(binary_length); | 516 | std::vector<u8> binary(binary_length); |
| 517 | glGetProgramBinary(program, binary_length, nullptr, &binary_format, binary.data()); | 517 | glGetProgramBinary(program, binary_length, nullptr, &binary_format, binary.data()); |
| 518 | 518 | ||
| 519 | const std::vector<u8> compressed_binary = | 519 | if (!SaveObjectToPrecompiled(static_cast<u32>(PrecompiledEntryKind::Dump)) || |
| 520 | Common::Compression::CompressDataZSTDDefault(binary.data(), binary.size()); | 520 | !SaveObjectToPrecompiled(usage) || |
| 521 | 521 | !SaveObjectToPrecompiled(static_cast<u32>(binary_format)) || | |
| 522 | if (compressed_binary.empty()) { | 522 | !SaveObjectToPrecompiled(static_cast<u32>(binary_length)) || |
| 523 | LOG_ERROR(Render_OpenGL, "Failed to compress binary program in shader={:016x}", | 523 | !SaveArrayToPrecompiled(binary.data(), binary.size())) { |
| 524 | usage.unique_identifier); | ||
| 525 | return; | ||
| 526 | } | ||
| 527 | |||
| 528 | FileUtil::IOFile file = AppendPrecompiledFile(); | ||
| 529 | if (!file.IsOpen()) | ||
| 530 | return; | ||
| 531 | |||
| 532 | if (file.WriteObject(static_cast<u32>(PrecompiledEntryKind::Dump)) != 1 || | ||
| 533 | file.WriteObject(usage) != 1 || file.WriteObject(static_cast<u32>(binary_format)) != 1 || | ||
| 534 | file.WriteObject(static_cast<u32>(binary_length)) != 1 || | ||
| 535 | file.WriteObject(static_cast<u32>(compressed_binary.size())) != 1 || | ||
| 536 | file.WriteArray(compressed_binary.data(), compressed_binary.size()) != | ||
| 537 | compressed_binary.size()) { | ||
| 538 | LOG_ERROR(Render_OpenGL, "Failed to save binary program file in shader={:016x} - removing", | 524 | LOG_ERROR(Render_OpenGL, "Failed to save binary program file in shader={:016x} - removing", |
| 539 | usage.unique_identifier); | 525 | usage.unique_identifier); |
| 540 | file.Close(); | ||
| 541 | InvalidatePrecompiled(); | 526 | InvalidatePrecompiled(); |
| 542 | return; | 527 | return; |
| 543 | } | 528 | } |
| @@ -570,28 +555,33 @@ FileUtil::IOFile ShaderDiskCacheOpenGL::AppendTransferableFile() const { | |||
| 570 | return file; | 555 | return file; |
| 571 | } | 556 | } |
| 572 | 557 | ||
| 573 | FileUtil::IOFile ShaderDiskCacheOpenGL::AppendPrecompiledFile() const { | 558 | void ShaderDiskCacheOpenGL::SavePrecompiledHeaderToVirtualPrecompiledCache() { |
| 574 | if (!EnsureDirectories()) | 559 | const auto hash{GetShaderCacheVersionHash()}; |
| 575 | return {}; | 560 | if (!SaveArrayToPrecompiled(hash.data(), hash.size())) { |
| 561 | LOG_ERROR( | ||
| 562 | Render_OpenGL, | ||
| 563 | "Failed to write precompiled cache version hash to virtual precompiled cache file"); | ||
| 564 | } | ||
| 565 | } | ||
| 566 | |||
| 567 | void ShaderDiskCacheOpenGL::SaveVirtualPrecompiledFile() { | ||
| 568 | precompiled_cache_virtual_file_offset = 0; | ||
| 569 | const std::vector<u8>& uncompressed = precompiled_cache_virtual_file.ReadAllBytes(); | ||
| 570 | const std::vector<u8>& compressed = | ||
| 571 | Common::Compression::CompressDataZSTDDefault(uncompressed.data(), uncompressed.size()); | ||
| 576 | 572 | ||
| 577 | const auto precompiled_path{GetPrecompiledPath()}; | 573 | const auto precompiled_path{GetPrecompiledPath()}; |
| 578 | const bool existed = FileUtil::Exists(precompiled_path); | 574 | FileUtil::IOFile file(precompiled_path, "wb"); |
| 579 | 575 | ||
| 580 | FileUtil::IOFile file(precompiled_path, "ab"); | ||
| 581 | if (!file.IsOpen()) { | 576 | if (!file.IsOpen()) { |
| 582 | LOG_ERROR(Render_OpenGL, "Failed to open precompiled cache in path={}", precompiled_path); | 577 | LOG_ERROR(Render_OpenGL, "Failed to open precompiled cache in path={}", precompiled_path); |
| 583 | return {}; | 578 | return; |
| 584 | } | 579 | } |
| 585 | 580 | if (file.WriteBytes(compressed.data(), compressed.size()) != compressed.size()) { | |
| 586 | if (!existed || file.GetSize() == 0) { | 581 | LOG_ERROR(Render_OpenGL, "Failed to write precompiled cache version in path={}", |
| 587 | const auto hash{GetShaderCacheVersionHash()}; | 582 | precompiled_path); |
| 588 | if (file.WriteArray(hash.data(), hash.size()) != hash.size()) { | 583 | return; |
| 589 | LOG_ERROR(Render_OpenGL, "Failed to write precompiled cache version hash in path={}", | ||
| 590 | precompiled_path); | ||
| 591 | return {}; | ||
| 592 | } | ||
| 593 | } | 584 | } |
| 594 | return file; | ||
| 595 | } | 585 | } |
| 596 | 586 | ||
| 597 | bool ShaderDiskCacheOpenGL::EnsureDirectories() const { | 587 | bool ShaderDiskCacheOpenGL::EnsureDirectories() const { |
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 6be0c0547..ecd72ba58 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.h | |||
| @@ -16,6 +16,7 @@ | |||
| 16 | 16 | ||
| 17 | #include "common/assert.h" | 17 | #include "common/assert.h" |
| 18 | #include "common/common_types.h" | 18 | #include "common/common_types.h" |
| 19 | #include "core/file_sys/vfs_vector.h" | ||
| 19 | #include "video_core/engines/maxwell_3d.h" | 20 | #include "video_core/engines/maxwell_3d.h" |
| 20 | #include "video_core/renderer_opengl/gl_shader_gen.h" | 21 | #include "video_core/renderer_opengl/gl_shader_gen.h" |
| 21 | 22 | ||
| @@ -32,6 +33,11 @@ namespace OpenGL { | |||
| 32 | using ProgramCode = std::vector<u64>; | 33 | using ProgramCode = std::vector<u64>; |
| 33 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; | 34 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; |
| 34 | 35 | ||
| 36 | struct ShaderDiskCacheUsage; | ||
| 37 | struct ShaderDiskCacheDump; | ||
| 38 | |||
| 39 | using ShaderDumpsMap = std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>; | ||
| 40 | |||
| 35 | /// Allocated bindings used by an OpenGL shader program | 41 | /// Allocated bindings used by an OpenGL shader program |
| 36 | struct BaseBindings { | 42 | struct BaseBindings { |
| 37 | u32 cbuf{}; | 43 | u32 cbuf{}; |
| @@ -69,14 +75,14 @@ namespace std { | |||
| 69 | 75 | ||
| 70 | template <> | 76 | template <> |
| 71 | struct hash<OpenGL::BaseBindings> { | 77 | struct hash<OpenGL::BaseBindings> { |
| 72 | std::size_t operator()(const OpenGL::BaseBindings& bindings) const { | 78 | std::size_t operator()(const OpenGL::BaseBindings& bindings) const noexcept { |
| 73 | return bindings.cbuf | bindings.gmem << 8 | bindings.sampler << 16; | 79 | return bindings.cbuf | bindings.gmem << 8 | bindings.sampler << 16; |
| 74 | } | 80 | } |
| 75 | }; | 81 | }; |
| 76 | 82 | ||
| 77 | template <> | 83 | template <> |
| 78 | struct hash<OpenGL::ShaderDiskCacheUsage> { | 84 | struct hash<OpenGL::ShaderDiskCacheUsage> { |
| 79 | std::size_t operator()(const OpenGL::ShaderDiskCacheUsage& usage) const { | 85 | std::size_t operator()(const OpenGL::ShaderDiskCacheUsage& usage) const noexcept { |
| 80 | return static_cast<std::size_t>(usage.unique_identifier) ^ | 86 | return static_cast<std::size_t>(usage.unique_identifier) ^ |
| 81 | std::hash<OpenGL::BaseBindings>()(usage.bindings) ^ usage.primitive << 16; | 87 | std::hash<OpenGL::BaseBindings>()(usage.bindings) ^ usage.primitive << 16; |
| 82 | } | 88 | } |
| @@ -161,6 +167,7 @@ struct ShaderDiskCacheDump { | |||
| 161 | class ShaderDiskCacheOpenGL { | 167 | class ShaderDiskCacheOpenGL { |
| 162 | public: | 168 | public: |
| 163 | explicit ShaderDiskCacheOpenGL(Core::System& system); | 169 | explicit ShaderDiskCacheOpenGL(Core::System& system); |
| 170 | ~ShaderDiskCacheOpenGL(); | ||
| 164 | 171 | ||
| 165 | /// Loads transferable cache. If file has a old version or on failure, it deletes the file. | 172 | /// Loads transferable cache. If file has a old version or on failure, it deletes the file. |
| 166 | std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>> | 173 | std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>> |
| @@ -172,10 +179,10 @@ public: | |||
| 172 | LoadPrecompiled(); | 179 | LoadPrecompiled(); |
| 173 | 180 | ||
| 174 | /// Removes the transferable (and precompiled) cache file. | 181 | /// Removes the transferable (and precompiled) cache file. |
| 175 | void InvalidateTransferable() const; | 182 | void InvalidateTransferable(); |
| 176 | 183 | ||
| 177 | /// Removes the precompiled cache file. | 184 | /// Removes the precompiled cache file and clears virtual precompiled cache file. |
| 178 | void InvalidatePrecompiled() const; | 185 | void InvalidatePrecompiled(); |
| 179 | 186 | ||
| 180 | /// Saves a raw dump to the transferable file. Checks for collisions. | 187 | /// Saves a raw dump to the transferable file. Checks for collisions. |
| 181 | void SaveRaw(const ShaderDiskCacheRaw& entry); | 188 | void SaveRaw(const ShaderDiskCacheRaw& entry); |
| @@ -190,18 +197,21 @@ public: | |||
| 190 | /// Saves a dump entry to the precompiled file. Does not check for collisions. | 197 | /// Saves a dump entry to the precompiled file. Does not check for collisions. |
| 191 | void SaveDump(const ShaderDiskCacheUsage& usage, GLuint program); | 198 | void SaveDump(const ShaderDiskCacheUsage& usage, GLuint program); |
| 192 | 199 | ||
| 200 | /// Serializes virtual precompiled shader cache file to real file | ||
| 201 | void SaveVirtualPrecompiledFile(); | ||
| 202 | |||
| 193 | private: | 203 | private: |
| 194 | /// Loads the transferable cache. Returns empty on failure. | 204 | /// Loads the transferable cache. Returns empty on failure. |
| 195 | std::optional<std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, | 205 | std::optional<std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, |
| 196 | std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>> | 206 | std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>> |
| 197 | LoadPrecompiledFile(FileUtil::IOFile& file); | 207 | LoadPrecompiledFile(FileUtil::IOFile& file); |
| 198 | 208 | ||
| 199 | /// Loads a decompiled cache entry from the passed file. Returns empty on failure. | 209 | /// Loads a decompiled cache entry from m_precompiled_cache_virtual_file. Returns empty on |
| 200 | std::optional<ShaderDiskCacheDecompiled> LoadDecompiledEntry(FileUtil::IOFile& file); | 210 | /// failure. |
| 211 | std::optional<ShaderDiskCacheDecompiled> LoadDecompiledEntry(); | ||
| 201 | 212 | ||
| 202 | /// Saves a decompiled entry to the passed file. Returns true on success. | 213 | /// Saves a decompiled entry to the passed file. Returns true on success. |
| 203 | bool SaveDecompiledFile(FileUtil::IOFile& file, u64 unique_identifier, const std::string& code, | 214 | bool SaveDecompiledFile(u64 unique_identifier, const std::string& code, |
| 204 | const std::vector<u8>& compressed_code, | ||
| 205 | const GLShader::ShaderEntries& entries); | 215 | const GLShader::ShaderEntries& entries); |
| 206 | 216 | ||
| 207 | /// Returns if the cache can be used | 217 | /// Returns if the cache can be used |
| @@ -210,8 +220,8 @@ private: | |||
| 210 | /// Opens current game's transferable file and write it's header if it doesn't exist | 220 | /// Opens current game's transferable file and write it's header if it doesn't exist |
| 211 | FileUtil::IOFile AppendTransferableFile() const; | 221 | FileUtil::IOFile AppendTransferableFile() const; |
| 212 | 222 | ||
| 213 | /// Opens current game's precompiled file and write it's header if it doesn't exist | 223 | /// Save precompiled header to precompiled_cache_in_memory |
| 214 | FileUtil::IOFile AppendPrecompiledFile() const; | 224 | void SavePrecompiledHeaderToVirtualPrecompiledCache(); |
| 215 | 225 | ||
| 216 | /// Create shader disk cache directories. Returns true on success. | 226 | /// Create shader disk cache directories. Returns true on success. |
| 217 | bool EnsureDirectories() const; | 227 | bool EnsureDirectories() const; |
| @@ -234,12 +244,59 @@ private: | |||
| 234 | /// Get current game's title id | 244 | /// Get current game's title id |
| 235 | std::string GetTitleID() const; | 245 | std::string GetTitleID() const; |
| 236 | 246 | ||
| 237 | // Copre system | 247 | template <typename T> |
| 248 | bool SaveArrayToPrecompiled(const T* data, std::size_t length) { | ||
| 249 | const std::size_t write_length = precompiled_cache_virtual_file.WriteArray( | ||
| 250 | data, length, precompiled_cache_virtual_file_offset); | ||
| 251 | precompiled_cache_virtual_file_offset += write_length; | ||
| 252 | return write_length == sizeof(T) * length; | ||
| 253 | } | ||
| 254 | |||
| 255 | template <typename T> | ||
| 256 | bool LoadArrayFromPrecompiled(T* data, std::size_t length) { | ||
| 257 | const std::size_t read_length = precompiled_cache_virtual_file.ReadArray( | ||
| 258 | data, length, precompiled_cache_virtual_file_offset); | ||
| 259 | precompiled_cache_virtual_file_offset += read_length; | ||
| 260 | return read_length == sizeof(T) * length; | ||
| 261 | } | ||
| 262 | |||
| 263 | template <typename T> | ||
| 264 | bool SaveObjectToPrecompiled(const T& object) { | ||
| 265 | return SaveArrayToPrecompiled(&object, 1); | ||
| 266 | } | ||
| 267 | |||
| 268 | bool SaveObjectToPrecompiled(bool object) { | ||
| 269 | const auto value = static_cast<u8>(object); | ||
| 270 | return SaveArrayToPrecompiled(&value, 1); | ||
| 271 | } | ||
| 272 | |||
| 273 | template <typename T> | ||
| 274 | bool LoadObjectFromPrecompiled(T& object) { | ||
| 275 | return LoadArrayFromPrecompiled(&object, 1); | ||
| 276 | } | ||
| 277 | |||
| 278 | bool LoadObjectFromPrecompiled(bool& object) { | ||
| 279 | u8 value; | ||
| 280 | const bool read_ok = LoadArrayFromPrecompiled(&value, 1); | ||
| 281 | if (!read_ok) { | ||
| 282 | return false; | ||
| 283 | } | ||
| 284 | |||
| 285 | object = value != 0; | ||
| 286 | return true; | ||
| 287 | } | ||
| 288 | |||
| 289 | // Core system | ||
| 238 | Core::System& system; | 290 | Core::System& system; |
| 239 | // Stored transferable shaders | 291 | // Stored transferable shaders |
| 240 | std::map<u64, std::unordered_set<ShaderDiskCacheUsage>> transferable; | 292 | std::map<u64, std::unordered_set<ShaderDiskCacheUsage>> transferable; |
| 293 | // Stores whole precompiled cache which will be read from/saved to the precompiled cache file | ||
| 294 | FileSys::VectorVfsFile precompiled_cache_virtual_file; | ||
| 295 | // Stores the current offset of the precompiled cache file for IO purposes | ||
| 296 | std::size_t precompiled_cache_virtual_file_offset = 0; | ||
| 297 | |||
| 241 | // The cache has been loaded at boot | 298 | // The cache has been loaded at boot |
| 242 | bool tried_to_load{}; | 299 | bool tried_to_load{}; |
| 243 | }; | 300 | }; |
| 244 | 301 | ||
| 245 | } // namespace OpenGL \ No newline at end of file | 302 | } // 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 6abf948f8..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}; | |||
| 19 | ProgramResult GenerateVertexShader(const Device& device, const ShaderSetup& setup) { | 19 | ProgramResult GenerateVertexShader(const Device& device, const ShaderSetup& setup) { |
| 20 | const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); | 20 | const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); |
| 21 | 21 | ||
| 22 | std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n"; | 22 | std::string out = "// Shader Unique Id: VS" + id + "\n\n"; |
| 23 | out += "// Shader Unique Id: VS" + id + "\n\n"; | ||
| 24 | out += GetCommonDeclarations(); | 23 | out += GetCommonDeclarations(); |
| 25 | 24 | ||
| 26 | out += R"( | 25 | out += R"( |
| @@ -33,14 +32,14 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform vs_config { | |||
| 33 | }; | 32 | }; |
| 34 | 33 | ||
| 35 | )"; | 34 | )"; |
| 36 | ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET); | 35 | const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET); |
| 37 | ProgramResult program = | 36 | ProgramResult program = |
| 38 | Decompile(device, program_ir, Maxwell3D::Regs::ShaderStage::Vertex, "vertex"); | 37 | Decompile(device, program_ir, Maxwell3D::Regs::ShaderStage::Vertex, "vertex"); |
| 39 | 38 | ||
| 40 | out += program.first; | 39 | out += program.first; |
| 41 | 40 | ||
| 42 | if (setup.IsDualProgram()) { | 41 | if (setup.IsDualProgram()) { |
| 43 | ShaderIR program_ir_b(setup.program.code_b, PROGRAM_OFFSET); | 42 | const ShaderIR program_ir_b(setup.program.code_b, PROGRAM_OFFSET); |
| 44 | ProgramResult program_b = | 43 | ProgramResult program_b = |
| 45 | Decompile(device, program_ir_b, Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b"); | 44 | Decompile(device, program_ir_b, Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b"); |
| 46 | 45 | ||
| @@ -76,14 +75,13 @@ void main() { | |||
| 76 | } | 75 | } |
| 77 | })"; | 76 | })"; |
| 78 | 77 | ||
| 79 | return {out, program.second}; | 78 | return {std::move(out), std::move(program.second)}; |
| 80 | } | 79 | } |
| 81 | 80 | ||
| 82 | ProgramResult GenerateGeometryShader(const Device& device, const ShaderSetup& setup) { | 81 | ProgramResult GenerateGeometryShader(const Device& device, const ShaderSetup& setup) { |
| 83 | const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); | 82 | const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); |
| 84 | 83 | ||
| 85 | std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n"; | 84 | std::string out = "// Shader Unique Id: GS" + id + "\n\n"; |
| 86 | out += "// Shader Unique Id: GS" + id + "\n\n"; | ||
| 87 | out += GetCommonDeclarations(); | 85 | out += GetCommonDeclarations(); |
| 88 | 86 | ||
| 89 | out += R"( | 87 | out += R"( |
| @@ -97,7 +95,7 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform gs_config { | |||
| 97 | }; | 95 | }; |
| 98 | 96 | ||
| 99 | )"; | 97 | )"; |
| 100 | ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET); | 98 | const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET); |
| 101 | ProgramResult program = | 99 | ProgramResult program = |
| 102 | Decompile(device, program_ir, Maxwell3D::Regs::ShaderStage::Geometry, "geometry"); | 100 | Decompile(device, program_ir, Maxwell3D::Regs::ShaderStage::Geometry, "geometry"); |
| 103 | out += program.first; | 101 | out += program.first; |
| @@ -107,14 +105,13 @@ void main() { | |||
| 107 | execute_geometry(); | 105 | execute_geometry(); |
| 108 | };)"; | 106 | };)"; |
| 109 | 107 | ||
| 110 | return {out, program.second}; | 108 | return {std::move(out), std::move(program.second)}; |
| 111 | } | 109 | } |
| 112 | 110 | ||
| 113 | ProgramResult GenerateFragmentShader(const Device& device, const ShaderSetup& setup) { | 111 | ProgramResult GenerateFragmentShader(const Device& device, const ShaderSetup& setup) { |
| 114 | const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); | 112 | const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); |
| 115 | 113 | ||
| 116 | std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n"; | 114 | std::string out = "// Shader Unique Id: FS" + id + "\n\n"; |
| 117 | out += "// Shader Unique Id: FS" + id + "\n\n"; | ||
| 118 | out += GetCommonDeclarations(); | 115 | out += GetCommonDeclarations(); |
| 119 | 116 | ||
| 120 | out += R"( | 117 | out += R"( |
| @@ -160,7 +157,7 @@ bool AlphaFunc(in float value) { | |||
| 160 | } | 157 | } |
| 161 | 158 | ||
| 162 | )"; | 159 | )"; |
| 163 | ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET); | 160 | const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET); |
| 164 | ProgramResult program = | 161 | ProgramResult program = |
| 165 | Decompile(device, program_ir, Maxwell3D::Regs::ShaderStage::Fragment, "fragment"); | 162 | Decompile(device, program_ir, Maxwell3D::Regs::ShaderStage::Fragment, "fragment"); |
| 166 | 163 | ||
| @@ -172,7 +169,7 @@ void main() { | |||
| 172 | } | 169 | } |
| 173 | 170 | ||
| 174 | )"; | 171 | )"; |
| 175 | return {out, program.second}; | 172 | return {std::move(out), std::move(program.second)}; |
| 176 | } | 173 | } |
| 177 | 174 | ||
| 178 | } // namespace OpenGL::GLShader | 175 | } // namespace OpenGL::GLShader |
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index a8833c06e..ed7b5cff0 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h | |||
| @@ -27,8 +27,7 @@ using Maxwell = Tegra::Engines::Maxwell3D::Regs; | |||
| 27 | inline GLenum VertexType(Maxwell::VertexAttribute attrib) { | 27 | inline GLenum VertexType(Maxwell::VertexAttribute attrib) { |
| 28 | switch (attrib.type) { | 28 | switch (attrib.type) { |
| 29 | case Maxwell::VertexAttribute::Type::UnsignedInt: | 29 | case Maxwell::VertexAttribute::Type::UnsignedInt: |
| 30 | case Maxwell::VertexAttribute::Type::UnsignedNorm: { | 30 | case Maxwell::VertexAttribute::Type::UnsignedNorm: |
| 31 | |||
| 32 | switch (attrib.size) { | 31 | switch (attrib.size) { |
| 33 | case Maxwell::VertexAttribute::Size::Size_8: | 32 | case Maxwell::VertexAttribute::Size::Size_8: |
| 34 | case Maxwell::VertexAttribute::Size::Size_8_8: | 33 | case Maxwell::VertexAttribute::Size::Size_8_8: |
| @@ -47,16 +46,13 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) { | |||
| 47 | return GL_UNSIGNED_INT; | 46 | return GL_UNSIGNED_INT; |
| 48 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: | 47 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: |
| 49 | return GL_UNSIGNED_INT_2_10_10_10_REV; | 48 | return GL_UNSIGNED_INT_2_10_10_10_REV; |
| 49 | default: | ||
| 50 | LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); | ||
| 51 | UNREACHABLE(); | ||
| 52 | return {}; | ||
| 50 | } | 53 | } |
| 51 | |||
| 52 | LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); | ||
| 53 | UNREACHABLE(); | ||
| 54 | return {}; | ||
| 55 | } | ||
| 56 | |||
| 57 | case Maxwell::VertexAttribute::Type::SignedInt: | 54 | case Maxwell::VertexAttribute::Type::SignedInt: |
| 58 | case Maxwell::VertexAttribute::Type::SignedNorm: { | 55 | case Maxwell::VertexAttribute::Type::SignedNorm: |
| 59 | |||
| 60 | switch (attrib.size) { | 56 | switch (attrib.size) { |
| 61 | case Maxwell::VertexAttribute::Size::Size_8: | 57 | case Maxwell::VertexAttribute::Size::Size_8: |
| 62 | case Maxwell::VertexAttribute::Size::Size_8_8: | 58 | case Maxwell::VertexAttribute::Size::Size_8_8: |
| @@ -75,14 +71,12 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) { | |||
| 75 | return GL_INT; | 71 | return GL_INT; |
| 76 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: | 72 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: |
| 77 | return GL_INT_2_10_10_10_REV; | 73 | return GL_INT_2_10_10_10_REV; |
| 74 | default: | ||
| 75 | LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); | ||
| 76 | UNREACHABLE(); | ||
| 77 | return {}; | ||
| 78 | } | 78 | } |
| 79 | 79 | case Maxwell::VertexAttribute::Type::Float: | |
| 80 | LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); | ||
| 81 | UNREACHABLE(); | ||
| 82 | return {}; | ||
| 83 | } | ||
| 84 | |||
| 85 | case Maxwell::VertexAttribute::Type::Float: { | ||
| 86 | switch (attrib.size) { | 80 | switch (attrib.size) { |
| 87 | case Maxwell::VertexAttribute::Size::Size_16: | 81 | case Maxwell::VertexAttribute::Size::Size_16: |
| 88 | case Maxwell::VertexAttribute::Size::Size_16_16: | 82 | case Maxwell::VertexAttribute::Size::Size_16_16: |
| @@ -94,13 +88,16 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) { | |||
| 94 | case Maxwell::VertexAttribute::Size::Size_32_32_32: | 88 | case Maxwell::VertexAttribute::Size::Size_32_32_32: |
| 95 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: | 89 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: |
| 96 | return GL_FLOAT; | 90 | return GL_FLOAT; |
| 91 | default: | ||
| 92 | LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); | ||
| 93 | UNREACHABLE(); | ||
| 94 | return {}; | ||
| 97 | } | 95 | } |
| 96 | default: | ||
| 97 | LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString()); | ||
| 98 | UNREACHABLE(); | ||
| 99 | return {}; | ||
| 98 | } | 100 | } |
| 99 | } | ||
| 100 | |||
| 101 | LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString()); | ||
| 102 | UNREACHABLE(); | ||
| 103 | return {}; | ||
| 104 | } | 101 | } |
| 105 | 102 | ||
| 106 | inline GLenum IndexFormat(Maxwell::IndexFormat index_format) { | 103 | inline GLenum IndexFormat(Maxwell::IndexFormat index_format) { |
| @@ -129,10 +126,13 @@ inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) { | |||
| 129 | return GL_TRIANGLES; | 126 | return GL_TRIANGLES; |
| 130 | case Maxwell::PrimitiveTopology::TriangleStrip: | 127 | case Maxwell::PrimitiveTopology::TriangleStrip: |
| 131 | return GL_TRIANGLE_STRIP; | 128 | return GL_TRIANGLE_STRIP; |
| 129 | case Maxwell::PrimitiveTopology::TriangleFan: | ||
| 130 | return GL_TRIANGLE_FAN; | ||
| 131 | default: | ||
| 132 | LOG_CRITICAL(Render_OpenGL, "Unimplemented topology={}", static_cast<u32>(topology)); | ||
| 133 | UNREACHABLE(); | ||
| 134 | return {}; | ||
| 132 | } | 135 | } |
| 133 | LOG_CRITICAL(Render_OpenGL, "Unimplemented topology={}", static_cast<u32>(topology)); | ||
| 134 | UNREACHABLE(); | ||
| 135 | return {}; | ||
| 136 | } | 136 | } |
| 137 | 137 | ||
| 138 | inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode, | 138 | inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode, |
| @@ -186,9 +186,10 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) { | |||
| 186 | } else { | 186 | } else { |
| 187 | return GL_MIRROR_CLAMP_TO_EDGE; | 187 | return GL_MIRROR_CLAMP_TO_EDGE; |
| 188 | } | 188 | } |
| 189 | default: | ||
| 190 | LOG_ERROR(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode)); | ||
| 191 | return GL_REPEAT; | ||
| 189 | } | 192 | } |
| 190 | LOG_ERROR(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode)); | ||
| 191 | return GL_REPEAT; | ||
| 192 | } | 193 | } |
| 193 | 194 | ||
| 194 | inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) { | 195 | inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) { |
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 | |||
| 97 | return matrix; | 97 | return matrix; |
| 98 | } | 98 | } |
| 99 | 99 | ||
| 100 | RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& window, Core::System& system) | 100 | RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system) |
| 101 | : VideoCore::RendererBase{window}, system{system} {} | 101 | : VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system} {} |
| 102 | 102 | ||
| 103 | RendererOpenGL::~RendererOpenGL() = default; | 103 | RendererOpenGL::~RendererOpenGL() = default; |
| 104 | 104 | ||
| @@ -265,7 +265,7 @@ void RendererOpenGL::CreateRasterizer() { | |||
| 265 | } | 265 | } |
| 266 | // Initialize sRGB Usage | 266 | // Initialize sRGB Usage |
| 267 | OpenGLState::ClearsRGBUsed(); | 267 | OpenGLState::ClearsRGBUsed(); |
| 268 | rasterizer = std::make_unique<RasterizerOpenGL>(system, screen_info); | 268 | rasterizer = std::make_unique<RasterizerOpenGL>(system, emu_window, screen_info); |
| 269 | } | 269 | } |
| 270 | 270 | ||
| 271 | void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, | 271 | 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 { | |||
| 45 | 45 | ||
| 46 | class RendererOpenGL : public VideoCore::RendererBase { | 46 | class RendererOpenGL : public VideoCore::RendererBase { |
| 47 | public: | 47 | public: |
| 48 | explicit RendererOpenGL(Core::Frontend::EmuWindow& window, Core::System& system); | 48 | explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system); |
| 49 | ~RendererOpenGL() override; | 49 | ~RendererOpenGL() override; |
| 50 | 50 | ||
| 51 | /// Swap buffers (render frame) | 51 | /// Swap buffers (render frame) |
| @@ -77,6 +77,7 @@ private: | |||
| 77 | void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a, | 77 | void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a, |
| 78 | const TextureInfo& texture); | 78 | const TextureInfo& texture); |
| 79 | 79 | ||
| 80 | Core::Frontend::EmuWindow& emu_window; | ||
| 80 | Core::System& system; | 81 | Core::System& system; |
| 81 | 82 | ||
| 82 | OpenGLState state; | 83 | 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 { | |||
| 38 | sizes.data()); | 38 | sizes.data()); |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string extra_info) { | 41 | void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string_view extra_info) { |
| 42 | if (!GLAD_GL_KHR_debug) { | 42 | if (!GLAD_GL_KHR_debug) { |
| 43 | return; // We don't need to throw an error as this is just for debugging | 43 | // We don't need to throw an error as this is just for debugging |
| 44 | return; | ||
| 44 | } | 45 | } |
| 45 | const std::string nice_addr = fmt::format("0x{:016x}", addr); | ||
| 46 | std::string object_label; | ||
| 47 | 46 | ||
| 47 | std::string object_label; | ||
| 48 | if (extra_info.empty()) { | 48 | if (extra_info.empty()) { |
| 49 | switch (identifier) { | 49 | switch (identifier) { |
| 50 | case GL_TEXTURE: | 50 | case GL_TEXTURE: |
| 51 | object_label = "Texture@" + nice_addr; | 51 | object_label = fmt::format("Texture@0x{:016X}", addr); |
| 52 | break; | 52 | break; |
| 53 | case GL_PROGRAM: | 53 | case GL_PROGRAM: |
| 54 | object_label = "Shader@" + nice_addr; | 54 | object_label = fmt::format("Shader@0x{:016X}", addr); |
| 55 | break; | 55 | break; |
| 56 | default: | 56 | default: |
| 57 | object_label = fmt::format("Object(0x{:x})@{}", identifier, nice_addr); | 57 | object_label = fmt::format("Object(0x{:X})@0x{:016X}", identifier, addr); |
| 58 | break; | 58 | break; |
| 59 | } | 59 | } |
| 60 | } else { | 60 | } else { |
| 61 | object_label = extra_info + '@' + nice_addr; | 61 | object_label = fmt::format("{}@0x{:016X}", extra_info, addr); |
| 62 | } | 62 | } |
| 63 | glObjectLabel(identifier, handle, -1, static_cast<const GLchar*>(object_label.c_str())); | 63 | glObjectLabel(identifier, handle, -1, static_cast<const GLchar*>(object_label.c_str())); |
| 64 | } | 64 | } |
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 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <string> | 7 | #include <string_view> |
| 8 | #include <vector> | 8 | #include <vector> |
| 9 | #include <glad/glad.h> | 9 | #include <glad/glad.h> |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| @@ -30,6 +30,6 @@ private: | |||
| 30 | std::vector<GLsizeiptr> sizes; | 30 | std::vector<GLsizeiptr> sizes; |
| 31 | }; | 31 | }; |
| 32 | 32 | ||
| 33 | void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string extra_info = ""); | 33 | void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string_view extra_info = {}); |
| 34 | 34 | ||
| 35 | } // namespace OpenGL \ No newline at end of file | 35 | } // namespace OpenGL \ No newline at end of file |
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index 34bf26ff2..9fe1e3280 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp | |||
| @@ -62,9 +62,10 @@ vk::SamplerAddressMode WrapMode(Tegra::Texture::WrapMode wrap_mode) { | |||
| 62 | case Tegra::Texture::WrapMode::MirrorOnceBorder: | 62 | case Tegra::Texture::WrapMode::MirrorOnceBorder: |
| 63 | UNIMPLEMENTED(); | 63 | UNIMPLEMENTED(); |
| 64 | return vk::SamplerAddressMode::eMirrorClampToEdge; | 64 | return vk::SamplerAddressMode::eMirrorClampToEdge; |
| 65 | default: | ||
| 66 | UNIMPLEMENTED_MSG("Unimplemented wrap mode={}", static_cast<u32>(wrap_mode)); | ||
| 67 | return {}; | ||
| 65 | } | 68 | } |
| 66 | UNIMPLEMENTED_MSG("Unimplemented wrap mode={}", static_cast<u32>(wrap_mode)); | ||
| 67 | return {}; | ||
| 68 | } | 69 | } |
| 69 | 70 | ||
| 70 | vk::CompareOp DepthCompareFunction(Tegra::Texture::DepthCompareFunc depth_compare_func) { | 71 | vk::CompareOp DepthCompareFunction(Tegra::Texture::DepthCompareFunc depth_compare_func) { |
| @@ -225,9 +226,10 @@ vk::PrimitiveTopology PrimitiveTopology(Maxwell::PrimitiveTopology topology) { | |||
| 225 | return vk::PrimitiveTopology::eTriangleList; | 226 | return vk::PrimitiveTopology::eTriangleList; |
| 226 | case Maxwell::PrimitiveTopology::TriangleStrip: | 227 | case Maxwell::PrimitiveTopology::TriangleStrip: |
| 227 | return vk::PrimitiveTopology::eTriangleStrip; | 228 | return vk::PrimitiveTopology::eTriangleStrip; |
| 229 | default: | ||
| 230 | UNIMPLEMENTED_MSG("Unimplemented topology={}", static_cast<u32>(topology)); | ||
| 231 | return {}; | ||
| 228 | } | 232 | } |
| 229 | UNIMPLEMENTED_MSG("Unimplemented topology={}", static_cast<u32>(topology)); | ||
| 230 | return {}; | ||
| 231 | } | 233 | } |
| 232 | 234 | ||
| 233 | vk::Format VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size) { | 235 | vk::Format VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size) { |
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index 08b786aad..3edf460df 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h | |||
| @@ -49,9 +49,6 @@ public: | |||
| 49 | return alignment; | 49 | return alignment; |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | // We do not have to flush this cache as things in it are never modified by us. | ||
| 53 | void Flush() override {} | ||
| 54 | |||
| 55 | private: | 52 | private: |
| 56 | VAddr cpu_addr{}; | 53 | VAddr cpu_addr{}; |
| 57 | std::size_t size{}; | 54 | std::size_t size{}; |
| @@ -87,6 +84,10 @@ public: | |||
| 87 | return buffer_handle; | 84 | return buffer_handle; |
| 88 | } | 85 | } |
| 89 | 86 | ||
| 87 | protected: | ||
| 88 | // We do not have to flush this cache as things in it are never modified by us. | ||
| 89 | void FlushObjectInner(const std::shared_ptr<CachedBufferEntry>& object) override {} | ||
| 90 | |||
| 90 | private: | 91 | private: |
| 91 | void AlignBuffer(std::size_t alignment); | 92 | void AlignBuffer(std::size_t alignment); |
| 92 | 93 | ||
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index 23d9b10db..a5b25aeff 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp | |||
| @@ -194,8 +194,8 @@ public: | |||
| 194 | for (const auto& sampler : ir.GetSamplers()) { | 194 | for (const auto& sampler : ir.GetSamplers()) { |
| 195 | entries.samplers.emplace_back(sampler); | 195 | entries.samplers.emplace_back(sampler); |
| 196 | } | 196 | } |
| 197 | for (const auto& attr : ir.GetInputAttributes()) { | 197 | for (const auto& attribute : ir.GetInputAttributes()) { |
| 198 | entries.attributes.insert(GetGenericAttributeLocation(attr.first)); | 198 | entries.attributes.insert(GetGenericAttributeLocation(attribute)); |
| 199 | } | 199 | } |
| 200 | entries.clip_distances = ir.GetClipDistances(); | 200 | entries.clip_distances = ir.GetClipDistances(); |
| 201 | entries.shader_length = ir.GetLength(); | 201 | entries.shader_length = ir.GetLength(); |
| @@ -315,15 +315,13 @@ private: | |||
| 315 | constexpr std::array<const char*, INTERNAL_FLAGS_COUNT> names = {"zero", "sign", "carry", | 315 | constexpr std::array<const char*, INTERNAL_FLAGS_COUNT> names = {"zero", "sign", "carry", |
| 316 | "overflow"}; | 316 | "overflow"}; |
| 317 | for (std::size_t flag = 0; flag < INTERNAL_FLAGS_COUNT; ++flag) { | 317 | for (std::size_t flag = 0; flag < INTERNAL_FLAGS_COUNT; ++flag) { |
| 318 | const auto flag_code = static_cast<InternalFlag>(flag); | ||
| 319 | const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false); | 318 | const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false); |
| 320 | internal_flags[flag] = AddGlobalVariable(Name(id, names[flag])); | 319 | internal_flags[flag] = AddGlobalVariable(Name(id, names[flag])); |
| 321 | } | 320 | } |
| 322 | } | 321 | } |
| 323 | 322 | ||
| 324 | void DeclareInputAttributes() { | 323 | void DeclareInputAttributes() { |
| 325 | for (const auto element : ir.GetInputAttributes()) { | 324 | for (const auto index : ir.GetInputAttributes()) { |
| 326 | const Attribute::Index index = element.first; | ||
| 327 | if (!IsGenericAttribute(index)) { | 325 | if (!IsGenericAttribute(index)) { |
| 328 | continue; | 326 | continue; |
| 329 | } | 327 | } |
| @@ -1037,6 +1035,18 @@ private: | |||
| 1037 | return {}; | 1035 | return {}; |
| 1038 | } | 1036 | } |
| 1039 | 1037 | ||
| 1038 | template <u32 element> | ||
| 1039 | Id LocalInvocationId(Operation) { | ||
| 1040 | UNIMPLEMENTED(); | ||
| 1041 | return {}; | ||
| 1042 | } | ||
| 1043 | |||
| 1044 | template <u32 element> | ||
| 1045 | Id WorkGroupId(Operation) { | ||
| 1046 | UNIMPLEMENTED(); | ||
| 1047 | return {}; | ||
| 1048 | } | ||
| 1049 | |||
| 1040 | Id DeclareBuiltIn(spv::BuiltIn builtin, spv::StorageClass storage, Id type, | 1050 | Id DeclareBuiltIn(spv::BuiltIn builtin, spv::StorageClass storage, Id type, |
| 1041 | const std::string& name) { | 1051 | const std::string& name) { |
| 1042 | const Id id = OpVariable(type, storage); | 1052 | const Id id = OpVariable(type, storage); |
| @@ -1293,6 +1303,12 @@ private: | |||
| 1293 | &SPIRVDecompiler::EndPrimitive, | 1303 | &SPIRVDecompiler::EndPrimitive, |
| 1294 | 1304 | ||
| 1295 | &SPIRVDecompiler::YNegate, | 1305 | &SPIRVDecompiler::YNegate, |
| 1306 | &SPIRVDecompiler::LocalInvocationId<0>, | ||
| 1307 | &SPIRVDecompiler::LocalInvocationId<1>, | ||
| 1308 | &SPIRVDecompiler::LocalInvocationId<2>, | ||
| 1309 | &SPIRVDecompiler::WorkGroupId<0>, | ||
| 1310 | &SPIRVDecompiler::WorkGroupId<1>, | ||
| 1311 | &SPIRVDecompiler::WorkGroupId<2>, | ||
| 1296 | }; | 1312 | }; |
| 1297 | 1313 | ||
| 1298 | const ShaderIR& ir; | 1314 | const ShaderIR& ir; |
diff --git a/src/video_core/shader/decode.cpp b/src/video_core/shader/decode.cpp index e4c438792..2da595c0d 100644 --- a/src/video_core/shader/decode.cpp +++ b/src/video_core/shader/decode.cpp | |||
| @@ -116,6 +116,8 @@ ExitMethod ShaderIR::Scan(u32 begin, u32 end, std::set<u32>& labels) { | |||
| 116 | // Continue scanning for an exit method. | 116 | // Continue scanning for an exit method. |
| 117 | break; | 117 | break; |
| 118 | } | 118 | } |
| 119 | default: | ||
| 120 | break; | ||
| 119 | } | 121 | } |
| 120 | } | 122 | } |
| 121 | return exit_method = ExitMethod::AlwaysReturn; | 123 | return exit_method = ExitMethod::AlwaysReturn; |
| @@ -206,4 +208,4 @@ u32 ShaderIR::DecodeInstr(NodeBlock& bb, u32 pc) { | |||
| 206 | return pc + 1; | 208 | return pc + 1; |
| 207 | } | 209 | } |
| 208 | 210 | ||
| 209 | } // namespace VideoCommon::Shader \ No newline at end of file | 211 | } // namespace VideoCommon::Shader |
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 @@ | |||
| 4 | 4 | ||
| 5 | #include "common/assert.h" | 5 | #include "common/assert.h" |
| 6 | #include "common/common_types.h" | 6 | #include "common/common_types.h" |
| 7 | #include "common/logging/log.h" | ||
| 7 | #include "video_core/engines/shader_bytecode.h" | 8 | #include "video_core/engines/shader_bytecode.h" |
| 8 | #include "video_core/shader/shader_ir.h" | 9 | #include "video_core/shader/shader_ir.h" |
| 9 | 10 | ||
| @@ -152,4 +153,4 @@ u32 ShaderIR::DecodeArithmetic(NodeBlock& bb, u32 pc) { | |||
| 152 | return pc; | 153 | return pc; |
| 153 | } | 154 | } |
| 154 | 155 | ||
| 155 | } // namespace VideoCommon::Shader \ No newline at end of file | 156 | } // namespace VideoCommon::Shader |
diff --git a/src/video_core/shader/decode/arithmetic_half.cpp b/src/video_core/shader/decode/arithmetic_half.cpp index 9467f9417..3a29c4a46 100644 --- a/src/video_core/shader/decode/arithmetic_half.cpp +++ b/src/video_core/shader/decode/arithmetic_half.cpp | |||
| @@ -4,11 +4,13 @@ | |||
| 4 | 4 | ||
| 5 | #include "common/assert.h" | 5 | #include "common/assert.h" |
| 6 | #include "common/common_types.h" | 6 | #include "common/common_types.h" |
| 7 | #include "common/logging/log.h" | ||
| 7 | #include "video_core/engines/shader_bytecode.h" | 8 | #include "video_core/engines/shader_bytecode.h" |
| 8 | #include "video_core/shader/shader_ir.h" | 9 | #include "video_core/shader/shader_ir.h" |
| 9 | 10 | ||
| 10 | namespace VideoCommon::Shader { | 11 | namespace VideoCommon::Shader { |
| 11 | 12 | ||
| 13 | using Tegra::Shader::HalfType; | ||
| 12 | using Tegra::Shader::Instruction; | 14 | using Tegra::Shader::Instruction; |
| 13 | using Tegra::Shader::OpCode; | 15 | using Tegra::Shader::OpCode; |
| 14 | 16 | ||
| @@ -22,7 +24,6 @@ u32 ShaderIR::DecodeArithmeticHalf(NodeBlock& bb, u32 pc) { | |||
| 22 | LOG_WARNING(HW_GPU, "{} FTZ not implemented", opcode->get().GetName()); | 24 | LOG_WARNING(HW_GPU, "{} FTZ not implemented", opcode->get().GetName()); |
| 23 | } | 25 | } |
| 24 | } | 26 | } |
| 25 | UNIMPLEMENTED_IF_MSG(instr.alu_half.saturate != 0, "Half float saturation not implemented"); | ||
| 26 | 27 | ||
| 27 | const bool negate_a = | 28 | const bool negate_a = |
| 28 | opcode->get().GetId() != OpCode::Id::HMUL2_R && instr.alu_half.negate_a != 0; | 29 | opcode->get().GetId() != OpCode::Id::HMUL2_R && instr.alu_half.negate_a != 0; |
| @@ -32,35 +33,37 @@ u32 ShaderIR::DecodeArithmeticHalf(NodeBlock& bb, u32 pc) { | |||
| 32 | Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.alu_half.type_a); | 33 | Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.alu_half.type_a); |
| 33 | op_a = GetOperandAbsNegHalf(op_a, instr.alu_half.abs_a, negate_a); | 34 | op_a = GetOperandAbsNegHalf(op_a, instr.alu_half.abs_a, negate_a); |
| 34 | 35 | ||
| 35 | Node op_b = [&]() { | 36 | auto [type_b, op_b] = [&]() -> std::tuple<HalfType, Node> { |
| 36 | switch (opcode->get().GetId()) { | 37 | switch (opcode->get().GetId()) { |
| 37 | case OpCode::Id::HADD2_C: | 38 | case OpCode::Id::HADD2_C: |
| 38 | case OpCode::Id::HMUL2_C: | 39 | case OpCode::Id::HMUL2_C: |
| 39 | return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()); | 40 | return {HalfType::F32, GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset())}; |
| 40 | case OpCode::Id::HADD2_R: | 41 | case OpCode::Id::HADD2_R: |
| 41 | case OpCode::Id::HMUL2_R: | 42 | case OpCode::Id::HMUL2_R: |
| 42 | return GetRegister(instr.gpr20); | 43 | return {instr.alu_half.type_b, GetRegister(instr.gpr20)}; |
| 43 | default: | 44 | default: |
| 44 | UNREACHABLE(); | 45 | UNREACHABLE(); |
| 45 | return Immediate(0); | 46 | return {HalfType::F32, Immediate(0)}; |
| 46 | } | 47 | } |
| 47 | }(); | 48 | }(); |
| 48 | op_b = UnpackHalfFloat(op_b, instr.alu_half.type_b); | 49 | op_b = UnpackHalfFloat(op_b, type_b); |
| 49 | op_b = GetOperandAbsNegHalf(op_b, instr.alu_half.abs_b, negate_b); | 50 | // redeclaration to avoid a bug in clang with reusing local bindings in lambdas |
| 51 | Node op_b_alt = GetOperandAbsNegHalf(op_b, instr.alu_half.abs_b, negate_b); | ||
| 50 | 52 | ||
| 51 | Node value = [&]() { | 53 | Node value = [&]() { |
| 52 | switch (opcode->get().GetId()) { | 54 | switch (opcode->get().GetId()) { |
| 53 | case OpCode::Id::HADD2_C: | 55 | case OpCode::Id::HADD2_C: |
| 54 | case OpCode::Id::HADD2_R: | 56 | case OpCode::Id::HADD2_R: |
| 55 | return Operation(OperationCode::HAdd, PRECISE, op_a, op_b); | 57 | return Operation(OperationCode::HAdd, PRECISE, op_a, op_b_alt); |
| 56 | case OpCode::Id::HMUL2_C: | 58 | case OpCode::Id::HMUL2_C: |
| 57 | case OpCode::Id::HMUL2_R: | 59 | case OpCode::Id::HMUL2_R: |
| 58 | return Operation(OperationCode::HMul, PRECISE, op_a, op_b); | 60 | return Operation(OperationCode::HMul, PRECISE, op_a, op_b_alt); |
| 59 | default: | 61 | default: |
| 60 | UNIMPLEMENTED_MSG("Unhandled half float instruction: {}", opcode->get().GetName()); | 62 | UNIMPLEMENTED_MSG("Unhandled half float instruction: {}", opcode->get().GetName()); |
| 61 | return Immediate(0); | 63 | return Immediate(0); |
| 62 | } | 64 | } |
| 63 | }(); | 65 | }(); |
| 66 | value = GetSaturatedHalfFloat(value, instr.alu_half.saturate); | ||
| 64 | value = HalfMerge(GetRegister(instr.gpr0), value, instr.alu_half.merge); | 67 | value = HalfMerge(GetRegister(instr.gpr0), value, instr.alu_half.merge); |
| 65 | 68 | ||
| 66 | SetRegister(bb, instr.gpr0, value); | 69 | SetRegister(bb, instr.gpr0, value); |
| @@ -68,4 +71,4 @@ u32 ShaderIR::DecodeArithmeticHalf(NodeBlock& bb, u32 pc) { | |||
| 68 | return pc; | 71 | return pc; |
| 69 | } | 72 | } |
| 70 | 73 | ||
| 71 | } // namespace VideoCommon::Shader \ No newline at end of file | 74 | } // namespace VideoCommon::Shader |
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 @@ | |||
| 4 | 4 | ||
| 5 | #include "common/assert.h" | 5 | #include "common/assert.h" |
| 6 | #include "common/common_types.h" | 6 | #include "common/common_types.h" |
| 7 | #include "common/logging/log.h" | ||
| 7 | #include "video_core/engines/shader_bytecode.h" | 8 | #include "video_core/engines/shader_bytecode.h" |
| 8 | #include "video_core/shader/shader_ir.h" | 9 | #include "video_core/shader/shader_ir.h" |
| 9 | 10 | ||
| @@ -47,4 +48,4 @@ u32 ShaderIR::DecodeArithmeticHalfImmediate(NodeBlock& bb, u32 pc) { | |||
| 47 | return pc; | 48 | return pc; |
| 48 | } | 49 | } |
| 49 | 50 | ||
| 50 | } // namespace VideoCommon::Shader \ No newline at end of file | 51 | } // 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) { | |||
| 49 | return pc; | 49 | return pc; |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | } // namespace VideoCommon::Shader \ No newline at end of file | 52 | } // 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 | |||
| 93 | } | 93 | } |
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | } // namespace VideoCommon::Shader \ No newline at end of file | 96 | } // 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) { | |||
| 46 | return pc; | 46 | return pc; |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | } // namespace VideoCommon::Shader \ No newline at end of file | 49 | } // 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) { | |||
| 38 | return pc; | 38 | return pc; |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | } // namespace VideoCommon::Shader \ No newline at end of file | 41 | } // namespace VideoCommon::Shader |
diff --git a/src/video_core/shader/decode/conversion.cpp b/src/video_core/shader/decode/conversion.cpp index ba15b1115..b5ec9a6f5 100644 --- a/src/video_core/shader/decode/conversion.cpp +++ b/src/video_core/shader/decode/conversion.cpp | |||
| @@ -120,10 +120,11 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) { | |||
| 120 | return Operation(OperationCode::FCeil, PRECISE, value); | 120 | return Operation(OperationCode::FCeil, PRECISE, value); |
| 121 | case Tegra::Shader::F2fRoundingOp::Trunc: | 121 | case Tegra::Shader::F2fRoundingOp::Trunc: |
| 122 | return Operation(OperationCode::FTrunc, PRECISE, value); | 122 | return Operation(OperationCode::FTrunc, PRECISE, value); |
| 123 | default: | ||
| 124 | UNIMPLEMENTED_MSG("Unimplemented F2F rounding mode {}", | ||
| 125 | static_cast<u32>(instr.conversion.f2f.rounding.Value())); | ||
| 126 | return Immediate(0); | ||
| 123 | } | 127 | } |
| 124 | UNIMPLEMENTED_MSG("Unimplemented F2F rounding mode {}", | ||
| 125 | static_cast<u32>(instr.conversion.f2f.rounding.Value())); | ||
| 126 | return Immediate(0); | ||
| 127 | }(); | 128 | }(); |
| 128 | value = GetSaturatedFloat(value, instr.alu.saturate_d); | 129 | value = GetSaturatedFloat(value, instr.alu.saturate_d); |
| 129 | 130 | ||
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) { | |||
| 56 | return pc; | 56 | return pc; |
| 57 | } | 57 | } |
| 58 | 58 | ||
| 59 | } // namespace VideoCommon::Shader \ No newline at end of file | 59 | } // 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) { | |||
| 55 | return pc; | 55 | return pc; |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | } // namespace VideoCommon::Shader \ No newline at end of file | 58 | } // 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) { | |||
| 53 | return pc; | 53 | return pc; |
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | } // namespace VideoCommon::Shader \ No newline at end of file | 56 | } // 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 @@ | |||
| 6 | 6 | ||
| 7 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "common/logging/log.h" | ||
| 9 | #include "video_core/engines/shader_bytecode.h" | 10 | #include "video_core/engines/shader_bytecode.h" |
| 10 | #include "video_core/shader/shader_ir.h" | 11 | #include "video_core/shader/shader_ir.h" |
| 11 | 12 | ||
| @@ -64,4 +65,4 @@ u32 ShaderIR::DecodeHalfSet(NodeBlock& bb, u32 pc) { | |||
| 64 | return pc; | 65 | return pc; |
| 65 | } | 66 | } |
| 66 | 67 | ||
| 67 | } // namespace VideoCommon::Shader \ No newline at end of file | 68 | } // 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) { | |||
| 59 | return pc; | 59 | return pc; |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | } // namespace VideoCommon::Shader \ No newline at end of file | 62 | } // namespace VideoCommon::Shader |
diff --git a/src/video_core/shader/decode/hfma2.cpp b/src/video_core/shader/decode/hfma2.cpp index 5c1becce5..a425f9eb7 100644 --- a/src/video_core/shader/decode/hfma2.cpp +++ b/src/video_core/shader/decode/hfma2.cpp | |||
| @@ -34,15 +34,14 @@ u32 ShaderIR::DecodeHfma2(NodeBlock& bb, u32 pc) { | |||
| 34 | case OpCode::Id::HFMA2_CR: | 34 | case OpCode::Id::HFMA2_CR: |
| 35 | neg_b = instr.hfma2.negate_b; | 35 | neg_b = instr.hfma2.negate_b; |
| 36 | neg_c = instr.hfma2.negate_c; | 36 | neg_c = instr.hfma2.negate_c; |
| 37 | return {instr.hfma2.saturate, instr.hfma2.type_b, | 37 | return {instr.hfma2.saturate, HalfType::F32, |
| 38 | GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()), | 38 | GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()), |
| 39 | instr.hfma2.type_reg39, GetRegister(instr.gpr39)}; | 39 | instr.hfma2.type_reg39, GetRegister(instr.gpr39)}; |
| 40 | case OpCode::Id::HFMA2_RC: | 40 | case OpCode::Id::HFMA2_RC: |
| 41 | neg_b = instr.hfma2.negate_b; | 41 | neg_b = instr.hfma2.negate_b; |
| 42 | neg_c = instr.hfma2.negate_c; | 42 | neg_c = instr.hfma2.negate_c; |
| 43 | return {instr.hfma2.saturate, instr.hfma2.type_reg39, GetRegister(instr.gpr39), | 43 | return {instr.hfma2.saturate, instr.hfma2.type_reg39, GetRegister(instr.gpr39), |
| 44 | instr.hfma2.type_b, | 44 | HalfType::F32, GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset())}; |
| 45 | GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset())}; | ||
| 46 | case OpCode::Id::HFMA2_RR: | 45 | case OpCode::Id::HFMA2_RR: |
| 47 | neg_b = instr.hfma2.rr.negate_b; | 46 | neg_b = instr.hfma2.rr.negate_b; |
| 48 | neg_c = instr.hfma2.rr.negate_c; | 47 | neg_c = instr.hfma2.rr.negate_c; |
| @@ -56,13 +55,13 @@ u32 ShaderIR::DecodeHfma2(NodeBlock& bb, u32 pc) { | |||
| 56 | return {false, identity, Immediate(0), identity, Immediate(0)}; | 55 | return {false, identity, Immediate(0), identity, Immediate(0)}; |
| 57 | } | 56 | } |
| 58 | }(); | 57 | }(); |
| 59 | UNIMPLEMENTED_IF_MSG(saturate, "HFMA2 saturation is not implemented"); | ||
| 60 | 58 | ||
| 61 | const Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.hfma2.type_a); | 59 | const Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.hfma2.type_a); |
| 62 | op_b = GetOperandAbsNegHalf(UnpackHalfFloat(op_b, type_b), false, neg_b); | 60 | op_b = GetOperandAbsNegHalf(UnpackHalfFloat(op_b, type_b), false, neg_b); |
| 63 | op_c = GetOperandAbsNegHalf(UnpackHalfFloat(op_c, type_c), false, neg_c); | 61 | op_c = GetOperandAbsNegHalf(UnpackHalfFloat(op_c, type_c), false, neg_c); |
| 64 | 62 | ||
| 65 | Node value = Operation(OperationCode::HFma, PRECISE, op_a, op_b, op_c); | 63 | Node value = Operation(OperationCode::HFma, PRECISE, op_a, op_b, op_c); |
| 64 | value = GetSaturatedHalfFloat(value, saturate); | ||
| 66 | value = HalfMerge(GetRegister(instr.gpr0), value, instr.hfma2.merge); | 65 | value = HalfMerge(GetRegister(instr.gpr0), value, instr.hfma2.merge); |
| 67 | 66 | ||
| 68 | SetRegister(bb, instr.gpr0, value); | 67 | SetRegister(bb, instr.gpr0, value); |
| @@ -70,4 +69,4 @@ u32 ShaderIR::DecodeHfma2(NodeBlock& bb, u32 pc) { | |||
| 70 | return pc; | 69 | return pc; |
| 71 | } | 70 | } |
| 72 | 71 | ||
| 73 | } // namespace VideoCommon::Shader \ No newline at end of file | 72 | } // 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 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/assert.h" | ||
| 6 | #include "common/common_types.h" | 5 | #include "common/common_types.h" |
| 7 | #include "video_core/engines/shader_bytecode.h" | 6 | #include "video_core/engines/shader_bytecode.h" |
| 8 | #include "video_core/shader/shader_ir.h" | 7 | #include "video_core/shader/shader_ir.h" |
| @@ -47,4 +46,4 @@ u32 ShaderIR::DecodeIntegerSet(NodeBlock& bb, u32 pc) { | |||
| 47 | return pc; | 46 | return pc; |
| 48 | } | 47 | } |
| 49 | 48 | ||
| 50 | } // namespace VideoCommon::Shader \ No newline at end of file | 49 | } // 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) { | |||
| 50 | return pc; | 50 | return pc; |
| 51 | } | 51 | } |
| 52 | 52 | ||
| 53 | } // namespace VideoCommon::Shader \ No newline at end of file | 53 | } // namespace VideoCommon::Shader |
diff --git a/src/video_core/shader/decode/memory.cpp b/src/video_core/shader/decode/memory.cpp index ea1092db1..e6a010a7d 100644 --- a/src/video_core/shader/decode/memory.cpp +++ b/src/video_core/shader/decode/memory.cpp | |||
| @@ -47,17 +47,20 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { | |||
| 47 | "Indirect attribute loads are not supported"); | 47 | "Indirect attribute loads are not supported"); |
| 48 | UNIMPLEMENTED_IF_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) != 0, | 48 | UNIMPLEMENTED_IF_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) != 0, |
| 49 | "Unaligned attribute loads are not supported"); | 49 | "Unaligned attribute loads are not supported"); |
| 50 | UNIMPLEMENTED_IF_MSG(instr.attribute.fmt20.IsPhysical() && | ||
| 51 | instr.attribute.fmt20.size != Tegra::Shader::AttributeSize::Word, | ||
| 52 | "Non-32 bits PHYS reads are not implemented"); | ||
| 50 | 53 | ||
| 51 | Tegra::Shader::IpaMode input_mode{Tegra::Shader::IpaInterpMode::Pass, | 54 | const Node buffer{GetRegister(instr.gpr39)}; |
| 52 | Tegra::Shader::IpaSampleMode::Default}; | ||
| 53 | 55 | ||
| 54 | u64 next_element = instr.attribute.fmt20.element; | 56 | u64 next_element = instr.attribute.fmt20.element; |
| 55 | auto next_index = static_cast<u64>(instr.attribute.fmt20.index.Value()); | 57 | auto next_index = static_cast<u64>(instr.attribute.fmt20.index.Value()); |
| 56 | 58 | ||
| 57 | const auto LoadNextElement = [&](u32 reg_offset) { | 59 | const auto LoadNextElement = [&](u32 reg_offset) { |
| 58 | const Node buffer = GetRegister(instr.gpr39); | 60 | const Node attribute{instr.attribute.fmt20.IsPhysical() |
| 59 | const Node attribute = GetInputAttribute(static_cast<Attribute::Index>(next_index), | 61 | ? GetPhysicalInputAttribute(instr.gpr8, buffer) |
| 60 | next_element, input_mode, buffer); | 62 | : GetInputAttribute(static_cast<Attribute::Index>(next_index), |
| 63 | next_element, buffer)}; | ||
| 61 | 64 | ||
| 62 | SetRegister(bb, instr.gpr0.Value() + reg_offset, attribute); | 65 | SetRegister(bb, instr.gpr0.Value() + reg_offset, attribute); |
| 63 | 66 | ||
| @@ -143,12 +146,25 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { | |||
| 143 | } | 146 | } |
| 144 | break; | 147 | break; |
| 145 | } | 148 | } |
| 149 | case OpCode::Id::LD: | ||
| 146 | case OpCode::Id::LDG: { | 150 | case OpCode::Id::LDG: { |
| 151 | const auto type = [instr, &opcode]() -> Tegra::Shader::UniformType { | ||
| 152 | switch (opcode->get().GetId()) { | ||
| 153 | case OpCode::Id::LD: | ||
| 154 | UNIMPLEMENTED_IF_MSG(!instr.generic.extended, "Unextended LD is not implemented"); | ||
| 155 | return instr.generic.type; | ||
| 156 | case OpCode::Id::LDG: | ||
| 157 | return instr.ldg.type; | ||
| 158 | default: | ||
| 159 | UNREACHABLE(); | ||
| 160 | return {}; | ||
| 161 | } | ||
| 162 | }(); | ||
| 163 | |||
| 147 | const auto [real_address_base, base_address, descriptor] = | 164 | const auto [real_address_base, base_address, descriptor] = |
| 148 | TrackAndGetGlobalMemory(bb, GetRegister(instr.gpr8), | 165 | TrackAndGetGlobalMemory(bb, instr, false); |
| 149 | static_cast<u32>(instr.ldg.immediate_offset.Value()), false); | ||
| 150 | 166 | ||
| 151 | const u32 count = GetUniformTypeElementsCount(instr.ldg.type); | 167 | const u32 count = GetUniformTypeElementsCount(type); |
| 152 | for (u32 i = 0; i < count; ++i) { | 168 | for (u32 i = 0; i < count; ++i) { |
| 153 | const Node it_offset = Immediate(i * 4); | 169 | const Node it_offset = Immediate(i * 4); |
| 154 | const Node real_address = | 170 | const Node real_address = |
| @@ -162,28 +178,6 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { | |||
| 162 | } | 178 | } |
| 163 | break; | 179 | break; |
| 164 | } | 180 | } |
| 165 | case OpCode::Id::STG: { | ||
| 166 | const auto [real_address_base, base_address, descriptor] = | ||
| 167 | TrackAndGetGlobalMemory(bb, GetRegister(instr.gpr8), | ||
| 168 | static_cast<u32>(instr.stg.immediate_offset.Value()), true); | ||
| 169 | |||
| 170 | // Encode in temporary registers like this: real_base_address, {registers_to_be_written...} | ||
| 171 | SetTemporal(bb, 0, real_address_base); | ||
| 172 | |||
| 173 | const u32 count = GetUniformTypeElementsCount(instr.stg.type); | ||
| 174 | for (u32 i = 0; i < count; ++i) { | ||
| 175 | SetTemporal(bb, i + 1, GetRegister(instr.gpr0.Value() + i)); | ||
| 176 | } | ||
| 177 | for (u32 i = 0; i < count; ++i) { | ||
| 178 | const Node it_offset = Immediate(i * 4); | ||
| 179 | const Node real_address = | ||
| 180 | Operation(OperationCode::UAdd, NO_PRECISE, real_address_base, it_offset); | ||
| 181 | const Node gmem = StoreNode(GmemNode(real_address, base_address, descriptor)); | ||
| 182 | |||
| 183 | bb.push_back(Operation(OperationCode::Assign, gmem, GetTemporal(i + 1))); | ||
| 184 | } | ||
| 185 | break; | ||
| 186 | } | ||
| 187 | case OpCode::Id::ST_A: { | 181 | case OpCode::Id::ST_A: { |
| 188 | UNIMPLEMENTED_IF_MSG(instr.gpr8.Value() != Register::ZeroIndex, | 182 | UNIMPLEMENTED_IF_MSG(instr.gpr8.Value() != Register::ZeroIndex, |
| 189 | "Indirect attribute loads are not supported"); | 183 | "Indirect attribute loads are not supported"); |
| @@ -239,6 +233,56 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { | |||
| 239 | } | 233 | } |
| 240 | break; | 234 | break; |
| 241 | } | 235 | } |
| 236 | case OpCode::Id::ST: | ||
| 237 | case OpCode::Id::STG: { | ||
| 238 | const auto type = [instr, &opcode]() -> Tegra::Shader::UniformType { | ||
| 239 | switch (opcode->get().GetId()) { | ||
| 240 | case OpCode::Id::ST: | ||
| 241 | UNIMPLEMENTED_IF_MSG(!instr.generic.extended, "Unextended ST is not implemented"); | ||
| 242 | return instr.generic.type; | ||
| 243 | case OpCode::Id::STG: | ||
| 244 | return instr.stg.type; | ||
| 245 | default: | ||
| 246 | UNREACHABLE(); | ||
| 247 | return {}; | ||
| 248 | } | ||
| 249 | }(); | ||
| 250 | |||
| 251 | const auto [real_address_base, base_address, descriptor] = | ||
| 252 | TrackAndGetGlobalMemory(bb, instr, true); | ||
| 253 | |||
| 254 | // Encode in temporary registers like this: real_base_address, {registers_to_be_written...} | ||
| 255 | SetTemporal(bb, 0, real_address_base); | ||
| 256 | |||
| 257 | const u32 count = GetUniformTypeElementsCount(type); | ||
| 258 | for (u32 i = 0; i < count; ++i) { | ||
| 259 | SetTemporal(bb, i + 1, GetRegister(instr.gpr0.Value() + i)); | ||
| 260 | } | ||
| 261 | for (u32 i = 0; i < count; ++i) { | ||
| 262 | const Node it_offset = Immediate(i * 4); | ||
| 263 | const Node real_address = | ||
| 264 | Operation(OperationCode::UAdd, NO_PRECISE, real_address_base, it_offset); | ||
| 265 | const Node gmem = StoreNode(GmemNode(real_address, base_address, descriptor)); | ||
| 266 | |||
| 267 | bb.push_back(Operation(OperationCode::Assign, gmem, GetTemporal(i + 1))); | ||
| 268 | } | ||
| 269 | break; | ||
| 270 | } | ||
| 271 | case OpCode::Id::AL2P: { | ||
| 272 | // Ignore al2p.direction since we don't care about it. | ||
| 273 | |||
| 274 | // Calculate emulation fake physical address. | ||
| 275 | const Node fixed_address{Immediate(static_cast<u32>(instr.al2p.address))}; | ||
| 276 | const Node reg{GetRegister(instr.gpr8)}; | ||
| 277 | const Node fake_address{Operation(OperationCode::IAdd, NO_PRECISE, reg, fixed_address)}; | ||
| 278 | |||
| 279 | // Set the fake address to target register. | ||
| 280 | SetRegister(bb, instr.gpr0, fake_address); | ||
| 281 | |||
| 282 | // Signal the shader IR to declare all possible attributes and varyings | ||
| 283 | uses_physical_attributes = true; | ||
| 284 | break; | ||
| 285 | } | ||
| 242 | default: | 286 | default: |
| 243 | UNIMPLEMENTED_MSG("Unhandled memory instruction: {}", opcode->get().GetName()); | 287 | UNIMPLEMENTED_MSG("Unhandled memory instruction: {}", opcode->get().GetName()); |
| 244 | } | 288 | } |
| @@ -247,9 +291,11 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { | |||
| 247 | } | 291 | } |
| 248 | 292 | ||
| 249 | std::tuple<Node, Node, GlobalMemoryBase> ShaderIR::TrackAndGetGlobalMemory(NodeBlock& bb, | 293 | std::tuple<Node, Node, GlobalMemoryBase> ShaderIR::TrackAndGetGlobalMemory(NodeBlock& bb, |
| 250 | Node addr_register, | 294 | Instruction instr, |
| 251 | u32 immediate_offset, | ||
| 252 | bool is_write) { | 295 | bool is_write) { |
| 296 | const auto addr_register{GetRegister(instr.gmem.gpr)}; | ||
| 297 | const auto immediate_offset{static_cast<u32>(instr.gmem.offset)}; | ||
| 298 | |||
| 253 | const Node base_address{ | 299 | const Node base_address{ |
| 254 | TrackCbuf(addr_register, global_code, static_cast<s64>(global_code.size()))}; | 300 | TrackCbuf(addr_register, global_code, static_cast<s64>(global_code.size()))}; |
| 255 | const auto cbuf = std::get_if<CbufNode>(base_address); | 301 | 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 d750a2936..a6c123573 100644 --- a/src/video_core/shader/decode/other.cpp +++ b/src/video_core/shader/decode/other.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include "common/assert.h" | 5 | #include "common/assert.h" |
| 6 | #include "common/common_types.h" | 6 | #include "common/common_types.h" |
| 7 | #include "common/logging/log.h" | ||
| 7 | #include "video_core/engines/shader_bytecode.h" | 8 | #include "video_core/engines/shader_bytecode.h" |
| 8 | #include "video_core/shader/shader_ir.h" | 9 | #include "video_core/shader/shader_ir.h" |
| 9 | 10 | ||
| @@ -13,6 +14,7 @@ using Tegra::Shader::ConditionCode; | |||
| 13 | using Tegra::Shader::Instruction; | 14 | using Tegra::Shader::Instruction; |
| 14 | using Tegra::Shader::OpCode; | 15 | using Tegra::Shader::OpCode; |
| 15 | using Tegra::Shader::Register; | 16 | using Tegra::Shader::Register; |
| 17 | using Tegra::Shader::SystemVariable; | ||
| 16 | 18 | ||
| 17 | u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { | 19 | u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { |
| 18 | const Instruction instr = {program_code[pc]}; | 20 | const Instruction instr = {program_code[pc]}; |
| @@ -58,20 +60,33 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { | |||
| 58 | break; | 60 | break; |
| 59 | } | 61 | } |
| 60 | case OpCode::Id::MOV_SYS: { | 62 | case OpCode::Id::MOV_SYS: { |
| 61 | switch (instr.sys20) { | 63 | const Node value = [&]() { |
| 62 | case Tegra::Shader::SystemVariable::InvocationInfo: { | 64 | switch (instr.sys20) { |
| 63 | LOG_WARNING(HW_GPU, "MOV_SYS instruction with InvocationInfo is incomplete"); | 65 | case SystemVariable::Ydirection: |
| 64 | SetRegister(bb, instr.gpr0, Immediate(0u)); | 66 | return Operation(OperationCode::YNegate); |
| 65 | break; | 67 | case SystemVariable::InvocationInfo: |
| 66 | } | 68 | LOG_WARNING(HW_GPU, "MOV_SYS instruction with InvocationInfo is incomplete"); |
| 67 | case Tegra::Shader::SystemVariable::Ydirection: { | 69 | return Immediate(0u); |
| 68 | // Config pack's third value is Y_NEGATE's state. | 70 | case SystemVariable::TidX: |
| 69 | SetRegister(bb, instr.gpr0, Operation(OperationCode::YNegate)); | 71 | return Operation(OperationCode::LocalInvocationIdX); |
| 70 | break; | 72 | case SystemVariable::TidY: |
| 71 | } | 73 | return Operation(OperationCode::LocalInvocationIdY); |
| 72 | default: | 74 | case SystemVariable::TidZ: |
| 73 | UNIMPLEMENTED_MSG("Unhandled system move: {}", static_cast<u32>(instr.sys20.Value())); | 75 | return Operation(OperationCode::LocalInvocationIdZ); |
| 74 | } | 76 | case SystemVariable::CtaIdX: |
| 77 | return Operation(OperationCode::WorkGroupIdX); | ||
| 78 | case SystemVariable::CtaIdY: | ||
| 79 | return Operation(OperationCode::WorkGroupIdY); | ||
| 80 | case SystemVariable::CtaIdZ: | ||
| 81 | return Operation(OperationCode::WorkGroupIdZ); | ||
| 82 | default: | ||
| 83 | UNIMPLEMENTED_MSG("Unhandled system move: {}", | ||
| 84 | static_cast<u32>(instr.sys20.Value())); | ||
| 85 | return Immediate(0u); | ||
| 86 | } | ||
| 87 | }(); | ||
| 88 | SetRegister(bb, instr.gpr0, value); | ||
| 89 | |||
| 75 | break; | 90 | break; |
| 76 | } | 91 | } |
| 77 | case OpCode::Id::BRA: { | 92 | case OpCode::Id::BRA: { |
| @@ -130,15 +145,18 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { | |||
| 130 | break; | 145 | break; |
| 131 | } | 146 | } |
| 132 | case OpCode::Id::IPA: { | 147 | case OpCode::Id::IPA: { |
| 133 | const auto& attribute = instr.attribute.fmt28; | 148 | const bool is_physical = instr.ipa.idx && instr.gpr8.Value() != 0xff; |
| 149 | |||
| 150 | const auto attribute = instr.attribute.fmt28; | ||
| 134 | const Tegra::Shader::IpaMode input_mode{instr.ipa.interp_mode.Value(), | 151 | const Tegra::Shader::IpaMode input_mode{instr.ipa.interp_mode.Value(), |
| 135 | instr.ipa.sample_mode.Value()}; | 152 | instr.ipa.sample_mode.Value()}; |
| 136 | 153 | ||
| 137 | const Node attr = GetInputAttribute(attribute.index, attribute.element, input_mode); | 154 | Node value = is_physical ? GetPhysicalInputAttribute(instr.gpr8) |
| 138 | Node value = attr; | 155 | : GetInputAttribute(attribute.index, attribute.element); |
| 139 | const Tegra::Shader::Attribute::Index index = attribute.index.Value(); | 156 | const Tegra::Shader::Attribute::Index index = attribute.index.Value(); |
| 140 | if (index >= Tegra::Shader::Attribute::Index::Attribute_0 && | 157 | const bool is_generic = index >= Tegra::Shader::Attribute::Index::Attribute_0 && |
| 141 | index <= Tegra::Shader::Attribute::Index::Attribute_31) { | 158 | index <= Tegra::Shader::Attribute::Index::Attribute_31; |
| 159 | if (is_generic || is_physical) { | ||
| 142 | // TODO(Blinkhawk): There are cases where a perspective attribute use PASS. | 160 | // TODO(Blinkhawk): There are cases where a perspective attribute use PASS. |
| 143 | // In theory by setting them as perspective, OpenGL does the perspective correction. | 161 | // In theory by setting them as perspective, OpenGL does the perspective correction. |
| 144 | // A way must figured to reverse the last step of it. | 162 | // A way must figured to reverse the last step of it. |
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) { | |||
| 64 | return pc; | 64 | return pc; |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | } // namespace VideoCommon::Shader \ No newline at end of file | 67 | } // 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) { | |||
| 43 | return pc; | 43 | return pc; |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | } // namespace VideoCommon::Shader \ No newline at end of file | 46 | } // 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) { | |||
| 48 | return pc; | 48 | return pc; |
| 49 | } | 49 | } |
| 50 | 50 | ||
| 51 | } // namespace VideoCommon::Shader \ No newline at end of file | 51 | } // 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) { | |||
| 52 | return pc; | 52 | return pc; |
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | } // namespace VideoCommon::Shader \ No newline at end of file | 55 | } // namespace VideoCommon::Shader |
diff --git a/src/video_core/shader/decode/texture.cpp b/src/video_core/shader/decode/texture.cpp index fa65ac9a9..5b033126d 100644 --- a/src/video_core/shader/decode/texture.cpp +++ b/src/video_core/shader/decode/texture.cpp | |||
| @@ -296,7 +296,7 @@ const Sampler& ShaderIR::GetBindlessSampler(const Tegra::Shader::Register& reg, | |||
| 296 | ASSERT(cbuf_offset_imm != nullptr); | 296 | ASSERT(cbuf_offset_imm != nullptr); |
| 297 | const auto cbuf_offset = cbuf_offset_imm->GetValue(); | 297 | const auto cbuf_offset = cbuf_offset_imm->GetValue(); |
| 298 | const auto cbuf_index = cbuf->GetIndex(); | 298 | const auto cbuf_index = cbuf->GetIndex(); |
| 299 | const u64 cbuf_key = (cbuf_index << 32) | cbuf_offset; | 299 | const auto cbuf_key = (static_cast<u64>(cbuf_index) << 32) | static_cast<u64>(cbuf_offset); |
| 300 | 300 | ||
| 301 | // If this sampler has already been used, return the existing mapping. | 301 | // If this sampler has already been used, return the existing mapping. |
| 302 | const auto itr = | 302 | const auto itr = |
| @@ -540,8 +540,6 @@ Node4 ShaderIR::GetTexsCode(Instruction instr, TextureType texture_type, | |||
| 540 | Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool depth_compare, | 540 | Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool depth_compare, |
| 541 | bool is_array, bool is_aoffi) { | 541 | bool is_array, bool is_aoffi) { |
| 542 | const std::size_t coord_count = GetCoordCount(texture_type); | 542 | const std::size_t coord_count = GetCoordCount(texture_type); |
| 543 | const std::size_t total_coord_count = coord_count + (is_array ? 1 : 0); | ||
| 544 | const std::size_t total_reg_count = total_coord_count + (depth_compare ? 1 : 0); | ||
| 545 | 543 | ||
| 546 | // If enabled arrays index is always stored in the gpr8 field | 544 | // If enabled arrays index is always stored in the gpr8 field |
| 547 | const u64 array_register = instr.gpr8.Value(); | 545 | const u64 array_register = instr.gpr8.Value(); |
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, | |||
| 108 | } | 108 | } |
| 109 | } | 109 | } |
| 110 | 110 | ||
| 111 | } // namespace VideoCommon::Shader \ No newline at end of file | 111 | } // namespace VideoCommon::Shader |
diff --git a/src/video_core/shader/decode/xmad.cpp b/src/video_core/shader/decode/xmad.cpp index db15c0718..04a776398 100644 --- a/src/video_core/shader/decode/xmad.cpp +++ b/src/video_core/shader/decode/xmad.cpp | |||
| @@ -56,9 +56,10 @@ u32 ShaderIR::DecodeXmad(NodeBlock& bb, u32 pc) { | |||
| 56 | instr.xmad.mode, | 56 | instr.xmad.mode, |
| 57 | Immediate(static_cast<u32>(instr.xmad.imm20_16)), | 57 | Immediate(static_cast<u32>(instr.xmad.imm20_16)), |
| 58 | GetRegister(instr.gpr39)}; | 58 | GetRegister(instr.gpr39)}; |
| 59 | default: | ||
| 60 | UNIMPLEMENTED_MSG("Unhandled XMAD instruction: {}", opcode->get().GetName()); | ||
| 61 | return {false, false, false, Tegra::Shader::XmadMode::None, Immediate(0), Immediate(0)}; | ||
| 59 | } | 62 | } |
| 60 | UNIMPLEMENTED_MSG("Unhandled XMAD instruction: {}", opcode->get().GetName()); | ||
| 61 | return {false, false, false, Tegra::Shader::XmadMode::None, Immediate(0), Immediate(0)}; | ||
| 62 | }(); | 63 | }(); |
| 63 | 64 | ||
| 64 | op_a = BitfieldExtract(op_a, instr.xmad.high_a ? 16 : 0, 16); | 65 | op_a = BitfieldExtract(op_a, instr.xmad.high_a ? 16 : 0, 16); |
diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp index 17f2f711c..8a6ee5cf5 100644 --- a/src/video_core/shader/shader_ir.cpp +++ b/src/video_core/shader/shader_ir.cpp | |||
| @@ -21,6 +21,13 @@ using Tegra::Shader::PredCondition; | |||
| 21 | using Tegra::Shader::PredOperation; | 21 | using Tegra::Shader::PredOperation; |
| 22 | using Tegra::Shader::Register; | 22 | using Tegra::Shader::Register; |
| 23 | 23 | ||
| 24 | ShaderIR::ShaderIR(const ProgramCode& program_code, u32 main_offset) | ||
| 25 | : program_code{program_code}, main_offset{main_offset} { | ||
| 26 | Decode(); | ||
| 27 | } | ||
| 28 | |||
| 29 | ShaderIR::~ShaderIR() = default; | ||
| 30 | |||
| 24 | Node ShaderIR::StoreNode(NodeData&& node_data) { | 31 | Node ShaderIR::StoreNode(NodeData&& node_data) { |
| 25 | auto store = std::make_unique<NodeData>(node_data); | 32 | auto store = std::make_unique<NodeData>(node_data); |
| 26 | const Node node = store.get(); | 33 | const Node node = store.get(); |
| @@ -32,8 +39,8 @@ Node ShaderIR::Conditional(Node condition, std::vector<Node>&& code) { | |||
| 32 | return StoreNode(ConditionalNode(condition, std::move(code))); | 39 | return StoreNode(ConditionalNode(condition, std::move(code))); |
| 33 | } | 40 | } |
| 34 | 41 | ||
| 35 | Node ShaderIR::Comment(const std::string& text) { | 42 | Node ShaderIR::Comment(std::string text) { |
| 36 | return StoreNode(CommentNode(text)); | 43 | return StoreNode(CommentNode(std::move(text))); |
| 37 | } | 44 | } |
| 38 | 45 | ||
| 39 | Node ShaderIR::Immediate(u32 value) { | 46 | Node ShaderIR::Immediate(u32 value) { |
| @@ -89,13 +96,14 @@ Node ShaderIR::GetPredicate(bool immediate) { | |||
| 89 | return GetPredicate(static_cast<u64>(immediate ? Pred::UnusedIndex : Pred::NeverExecute)); | 96 | return GetPredicate(static_cast<u64>(immediate ? Pred::UnusedIndex : Pred::NeverExecute)); |
| 90 | } | 97 | } |
| 91 | 98 | ||
| 92 | Node ShaderIR::GetInputAttribute(Attribute::Index index, u64 element, | 99 | Node ShaderIR::GetInputAttribute(Attribute::Index index, u64 element, Node buffer) { |
| 93 | const Tegra::Shader::IpaMode& input_mode, Node buffer) { | 100 | used_input_attributes.emplace(index); |
| 94 | const auto [entry, is_new] = | 101 | return StoreNode(AbufNode(index, static_cast<u32>(element), buffer)); |
| 95 | used_input_attributes.emplace(std::make_pair(index, std::set<Tegra::Shader::IpaMode>{})); | 102 | } |
| 96 | entry->second.insert(input_mode); | ||
| 97 | 103 | ||
| 98 | return StoreNode(AbufNode(index, static_cast<u32>(element), input_mode, buffer)); | 104 | Node ShaderIR::GetPhysicalInputAttribute(Tegra::Shader::Register physical_address, Node buffer) { |
| 105 | uses_physical_attributes = true; | ||
| 106 | return StoreNode(AbufNode(GetRegister(physical_address), buffer)); | ||
| 99 | } | 107 | } |
| 100 | 108 | ||
| 101 | Node ShaderIR::GetOutputAttribute(Attribute::Index index, u64 element, Node buffer) { | 109 | Node ShaderIR::GetOutputAttribute(Attribute::Index index, u64 element, Node buffer) { |
| @@ -439,11 +447,14 @@ Node ShaderIR::BitfieldExtract(Node value, u32 offset, u32 bits) { | |||
| 439 | return OperationCode::LogicalUGreaterEqual; | 447 | return OperationCode::LogicalUGreaterEqual; |
| 440 | case OperationCode::INegate: | 448 | case OperationCode::INegate: |
| 441 | UNREACHABLE_MSG("Can't negate an unsigned integer"); | 449 | UNREACHABLE_MSG("Can't negate an unsigned integer"); |
| 450 | return {}; | ||
| 442 | case OperationCode::IAbsolute: | 451 | case OperationCode::IAbsolute: |
| 443 | UNREACHABLE_MSG("Can't apply absolute to an unsigned integer"); | 452 | UNREACHABLE_MSG("Can't apply absolute to an unsigned integer"); |
| 453 | return {}; | ||
| 454 | default: | ||
| 455 | UNREACHABLE_MSG("Unknown signed operation with code={}", static_cast<u32>(operation_code)); | ||
| 456 | return {}; | ||
| 444 | } | 457 | } |
| 445 | UNREACHABLE_MSG("Unknown signed operation with code={}", static_cast<u32>(operation_code)); | ||
| 446 | return {}; | ||
| 447 | } | 458 | } |
| 448 | 459 | ||
| 449 | } // namespace VideoCommon::Shader \ No newline at end of file | 460 | } // namespace VideoCommon::Shader |
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h index 81278fb33..ff7472e30 100644 --- a/src/video_core/shader/shader_ir.h +++ b/src/video_core/shader/shader_ir.h | |||
| @@ -181,7 +181,13 @@ enum class OperationCode { | |||
| 181 | EmitVertex, /// () -> void | 181 | EmitVertex, /// () -> void |
| 182 | EndPrimitive, /// () -> void | 182 | EndPrimitive, /// () -> void |
| 183 | 183 | ||
| 184 | YNegate, /// () -> float | 184 | YNegate, /// () -> float |
| 185 | LocalInvocationIdX, /// () -> uint | ||
| 186 | LocalInvocationIdY, /// () -> uint | ||
| 187 | LocalInvocationIdZ, /// () -> uint | ||
| 188 | WorkGroupIdX, /// () -> uint | ||
| 189 | WorkGroupIdY, /// () -> uint | ||
| 190 | WorkGroupIdZ, /// () -> uint | ||
| 185 | 191 | ||
| 186 | Amount, | 192 | Amount, |
| 187 | }; | 193 | }; |
| @@ -251,8 +257,9 @@ public: | |||
| 251 | } | 257 | } |
| 252 | 258 | ||
| 253 | bool operator<(const Sampler& rhs) const { | 259 | bool operator<(const Sampler& rhs) const { |
| 254 | return std::tie(offset, index, type, is_array, is_shadow) < | 260 | return std::tie(index, offset, type, is_array, is_shadow, is_bindless) < |
| 255 | std::tie(rhs.offset, rhs.index, rhs.type, rhs.is_array, rhs.is_shadow); | 261 | std::tie(rhs.index, rhs.offset, rhs.type, rhs.is_array, rhs.is_shadow, |
| 262 | rhs.is_bindless); | ||
| 256 | } | 263 | } |
| 257 | 264 | ||
| 258 | private: | 265 | private: |
| @@ -327,40 +334,31 @@ struct MetaTexture { | |||
| 327 | u32 element{}; | 334 | u32 element{}; |
| 328 | }; | 335 | }; |
| 329 | 336 | ||
| 330 | inline constexpr MetaArithmetic PRECISE = {true}; | 337 | constexpr MetaArithmetic PRECISE = {true}; |
| 331 | inline constexpr MetaArithmetic NO_PRECISE = {false}; | 338 | constexpr MetaArithmetic NO_PRECISE = {false}; |
| 332 | 339 | ||
| 333 | using Meta = std::variant<MetaArithmetic, MetaTexture, Tegra::Shader::HalfType>; | 340 | using Meta = std::variant<MetaArithmetic, MetaTexture, Tegra::Shader::HalfType>; |
| 334 | 341 | ||
| 335 | /// Holds any kind of operation that can be done in the IR | 342 | /// Holds any kind of operation that can be done in the IR |
| 336 | class OperationNode final { | 343 | class OperationNode final { |
| 337 | public: | 344 | public: |
| 338 | template <typename... T> | 345 | explicit OperationNode(OperationCode code) : code{code} {} |
| 339 | explicit constexpr OperationNode(OperationCode code) : code{code}, meta{} {} | ||
| 340 | 346 | ||
| 341 | template <typename... T> | 347 | explicit OperationNode(OperationCode code, Meta&& meta) : code{code}, meta{std::move(meta)} {} |
| 342 | explicit constexpr OperationNode(OperationCode code, Meta&& meta) | ||
| 343 | : code{code}, meta{std::move(meta)} {} | ||
| 344 | 348 | ||
| 345 | template <typename... T> | 349 | template <typename... T> |
| 346 | explicit constexpr OperationNode(OperationCode code, const T*... operands) | 350 | explicit OperationNode(OperationCode code, const T*... operands) |
| 347 | : OperationNode(code, {}, operands...) {} | 351 | : OperationNode(code, {}, operands...) {} |
| 348 | 352 | ||
| 349 | template <typename... T> | 353 | template <typename... T> |
| 350 | explicit constexpr OperationNode(OperationCode code, Meta&& meta, const T*... operands_) | 354 | explicit OperationNode(OperationCode code, Meta&& meta, const T*... operands_) |
| 351 | : code{code}, meta{std::move(meta)} { | 355 | : code{code}, meta{std::move(meta)}, operands{operands_...} {} |
| 352 | |||
| 353 | auto operands_list = {operands_...}; | ||
| 354 | for (auto& operand : operands_list) { | ||
| 355 | operands.push_back(operand); | ||
| 356 | } | ||
| 357 | } | ||
| 358 | 356 | ||
| 359 | explicit OperationNode(OperationCode code, Meta&& meta, std::vector<Node>&& operands) | 357 | explicit OperationNode(OperationCode code, Meta&& meta, std::vector<Node>&& operands) |
| 360 | : code{code}, meta{meta}, operands{std::move(operands)} {} | 358 | : code{code}, meta{meta}, operands{std::move(operands)} {} |
| 361 | 359 | ||
| 362 | explicit OperationNode(OperationCode code, std::vector<Node>&& operands) | 360 | explicit OperationNode(OperationCode code, std::vector<Node>&& operands) |
| 363 | : code{code}, meta{}, operands{std::move(operands)} {} | 361 | : code{code}, operands{std::move(operands)} {} |
| 364 | 362 | ||
| 365 | OperationCode GetCode() const { | 363 | OperationCode GetCode() const { |
| 366 | return code; | 364 | return code; |
| @@ -464,17 +462,14 @@ private: | |||
| 464 | /// Attribute buffer memory (known as attributes or varyings in GLSL terms) | 462 | /// Attribute buffer memory (known as attributes or varyings in GLSL terms) |
| 465 | class AbufNode final { | 463 | class AbufNode final { |
| 466 | public: | 464 | public: |
| 467 | explicit constexpr AbufNode(Tegra::Shader::Attribute::Index index, u32 element, | 465 | // Initialize for standard attributes (index is explicit). |
| 468 | const Tegra::Shader::IpaMode& input_mode, Node buffer = {}) | ||
| 469 | : input_mode{input_mode}, buffer{buffer}, index{index}, element{element} {} | ||
| 470 | |||
| 471 | explicit constexpr AbufNode(Tegra::Shader::Attribute::Index index, u32 element, | 466 | explicit constexpr AbufNode(Tegra::Shader::Attribute::Index index, u32 element, |
| 472 | Node buffer = {}) | 467 | Node buffer = {}) |
| 473 | : input_mode{}, buffer{buffer}, index{index}, element{element} {} | 468 | : buffer{buffer}, index{index}, element{element} {} |
| 474 | 469 | ||
| 475 | Tegra::Shader::IpaMode GetInputMode() const { | 470 | // Initialize for physical attributes (index is a variable value). |
| 476 | return input_mode; | 471 | explicit constexpr AbufNode(Node physical_address, Node buffer = {}) |
| 477 | } | 472 | : physical_address{physical_address}, buffer{buffer} {} |
| 478 | 473 | ||
| 479 | Tegra::Shader::Attribute::Index GetIndex() const { | 474 | Tegra::Shader::Attribute::Index GetIndex() const { |
| 480 | return index; | 475 | return index; |
| @@ -488,11 +483,19 @@ public: | |||
| 488 | return buffer; | 483 | return buffer; |
| 489 | } | 484 | } |
| 490 | 485 | ||
| 486 | bool IsPhysicalBuffer() const { | ||
| 487 | return physical_address != nullptr; | ||
| 488 | } | ||
| 489 | |||
| 490 | Node GetPhysicalAddress() const { | ||
| 491 | return physical_address; | ||
| 492 | } | ||
| 493 | |||
| 491 | private: | 494 | private: |
| 492 | const Tegra::Shader::IpaMode input_mode; | 495 | Node physical_address{}; |
| 493 | const Node buffer; | 496 | Node buffer{}; |
| 494 | const Tegra::Shader::Attribute::Index index; | 497 | Tegra::Shader::Attribute::Index index{}; |
| 495 | const u32 element; | 498 | u32 element{}; |
| 496 | }; | 499 | }; |
| 497 | 500 | ||
| 498 | /// Constant buffer node, usually mapped to uniform buffers in GLSL | 501 | /// Constant buffer node, usually mapped to uniform buffers in GLSL |
| @@ -566,11 +569,8 @@ private: | |||
| 566 | 569 | ||
| 567 | class ShaderIR final { | 570 | class ShaderIR final { |
| 568 | public: | 571 | public: |
| 569 | explicit ShaderIR(const ProgramCode& program_code, u32 main_offset) | 572 | explicit ShaderIR(const ProgramCode& program_code, u32 main_offset); |
| 570 | : program_code{program_code}, main_offset{main_offset} { | 573 | ~ShaderIR(); |
| 571 | |||
| 572 | Decode(); | ||
| 573 | } | ||
| 574 | 574 | ||
| 575 | const std::map<u32, NodeBlock>& GetBasicBlocks() const { | 575 | const std::map<u32, NodeBlock>& GetBasicBlocks() const { |
| 576 | return basic_blocks; | 576 | return basic_blocks; |
| @@ -584,8 +584,7 @@ public: | |||
| 584 | return used_predicates; | 584 | return used_predicates; |
| 585 | } | 585 | } |
| 586 | 586 | ||
| 587 | const std::map<Tegra::Shader::Attribute::Index, std::set<Tegra::Shader::IpaMode>>& | 587 | const std::set<Tegra::Shader::Attribute::Index>& GetInputAttributes() const { |
| 588 | GetInputAttributes() const { | ||
| 589 | return used_input_attributes; | 588 | return used_input_attributes; |
| 590 | } | 589 | } |
| 591 | 590 | ||
| @@ -614,6 +613,10 @@ public: | |||
| 614 | return static_cast<std::size_t>(coverage_end * sizeof(u64)); | 613 | return static_cast<std::size_t>(coverage_end * sizeof(u64)); |
| 615 | } | 614 | } |
| 616 | 615 | ||
| 616 | bool HasPhysicalAttributes() const { | ||
| 617 | return uses_physical_attributes; | ||
| 618 | } | ||
| 619 | |||
| 617 | const Tegra::Shader::Header& GetHeader() const { | 620 | const Tegra::Shader::Header& GetHeader() const { |
| 618 | return header; | 621 | return header; |
| 619 | } | 622 | } |
| @@ -666,7 +669,7 @@ private: | |||
| 666 | /// Creates a conditional node | 669 | /// Creates a conditional node |
| 667 | Node Conditional(Node condition, std::vector<Node>&& code); | 670 | Node Conditional(Node condition, std::vector<Node>&& code); |
| 668 | /// Creates a commentary | 671 | /// Creates a commentary |
| 669 | Node Comment(const std::string& text); | 672 | Node Comment(std::string text); |
| 670 | /// Creates an u32 immediate | 673 | /// Creates an u32 immediate |
| 671 | Node Immediate(u32 value); | 674 | Node Immediate(u32 value); |
| 672 | /// Creates a s32 immediate | 675 | /// Creates a s32 immediate |
| @@ -695,8 +698,9 @@ private: | |||
| 695 | /// Generates a predicate node for an immediate true or false value | 698 | /// Generates a predicate node for an immediate true or false value |
| 696 | Node GetPredicate(bool immediate); | 699 | Node GetPredicate(bool immediate); |
| 697 | /// Generates a node representing an input attribute. Keeps track of used attributes. | 700 | /// Generates a node representing an input attribute. Keeps track of used attributes. |
| 698 | Node GetInputAttribute(Tegra::Shader::Attribute::Index index, u64 element, | 701 | Node GetInputAttribute(Tegra::Shader::Attribute::Index index, u64 element, Node buffer = {}); |
| 699 | const Tegra::Shader::IpaMode& input_mode, Node buffer = {}); | 702 | /// Generates a node representing a physical input attribute. |
| 703 | Node GetPhysicalInputAttribute(Tegra::Shader::Register physical_address, Node buffer = {}); | ||
| 700 | /// Generates a node representing an output attribute. Keeps track of used attributes. | 704 | /// Generates a node representing an output attribute. Keeps track of used attributes. |
| 701 | Node GetOutputAttribute(Tegra::Shader::Attribute::Index index, u64 element, Node buffer); | 705 | Node GetOutputAttribute(Tegra::Shader::Attribute::Index index, u64 element, Node buffer); |
| 702 | /// Generates a node representing an internal flag | 706 | /// Generates a node representing an internal flag |
| @@ -813,16 +817,15 @@ private: | |||
| 813 | void WriteLop3Instruction(NodeBlock& bb, Tegra::Shader::Register dest, Node op_a, Node op_b, | 817 | void WriteLop3Instruction(NodeBlock& bb, Tegra::Shader::Register dest, Node op_a, Node op_b, |
| 814 | Node op_c, Node imm_lut, bool sets_cc); | 818 | Node op_c, Node imm_lut, bool sets_cc); |
| 815 | 819 | ||
| 816 | Node TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor); | 820 | Node TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) const; |
| 817 | 821 | ||
| 818 | std::optional<u32> TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor); | 822 | std::optional<u32> TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor) const; |
| 819 | 823 | ||
| 820 | std::pair<Node, s64> TrackRegister(const GprNode* tracked, const NodeBlock& code, s64 cursor); | 824 | std::pair<Node, s64> TrackRegister(const GprNode* tracked, const NodeBlock& code, |
| 825 | s64 cursor) const; | ||
| 821 | 826 | ||
| 822 | std::tuple<Node, Node, GlobalMemoryBase> TrackAndGetGlobalMemory(NodeBlock& bb, | 827 | std::tuple<Node, Node, GlobalMemoryBase> TrackAndGetGlobalMemory( |
| 823 | Node addr_register, | 828 | NodeBlock& bb, Tegra::Shader::Instruction instr, bool is_write); |
| 824 | u32 immediate_offset, | ||
| 825 | bool is_write); | ||
| 826 | 829 | ||
| 827 | template <typename... T> | 830 | template <typename... T> |
| 828 | Node Operation(OperationCode code, const T*... operands) { | 831 | Node Operation(OperationCode code, const T*... operands) { |
| @@ -834,12 +837,10 @@ private: | |||
| 834 | return StoreNode(OperationNode(code, std::move(meta), operands...)); | 837 | return StoreNode(OperationNode(code, std::move(meta), operands...)); |
| 835 | } | 838 | } |
| 836 | 839 | ||
| 837 | template <typename... T> | ||
| 838 | Node Operation(OperationCode code, std::vector<Node>&& operands) { | 840 | Node Operation(OperationCode code, std::vector<Node>&& operands) { |
| 839 | return StoreNode(OperationNode(code, std::move(operands))); | 841 | return StoreNode(OperationNode(code, std::move(operands))); |
| 840 | } | 842 | } |
| 841 | 843 | ||
| 842 | template <typename... T> | ||
| 843 | Node Operation(OperationCode code, Meta&& meta, std::vector<Node>&& operands) { | 844 | Node Operation(OperationCode code, Meta&& meta, std::vector<Node>&& operands) { |
| 844 | return StoreNode(OperationNode(code, std::move(meta), std::move(operands))); | 845 | return StoreNode(OperationNode(code, std::move(meta), std::move(operands))); |
| 845 | } | 846 | } |
| @@ -871,13 +872,13 @@ private: | |||
| 871 | 872 | ||
| 872 | std::set<u32> used_registers; | 873 | std::set<u32> used_registers; |
| 873 | std::set<Tegra::Shader::Pred> used_predicates; | 874 | std::set<Tegra::Shader::Pred> used_predicates; |
| 874 | std::map<Tegra::Shader::Attribute::Index, std::set<Tegra::Shader::IpaMode>> | 875 | std::set<Tegra::Shader::Attribute::Index> used_input_attributes; |
| 875 | used_input_attributes; | ||
| 876 | std::set<Tegra::Shader::Attribute::Index> used_output_attributes; | 876 | std::set<Tegra::Shader::Attribute::Index> used_output_attributes; |
| 877 | std::map<u32, ConstBuffer> used_cbufs; | 877 | std::map<u32, ConstBuffer> used_cbufs; |
| 878 | std::set<Sampler> used_samplers; | 878 | std::set<Sampler> used_samplers; |
| 879 | std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances> used_clip_distances{}; | 879 | std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances> used_clip_distances{}; |
| 880 | std::map<GlobalMemoryBase, GlobalMemoryUsage> used_global_memory; | 880 | std::map<GlobalMemoryBase, GlobalMemoryUsage> used_global_memory; |
| 881 | bool uses_physical_attributes{}; // Shader uses AL2P or physical attribute read/writes | ||
| 881 | 882 | ||
| 882 | Tegra::Shader::Header header; | 883 | Tegra::Shader::Header header; |
| 883 | }; | 884 | }; |
diff --git a/src/video_core/shader/track.cpp b/src/video_core/shader/track.cpp index 4505667ff..19ede1eb9 100644 --- a/src/video_core/shader/track.cpp +++ b/src/video_core/shader/track.cpp | |||
| @@ -17,22 +17,24 @@ std::pair<Node, s64> FindOperation(const NodeBlock& code, s64 cursor, | |||
| 17 | for (; cursor >= 0; --cursor) { | 17 | for (; cursor >= 0; --cursor) { |
| 18 | const Node node = code.at(cursor); | 18 | const Node node = code.at(cursor); |
| 19 | if (const auto operation = std::get_if<OperationNode>(node)) { | 19 | if (const auto operation = std::get_if<OperationNode>(node)) { |
| 20 | if (operation->GetCode() == operation_code) | 20 | if (operation->GetCode() == operation_code) { |
| 21 | return {node, cursor}; | 21 | return {node, cursor}; |
| 22 | } | ||
| 22 | } | 23 | } |
| 23 | if (const auto conditional = std::get_if<ConditionalNode>(node)) { | 24 | if (const auto conditional = std::get_if<ConditionalNode>(node)) { |
| 24 | const auto& conditional_code = conditional->GetCode(); | 25 | const auto& conditional_code = conditional->GetCode(); |
| 25 | const auto [found, internal_cursor] = FindOperation( | 26 | const auto [found, internal_cursor] = FindOperation( |
| 26 | conditional_code, static_cast<s64>(conditional_code.size() - 1), operation_code); | 27 | conditional_code, static_cast<s64>(conditional_code.size() - 1), operation_code); |
| 27 | if (found) | 28 | if (found) { |
| 28 | return {found, cursor}; | 29 | return {found, cursor}; |
| 30 | } | ||
| 29 | } | 31 | } |
| 30 | } | 32 | } |
| 31 | return {}; | 33 | return {}; |
| 32 | } | 34 | } |
| 33 | } // namespace | 35 | } // namespace |
| 34 | 36 | ||
| 35 | Node ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) { | 37 | Node ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) const { |
| 36 | if (const auto cbuf = std::get_if<CbufNode>(tracked)) { | 38 | if (const auto cbuf = std::get_if<CbufNode>(tracked)) { |
| 37 | // Cbuf found, but it has to be immediate | 39 | // Cbuf found, but it has to be immediate |
| 38 | return std::holds_alternative<ImmediateNode>(*cbuf->GetOffset()) ? tracked : nullptr; | 40 | return std::holds_alternative<ImmediateNode>(*cbuf->GetOffset()) ? tracked : nullptr; |
| @@ -65,7 +67,7 @@ Node ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) { | |||
| 65 | return nullptr; | 67 | return nullptr; |
| 66 | } | 68 | } |
| 67 | 69 | ||
| 68 | std::optional<u32> ShaderIR::TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor) { | 70 | std::optional<u32> ShaderIR::TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor) const { |
| 69 | // Reduce the cursor in one to avoid infinite loops when the instruction sets the same register | 71 | // Reduce the cursor in one to avoid infinite loops when the instruction sets the same register |
| 70 | // that it uses as operand | 72 | // that it uses as operand |
| 71 | const auto [found, found_cursor] = | 73 | const auto [found, found_cursor] = |
| @@ -80,7 +82,7 @@ std::optional<u32> ShaderIR::TrackImmediate(Node tracked, const NodeBlock& code, | |||
| 80 | } | 82 | } |
| 81 | 83 | ||
| 82 | std::pair<Node, s64> ShaderIR::TrackRegister(const GprNode* tracked, const NodeBlock& code, | 84 | std::pair<Node, s64> ShaderIR::TrackRegister(const GprNode* tracked, const NodeBlock& code, |
| 83 | s64 cursor) { | 85 | s64 cursor) const { |
| 84 | for (; cursor >= 0; --cursor) { | 86 | for (; cursor >= 0; --cursor) { |
| 85 | const auto [found_node, new_cursor] = FindOperation(code, cursor, OperationCode::Assign); | 87 | const auto [found_node, new_cursor] = FindOperation(code, cursor, OperationCode::Assign); |
| 86 | if (!found_node) { | 88 | if (!found_node) { |
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp index 3b022a456..6384fa8d2 100644 --- a/src/video_core/surface.cpp +++ b/src/video_core/surface.cpp | |||
| @@ -178,39 +178,44 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format, | |||
| 178 | return PixelFormat::ABGR8S; | 178 | return PixelFormat::ABGR8S; |
| 179 | case Tegra::Texture::ComponentType::UINT: | 179 | case Tegra::Texture::ComponentType::UINT: |
| 180 | return PixelFormat::ABGR8UI; | 180 | return PixelFormat::ABGR8UI; |
| 181 | default: | ||
| 182 | break; | ||
| 181 | } | 183 | } |
| 182 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); | 184 | break; |
| 183 | UNREACHABLE(); | ||
| 184 | case Tegra::Texture::TextureFormat::B5G6R5: | 185 | case Tegra::Texture::TextureFormat::B5G6R5: |
| 185 | switch (component_type) { | 186 | switch (component_type) { |
| 186 | case Tegra::Texture::ComponentType::UNORM: | 187 | case Tegra::Texture::ComponentType::UNORM: |
| 187 | return PixelFormat::B5G6R5U; | 188 | return PixelFormat::B5G6R5U; |
| 189 | default: | ||
| 190 | break; | ||
| 188 | } | 191 | } |
| 189 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); | 192 | break; |
| 190 | UNREACHABLE(); | ||
| 191 | case Tegra::Texture::TextureFormat::A2B10G10R10: | 193 | case Tegra::Texture::TextureFormat::A2B10G10R10: |
| 192 | switch (component_type) { | 194 | switch (component_type) { |
| 193 | case Tegra::Texture::ComponentType::UNORM: | 195 | case Tegra::Texture::ComponentType::UNORM: |
| 194 | return PixelFormat::A2B10G10R10U; | 196 | return PixelFormat::A2B10G10R10U; |
| 197 | default: | ||
| 198 | break; | ||
| 195 | } | 199 | } |
| 196 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); | 200 | break; |
| 197 | UNREACHABLE(); | ||
| 198 | case Tegra::Texture::TextureFormat::A1B5G5R5: | 201 | case Tegra::Texture::TextureFormat::A1B5G5R5: |
| 199 | switch (component_type) { | 202 | switch (component_type) { |
| 200 | case Tegra::Texture::ComponentType::UNORM: | 203 | case Tegra::Texture::ComponentType::UNORM: |
| 201 | return PixelFormat::A1B5G5R5U; | 204 | return PixelFormat::A1B5G5R5U; |
| 205 | default: | ||
| 206 | break; | ||
| 202 | } | 207 | } |
| 203 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); | 208 | break; |
| 204 | UNREACHABLE(); | ||
| 205 | case Tegra::Texture::TextureFormat::R8: | 209 | case Tegra::Texture::TextureFormat::R8: |
| 206 | switch (component_type) { | 210 | switch (component_type) { |
| 207 | case Tegra::Texture::ComponentType::UNORM: | 211 | case Tegra::Texture::ComponentType::UNORM: |
| 208 | return PixelFormat::R8U; | 212 | return PixelFormat::R8U; |
| 209 | case Tegra::Texture::ComponentType::UINT: | 213 | case Tegra::Texture::ComponentType::UINT: |
| 210 | return PixelFormat::R8UI; | 214 | return PixelFormat::R8UI; |
| 215 | default: | ||
| 216 | break; | ||
| 211 | } | 217 | } |
| 212 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); | 218 | break; |
| 213 | UNREACHABLE(); | ||
| 214 | case Tegra::Texture::TextureFormat::G8R8: | 219 | case Tegra::Texture::TextureFormat::G8R8: |
| 215 | // TextureFormat::G8R8 is actually ordered red then green, as such we can use | 220 | // TextureFormat::G8R8 is actually ordered red then green, as such we can use |
| 216 | // PixelFormat::RG8U and PixelFormat::RG8S. This was tested with The Legend of Zelda: Breath | 221 | // PixelFormat::RG8U and PixelFormat::RG8S. This was tested with The Legend of Zelda: Breath |
| @@ -220,50 +225,55 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format, | |||
| 220 | return PixelFormat::RG8U; | 225 | return PixelFormat::RG8U; |
| 221 | case Tegra::Texture::ComponentType::SNORM: | 226 | case Tegra::Texture::ComponentType::SNORM: |
| 222 | return PixelFormat::RG8S; | 227 | return PixelFormat::RG8S; |
| 228 | default: | ||
| 229 | break; | ||
| 223 | } | 230 | } |
| 224 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); | 231 | break; |
| 225 | UNREACHABLE(); | ||
| 226 | case Tegra::Texture::TextureFormat::R16_G16_B16_A16: | 232 | case Tegra::Texture::TextureFormat::R16_G16_B16_A16: |
| 227 | switch (component_type) { | 233 | switch (component_type) { |
| 228 | case Tegra::Texture::ComponentType::UNORM: | 234 | case Tegra::Texture::ComponentType::UNORM: |
| 229 | return PixelFormat::RGBA16U; | 235 | return PixelFormat::RGBA16U; |
| 230 | case Tegra::Texture::ComponentType::FLOAT: | 236 | case Tegra::Texture::ComponentType::FLOAT: |
| 231 | return PixelFormat::RGBA16F; | 237 | return PixelFormat::RGBA16F; |
| 238 | default: | ||
| 239 | break; | ||
| 232 | } | 240 | } |
| 233 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); | 241 | break; |
| 234 | UNREACHABLE(); | ||
| 235 | case Tegra::Texture::TextureFormat::BF10GF11RF11: | 242 | case Tegra::Texture::TextureFormat::BF10GF11RF11: |
| 236 | switch (component_type) { | 243 | switch (component_type) { |
| 237 | case Tegra::Texture::ComponentType::FLOAT: | 244 | case Tegra::Texture::ComponentType::FLOAT: |
| 238 | return PixelFormat::R11FG11FB10F; | 245 | return PixelFormat::R11FG11FB10F; |
| 246 | default: | ||
| 247 | break; | ||
| 239 | } | 248 | } |
| 240 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); | ||
| 241 | UNREACHABLE(); | ||
| 242 | case Tegra::Texture::TextureFormat::R32_G32_B32_A32: | 249 | case Tegra::Texture::TextureFormat::R32_G32_B32_A32: |
| 243 | switch (component_type) { | 250 | switch (component_type) { |
| 244 | case Tegra::Texture::ComponentType::FLOAT: | 251 | case Tegra::Texture::ComponentType::FLOAT: |
| 245 | return PixelFormat::RGBA32F; | 252 | return PixelFormat::RGBA32F; |
| 246 | case Tegra::Texture::ComponentType::UINT: | 253 | case Tegra::Texture::ComponentType::UINT: |
| 247 | return PixelFormat::RGBA32UI; | 254 | return PixelFormat::RGBA32UI; |
| 255 | default: | ||
| 256 | break; | ||
| 248 | } | 257 | } |
| 249 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); | 258 | break; |
| 250 | UNREACHABLE(); | ||
| 251 | case Tegra::Texture::TextureFormat::R32_G32: | 259 | case Tegra::Texture::TextureFormat::R32_G32: |
| 252 | switch (component_type) { | 260 | switch (component_type) { |
| 253 | case Tegra::Texture::ComponentType::FLOAT: | 261 | case Tegra::Texture::ComponentType::FLOAT: |
| 254 | return PixelFormat::RG32F; | 262 | return PixelFormat::RG32F; |
| 255 | case Tegra::Texture::ComponentType::UINT: | 263 | case Tegra::Texture::ComponentType::UINT: |
| 256 | return PixelFormat::RG32UI; | 264 | return PixelFormat::RG32UI; |
| 265 | default: | ||
| 266 | break; | ||
| 257 | } | 267 | } |
| 258 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); | 268 | break; |
| 259 | UNREACHABLE(); | ||
| 260 | case Tegra::Texture::TextureFormat::R32_G32_B32: | 269 | case Tegra::Texture::TextureFormat::R32_G32_B32: |
| 261 | switch (component_type) { | 270 | switch (component_type) { |
| 262 | case Tegra::Texture::ComponentType::FLOAT: | 271 | case Tegra::Texture::ComponentType::FLOAT: |
| 263 | return PixelFormat::RGB32F; | 272 | return PixelFormat::RGB32F; |
| 273 | default: | ||
| 274 | break; | ||
| 264 | } | 275 | } |
| 265 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); | 276 | break; |
| 266 | UNREACHABLE(); | ||
| 267 | case Tegra::Texture::TextureFormat::R16: | 277 | case Tegra::Texture::TextureFormat::R16: |
| 268 | switch (component_type) { | 278 | switch (component_type) { |
| 269 | case Tegra::Texture::ComponentType::FLOAT: | 279 | case Tegra::Texture::ComponentType::FLOAT: |
| @@ -276,18 +286,20 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format, | |||
| 276 | return PixelFormat::R16UI; | 286 | return PixelFormat::R16UI; |
| 277 | case Tegra::Texture::ComponentType::SINT: | 287 | case Tegra::Texture::ComponentType::SINT: |
| 278 | return PixelFormat::R16I; | 288 | return PixelFormat::R16I; |
| 289 | default: | ||
| 290 | break; | ||
| 279 | } | 291 | } |
| 280 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); | 292 | break; |
| 281 | UNREACHABLE(); | ||
| 282 | case Tegra::Texture::TextureFormat::R32: | 293 | case Tegra::Texture::TextureFormat::R32: |
| 283 | switch (component_type) { | 294 | switch (component_type) { |
| 284 | case Tegra::Texture::ComponentType::FLOAT: | 295 | case Tegra::Texture::ComponentType::FLOAT: |
| 285 | return PixelFormat::R32F; | 296 | return PixelFormat::R32F; |
| 286 | case Tegra::Texture::ComponentType::UINT: | 297 | case Tegra::Texture::ComponentType::UINT: |
| 287 | return PixelFormat::R32UI; | 298 | return PixelFormat::R32UI; |
| 299 | default: | ||
| 300 | break; | ||
| 288 | } | 301 | } |
| 289 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); | 302 | break; |
| 290 | UNREACHABLE(); | ||
| 291 | case Tegra::Texture::TextureFormat::ZF32: | 303 | case Tegra::Texture::TextureFormat::ZF32: |
| 292 | return PixelFormat::Z32F; | 304 | return PixelFormat::Z32F; |
| 293 | case Tegra::Texture::TextureFormat::Z16: | 305 | case Tegra::Texture::TextureFormat::Z16: |
| @@ -310,9 +322,10 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format, | |||
| 310 | return PixelFormat::DXN2UNORM; | 322 | return PixelFormat::DXN2UNORM; |
| 311 | case Tegra::Texture::ComponentType::SNORM: | 323 | case Tegra::Texture::ComponentType::SNORM: |
| 312 | return PixelFormat::DXN2SNORM; | 324 | return PixelFormat::DXN2SNORM; |
| 325 | default: | ||
| 326 | break; | ||
| 313 | } | 327 | } |
| 314 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); | 328 | break; |
| 315 | UNREACHABLE(); | ||
| 316 | case Tegra::Texture::TextureFormat::BC7U: | 329 | case Tegra::Texture::TextureFormat::BC7U: |
| 317 | return is_srgb ? PixelFormat::BC7U_SRGB : PixelFormat::BC7U; | 330 | return is_srgb ? PixelFormat::BC7U_SRGB : PixelFormat::BC7U; |
| 318 | case Tegra::Texture::TextureFormat::BC6H_UF16: | 331 | case Tegra::Texture::TextureFormat::BC6H_UF16: |
| @@ -343,15 +356,17 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format, | |||
| 343 | return PixelFormat::RG16UI; | 356 | return PixelFormat::RG16UI; |
| 344 | case Tegra::Texture::ComponentType::SINT: | 357 | case Tegra::Texture::ComponentType::SINT: |
| 345 | return PixelFormat::RG16I; | 358 | return PixelFormat::RG16I; |
| 359 | default: | ||
| 360 | break; | ||
| 346 | } | 361 | } |
| 347 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); | 362 | break; |
| 348 | UNREACHABLE(); | ||
| 349 | default: | 363 | default: |
| 350 | LOG_CRITICAL(HW_GPU, "Unimplemented format={}, component_type={}", static_cast<u32>(format), | 364 | break; |
| 351 | static_cast<u32>(component_type)); | ||
| 352 | UNREACHABLE(); | ||
| 353 | return PixelFormat::ABGR8U; | ||
| 354 | } | 365 | } |
| 366 | LOG_CRITICAL(HW_GPU, "Unimplemented format={}, component_type={}", static_cast<u32>(format), | ||
| 367 | static_cast<u32>(component_type)); | ||
| 368 | UNREACHABLE(); | ||
| 369 | return PixelFormat::ABGR8U; | ||
| 355 | } | 370 | } |
| 356 | 371 | ||
| 357 | ComponentType ComponentTypeFromTexture(Tegra::Texture::ComponentType type) { | 372 | ComponentType ComponentTypeFromTexture(Tegra::Texture::ComponentType type) { |
| @@ -513,8 +528,9 @@ bool IsFormatBCn(PixelFormat format) { | |||
| 513 | case PixelFormat::DXT45_SRGB: | 528 | case PixelFormat::DXT45_SRGB: |
| 514 | case PixelFormat::BC7U_SRGB: | 529 | case PixelFormat::BC7U_SRGB: |
| 515 | return true; | 530 | return true; |
| 531 | default: | ||
| 532 | return false; | ||
| 516 | } | 533 | } |
| 517 | return false; | ||
| 518 | } | 534 | } |
| 519 | 535 | ||
| 520 | } // namespace VideoCore::Surface | 536 | } // namespace VideoCore::Surface |
diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp index b508d64e9..a9b8f69af 100644 --- a/src/video_core/textures/astc.cpp +++ b/src/video_core/textures/astc.cpp | |||
| @@ -25,8 +25,8 @@ | |||
| 25 | 25 | ||
| 26 | class InputBitStream { | 26 | class InputBitStream { |
| 27 | public: | 27 | public: |
| 28 | explicit InputBitStream(const unsigned char* ptr, int nBits = 0, int start_offset = 0) | 28 | explicit InputBitStream(const unsigned char* ptr, int start_offset = 0) |
| 29 | : m_NumBits(nBits), m_CurByte(ptr), m_NextBit(start_offset % 8) {} | 29 | : m_CurByte(ptr), m_NextBit(start_offset % 8) {} |
| 30 | 30 | ||
| 31 | ~InputBitStream() = default; | 31 | ~InputBitStream() = default; |
| 32 | 32 | ||
| @@ -55,12 +55,9 @@ public: | |||
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | private: | 57 | private: |
| 58 | const int m_NumBits; | ||
| 59 | const unsigned char* m_CurByte; | 58 | const unsigned char* m_CurByte; |
| 60 | int m_NextBit = 0; | 59 | int m_NextBit = 0; |
| 61 | int m_BitsRead = 0; | 60 | int m_BitsRead = 0; |
| 62 | |||
| 63 | bool done = false; | ||
| 64 | }; | 61 | }; |
| 65 | 62 | ||
| 66 | class OutputBitStream { | 63 | class OutputBitStream { |
| @@ -114,7 +111,6 @@ private: | |||
| 114 | const int m_NumBits; | 111 | const int m_NumBits; |
| 115 | unsigned char* m_CurByte; | 112 | unsigned char* m_CurByte; |
| 116 | int m_NextBit = 0; | 113 | int m_NextBit = 0; |
| 117 | int m_BitsRead = 0; | ||
| 118 | 114 | ||
| 119 | bool done = false; | 115 | bool done = false; |
| 120 | }; | 116 | }; |
| @@ -1616,6 +1612,7 @@ namespace Tegra::Texture::ASTC { | |||
| 1616 | std::vector<uint8_t> Decompress(const uint8_t* data, uint32_t width, uint32_t height, | 1612 | std::vector<uint8_t> Decompress(const uint8_t* data, uint32_t width, uint32_t height, |
| 1617 | uint32_t depth, uint32_t block_width, uint32_t block_height) { | 1613 | uint32_t depth, uint32_t block_width, uint32_t block_height) { |
| 1618 | uint32_t blockIdx = 0; | 1614 | uint32_t blockIdx = 0; |
| 1615 | std::size_t depth_offset = 0; | ||
| 1619 | std::vector<uint8_t> outData(height * width * depth * 4); | 1616 | std::vector<uint8_t> outData(height * width * depth * 4); |
| 1620 | for (uint32_t k = 0; k < depth; k++) { | 1617 | for (uint32_t k = 0; k < depth; k++) { |
| 1621 | for (uint32_t j = 0; j < height; j += block_height) { | 1618 | for (uint32_t j = 0; j < height; j += block_height) { |
| @@ -1630,7 +1627,7 @@ std::vector<uint8_t> Decompress(const uint8_t* data, uint32_t width, uint32_t he | |||
| 1630 | uint32_t decompWidth = std::min(block_width, width - i); | 1627 | uint32_t decompWidth = std::min(block_width, width - i); |
| 1631 | uint32_t decompHeight = std::min(block_height, height - j); | 1628 | uint32_t decompHeight = std::min(block_height, height - j); |
| 1632 | 1629 | ||
| 1633 | uint8_t* outRow = outData.data() + (j * width + i) * 4; | 1630 | uint8_t* outRow = depth_offset + outData.data() + (j * width + i) * 4; |
| 1634 | for (uint32_t jj = 0; jj < decompHeight; jj++) { | 1631 | for (uint32_t jj = 0; jj < decompHeight; jj++) { |
| 1635 | memcpy(outRow + jj * width * 4, uncompData + jj * block_width, decompWidth * 4); | 1632 | memcpy(outRow + jj * width * 4, uncompData + jj * block_width, decompWidth * 4); |
| 1636 | } | 1633 | } |
| @@ -1638,6 +1635,7 @@ std::vector<uint8_t> Decompress(const uint8_t* data, uint32_t width, uint32_t he | |||
| 1638 | blockIdx++; | 1635 | blockIdx++; |
| 1639 | } | 1636 | } |
| 1640 | } | 1637 | } |
| 1638 | depth_offset += height * width * 4; | ||
| 1641 | } | 1639 | } |
| 1642 | 1640 | ||
| 1643 | return outData; | 1641 | return outData; |
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 5138bd9a3..3ea7b55d0 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -82,8 +82,6 @@ add_executable(yuzu | |||
| 82 | util/limitable_input_dialog.h | 82 | util/limitable_input_dialog.h |
| 83 | util/sequence_dialog/sequence_dialog.cpp | 83 | util/sequence_dialog/sequence_dialog.cpp |
| 84 | util/sequence_dialog/sequence_dialog.h | 84 | util/sequence_dialog/sequence_dialog.h |
| 85 | util/spinbox.cpp | ||
| 86 | util/spinbox.h | ||
| 87 | util/util.cpp | 85 | util/util.cpp |
| 88 | util/util.h | 86 | util/util.h |
| 89 | compatdb.cpp | 87 | compatdb.cpp |
| @@ -157,6 +155,10 @@ target_compile_definitions(yuzu PRIVATE | |||
| 157 | # Use QStringBuilder for string concatenation to reduce | 155 | # Use QStringBuilder for string concatenation to reduce |
| 158 | # the overall number of temporary strings created. | 156 | # the overall number of temporary strings created. |
| 159 | -DQT_USE_QSTRINGBUILDER | 157 | -DQT_USE_QSTRINGBUILDER |
| 158 | |||
| 159 | # Disable implicit conversions from/to C strings | ||
| 160 | -DQT_NO_CAST_FROM_ASCII | ||
| 161 | -DQT_NO_CAST_TO_ASCII | ||
| 160 | ) | 162 | ) |
| 161 | 163 | ||
| 162 | if (YUZU_ENABLE_COMPATIBILITY_REPORTING) | 164 | if (YUZU_ENABLE_COMPATIBILITY_REPORTING) |
diff --git a/src/yuzu/about_dialog.cpp b/src/yuzu/about_dialog.cpp index 3efa65a38..d39b3f07a 100644 --- a/src/yuzu/about_dialog.cpp +++ b/src/yuzu/about_dialog.cpp | |||
| @@ -9,10 +9,10 @@ | |||
| 9 | 9 | ||
| 10 | AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AboutDialog) { | 10 | AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AboutDialog) { |
| 11 | ui->setupUi(this); | 11 | ui->setupUi(this); |
| 12 | ui->labelLogo->setPixmap(QIcon::fromTheme("yuzu").pixmap(200)); | 12 | ui->labelLogo->setPixmap(QIcon::fromTheme(QStringLiteral("yuzu")).pixmap(200)); |
| 13 | ui->labelBuildInfo->setText( | 13 | ui->labelBuildInfo->setText(ui->labelBuildInfo->text().arg( |
| 14 | ui->labelBuildInfo->text().arg(Common::g_build_fullname, Common::g_scm_branch, | 14 | QString::fromUtf8(Common::g_build_fullname), QString::fromUtf8(Common::g_scm_branch), |
| 15 | Common::g_scm_desc, QString(Common::g_build_date).left(10))); | 15 | QString::fromUtf8(Common::g_scm_desc), QString::fromUtf8(Common::g_build_date).left(10))); |
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | AboutDialog::~AboutDialog() = default; | 18 | AboutDialog::~AboutDialog() = default; |
diff --git a/src/yuzu/applets/error.cpp b/src/yuzu/applets/error.cpp index 1fb2fe277..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) | |||
| 29 | void QtErrorDisplay::ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time, | 29 | void QtErrorDisplay::ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time, |
| 30 | std::function<void()> finished) const { | 30 | std::function<void()> finished) const { |
| 31 | this->callback = std::move(finished); | 31 | this->callback = std::move(finished); |
| 32 | |||
| 33 | const QDateTime date_time = QDateTime::fromSecsSinceEpoch(time.count()); | ||
| 32 | emit MainWindowDisplayError( | 34 | emit MainWindowDisplayError( |
| 33 | tr("An error occured on %1 at %2.\nPlease try again or contact the " | 35 | tr("An error occured on %1 at %2.\nPlease try again or contact the " |
| 34 | "developer of the software.\n\nError Code: %3-%4 (0x%5)") | 36 | "developer of the software.\n\nError Code: %3-%4 (0x%5)") |
| 35 | .arg(QDateTime::fromSecsSinceEpoch(time.count()).toString("dddd, MMMM d, yyyy")) | 37 | .arg(date_time.toString(QStringLiteral("dddd, MMMM d, yyyy"))) |
| 36 | .arg(QDateTime::fromSecsSinceEpoch(time.count()).toString("h:mm:ss A")) | 38 | .arg(date_time.toString(QStringLiteral("h:mm:ss A"))) |
| 37 | .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) | 39 | .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) |
| 38 | .arg(error.description, 4, 10, QChar::fromLatin1('0')) | 40 | .arg(error.description, 4, 10, QChar::fromLatin1('0')) |
| 39 | .arg(error.raw, 8, 16, QChar::fromLatin1('0'))); | 41 | .arg(error.raw, 8, 16, QChar::fromLatin1('0'))); |
| @@ -54,6 +56,6 @@ void QtErrorDisplay::ShowCustomErrorText(ResultCode error, std::string dialog_te | |||
| 54 | 56 | ||
| 55 | void QtErrorDisplay::MainWindowFinishedError() { | 57 | void QtErrorDisplay::MainWindowFinishedError() { |
| 56 | // Acquire the HLE mutex | 58 | // Acquire the HLE mutex |
| 57 | std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); | 59 | std::lock_guard lock{HLE::g_hle_lock}; |
| 58 | callback(); | 60 | callback(); |
| 59 | } | 61 | } |
diff --git a/src/yuzu/applets/profile_select.cpp b/src/yuzu/applets/profile_select.cpp index 812b0142f..42e26b978 100644 --- a/src/yuzu/applets/profile_select.cpp +++ b/src/yuzu/applets/profile_select.cpp | |||
| @@ -84,10 +84,10 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent) | |||
| 84 | tree_view->setContextMenuPolicy(Qt::NoContextMenu); | 84 | tree_view->setContextMenuPolicy(Qt::NoContextMenu); |
| 85 | 85 | ||
| 86 | item_model->insertColumns(0, 1); | 86 | item_model->insertColumns(0, 1); |
| 87 | item_model->setHeaderData(0, Qt::Horizontal, "Users"); | 87 | item_model->setHeaderData(0, Qt::Horizontal, tr("Users")); |
| 88 | 88 | ||
| 89 | // We must register all custom types with the Qt Automoc system so that we are able to use it | 89 | // We must register all custom types with the Qt Automoc system so that we are able to use it |
| 90 | // with signals/slots. In this case, QList falls under the umbrells of custom types. | 90 | // with signals/slots. In this case, QList falls under the umbrella of custom types. |
| 91 | qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); | 91 | qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); |
| 92 | 92 | ||
| 93 | layout->setContentsMargins(0, 0, 0, 0); | 93 | layout->setContentsMargins(0, 0, 0, 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( | |||
| 18 | : parameters(std::move(parameters)) {} | 18 | : parameters(std::move(parameters)) {} |
| 19 | 19 | ||
| 20 | QValidator::State QtSoftwareKeyboardValidator::validate(QString& input, int& pos) const { | 20 | QValidator::State QtSoftwareKeyboardValidator::validate(QString& input, int& pos) const { |
| 21 | if (input.size() > parameters.max_length) | 21 | if (input.size() > static_cast<s64>(parameters.max_length)) { |
| 22 | return Invalid; | 22 | return Invalid; |
| 23 | if (parameters.disable_space && input.contains(' ')) | 23 | } |
| 24 | if (parameters.disable_space && input.contains(QLatin1Char{' '})) { | ||
| 24 | return Invalid; | 25 | return Invalid; |
| 25 | if (parameters.disable_address && input.contains('@')) | 26 | } |
| 27 | if (parameters.disable_address && input.contains(QLatin1Char{'@'})) { | ||
| 26 | return Invalid; | 28 | return Invalid; |
| 27 | if (parameters.disable_percent && input.contains('%')) | 29 | } |
| 30 | if (parameters.disable_percent && input.contains(QLatin1Char{'%'})) { | ||
| 28 | return Invalid; | 31 | return Invalid; |
| 29 | if (parameters.disable_slash && (input.contains('/') || input.contains('\\'))) | 32 | } |
| 33 | if (parameters.disable_slash && | ||
| 34 | (input.contains(QLatin1Char{'/'}) || input.contains(QLatin1Char{'\\'}))) { | ||
| 30 | return Invalid; | 35 | return Invalid; |
| 36 | } | ||
| 31 | if (parameters.disable_number && | 37 | if (parameters.disable_number && |
| 32 | std::any_of(input.begin(), input.end(), [](QChar c) { return c.isDigit(); })) { | 38 | std::any_of(input.begin(), input.end(), [](QChar c) { return c.isDigit(); })) { |
| 33 | return Invalid; | 39 | return Invalid; |
| 34 | } | 40 | } |
| 35 | 41 | ||
| 36 | if (parameters.disable_download_code && | 42 | if (parameters.disable_download_code && std::any_of(input.begin(), input.end(), [](QChar c) { |
| 37 | std::any_of(input.begin(), input.end(), [](QChar c) { return c == 'O' || c == 'I'; })) { | 43 | return c == QLatin1Char{'O'} || c == QLatin1Char{'I'}; |
| 44 | })) { | ||
| 38 | return Invalid; | 45 | return Invalid; |
| 39 | } | 46 | } |
| 40 | 47 | ||
| @@ -142,7 +149,7 @@ void QtSoftwareKeyboard::SendTextCheckDialog(std::u16string error_message, | |||
| 142 | void QtSoftwareKeyboard::MainWindowFinishedText(std::optional<std::u16string> text) { | 149 | void QtSoftwareKeyboard::MainWindowFinishedText(std::optional<std::u16string> text) { |
| 143 | // Acquire the HLE mutex | 150 | // Acquire the HLE mutex |
| 144 | std::lock_guard lock{HLE::g_hle_lock}; | 151 | std::lock_guard lock{HLE::g_hle_lock}; |
| 145 | text_output(text); | 152 | text_output(std::move(text)); |
| 146 | } | 153 | } |
| 147 | 154 | ||
| 148 | void QtSoftwareKeyboard::MainWindowFinishedCheckDialog() { | 155 | 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 @@ | |||
| 6 | 6 | ||
| 7 | #include <QDialog> | 7 | #include <QDialog> |
| 8 | #include <QValidator> | 8 | #include <QValidator> |
| 9 | #include "common/assert.h" | ||
| 10 | #include "core/frontend/applets/software_keyboard.h" | 9 | #include "core/frontend/applets/software_keyboard.h" |
| 11 | 10 | ||
| 12 | class GMainWindow; | 11 | class GMainWindow; |
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 5c98636c5..9e420b359 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -91,25 +91,25 @@ void EmuThread::run() { | |||
| 91 | 91 | ||
| 92 | class GGLContext : public Core::Frontend::GraphicsContext { | 92 | class GGLContext : public Core::Frontend::GraphicsContext { |
| 93 | public: | 93 | public: |
| 94 | explicit GGLContext(QOpenGLContext* shared_context) | 94 | explicit GGLContext(QOpenGLContext* shared_context) : shared_context{shared_context} { |
| 95 | : context{std::make_unique<QOpenGLContext>(shared_context)} { | 95 | context.setFormat(shared_context->format()); |
| 96 | surface.setFormat(shared_context->format()); | 96 | context.setShareContext(shared_context); |
| 97 | surface.create(); | 97 | context.create(); |
| 98 | } | 98 | } |
| 99 | 99 | ||
| 100 | void MakeCurrent() override { | 100 | void MakeCurrent() override { |
| 101 | context->makeCurrent(&surface); | 101 | context.makeCurrent(shared_context->surface()); |
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | void DoneCurrent() override { | 104 | void DoneCurrent() override { |
| 105 | context->doneCurrent(); | 105 | context.doneCurrent(); |
| 106 | } | 106 | } |
| 107 | 107 | ||
| 108 | void SwapBuffers() override {} | 108 | void SwapBuffers() override {} |
| 109 | 109 | ||
| 110 | private: | 110 | private: |
| 111 | std::unique_ptr<QOpenGLContext> context; | 111 | QOpenGLContext* shared_context; |
| 112 | QOffscreenSurface surface; | 112 | QOpenGLContext context; |
| 113 | }; | 113 | }; |
| 114 | 114 | ||
| 115 | // This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL | 115 | // This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL |
| @@ -188,7 +188,9 @@ private: | |||
| 188 | GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) | 188 | GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) |
| 189 | : QWidget(parent), emu_thread(emu_thread) { | 189 | : QWidget(parent), emu_thread(emu_thread) { |
| 190 | setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") | 190 | setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") |
| 191 | .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); | 191 | .arg(QString::fromUtf8(Common::g_build_name), |
| 192 | QString::fromUtf8(Common::g_scm_branch), | ||
| 193 | QString::fromUtf8(Common::g_scm_desc))); | ||
| 192 | setAttribute(Qt::WA_AcceptTouchEvents); | 194 | setAttribute(Qt::WA_AcceptTouchEvents); |
| 193 | 195 | ||
| 194 | InputCommon::Init(); | 196 | InputCommon::Init(); |
| @@ -217,7 +219,7 @@ void GRenderWindow::SwapBuffers() { | |||
| 217 | // However: | 219 | // However: |
| 218 | // - The Qt debug runtime prints a bogus warning on the console if `makeCurrent` wasn't called | 220 | // - The Qt debug runtime prints a bogus warning on the console if `makeCurrent` wasn't called |
| 219 | // since the last time `swapBuffers` was executed; | 221 | // since the last time `swapBuffers` was executed; |
| 220 | // - On macOS, if `makeCurrent` isn't called explicitely, resizing the buffer breaks. | 222 | // - On macOS, if `makeCurrent` isn't called explicitly, resizing the buffer breaks. |
| 221 | context->makeCurrent(child); | 223 | context->makeCurrent(child); |
| 222 | 224 | ||
| 223 | context->swapBuffers(child); | 225 | context->swapBuffers(child); |
| @@ -356,7 +358,7 @@ void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) { | |||
| 356 | } | 358 | } |
| 357 | 359 | ||
| 358 | std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { | 360 | std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { |
| 359 | return std::make_unique<GGLContext>(shared_context.get()); | 361 | return std::make_unique<GGLContext>(context.get()); |
| 360 | } | 362 | } |
| 361 | 363 | ||
| 362 | void GRenderWindow::InitRenderTarget() { | 364 | void GRenderWindow::InitRenderTarget() { |
| @@ -379,6 +381,7 @@ void GRenderWindow::InitRenderTarget() { | |||
| 379 | fmt.setVersion(4, 3); | 381 | fmt.setVersion(4, 3); |
| 380 | if (Settings::values.use_compatibility_profile) { | 382 | if (Settings::values.use_compatibility_profile) { |
| 381 | fmt.setProfile(QSurfaceFormat::CompatibilityProfile); | 383 | fmt.setProfile(QSurfaceFormat::CompatibilityProfile); |
| 384 | fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); | ||
| 382 | } else { | 385 | } else { |
| 383 | fmt.setProfile(QSurfaceFormat::CoreProfile); | 386 | fmt.setProfile(QSurfaceFormat::CoreProfile); |
| 384 | } | 387 | } |
| @@ -437,8 +440,7 @@ void GRenderWindow::CaptureScreenshot(u16 res_scale, const QString& screenshot_p | |||
| 437 | layout); | 440 | layout); |
| 438 | } | 441 | } |
| 439 | 442 | ||
| 440 | void GRenderWindow::OnMinimalClientAreaChangeRequest( | 443 | void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) { |
| 441 | const std::pair<unsigned, unsigned>& minimal_size) { | ||
| 442 | setMinimumSize(minimal_size.first, minimal_size.second); | 444 | setMinimumSize(minimal_size.first, minimal_size.second); |
| 443 | } | 445 | } |
| 444 | 446 | ||
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: | |||
| 162 | void TouchUpdateEvent(const QTouchEvent* event); | 162 | void TouchUpdateEvent(const QTouchEvent* event); |
| 163 | void TouchEndEvent(); | 163 | void TouchEndEvent(); |
| 164 | 164 | ||
| 165 | void OnMinimalClientAreaChangeRequest( | 165 | void OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) override; |
| 166 | const std::pair<unsigned, unsigned>& minimal_size) override; | ||
| 167 | 166 | ||
| 168 | QWidget* container = nullptr; | 167 | QWidget* container = nullptr; |
| 169 | GGLWidgetInternal* child = nullptr; | 168 | GGLWidgetInternal* child = nullptr; |
diff --git a/src/yuzu/compatdb.cpp b/src/yuzu/compatdb.cpp index c8b0a5ec0..5477f050c 100644 --- a/src/yuzu/compatdb.cpp +++ b/src/yuzu/compatdb.cpp | |||
| @@ -58,7 +58,7 @@ void CompatDB::Submit() { | |||
| 58 | 58 | ||
| 59 | button(NextButton)->setEnabled(false); | 59 | button(NextButton)->setEnabled(false); |
| 60 | button(NextButton)->setText(tr("Submitting")); | 60 | button(NextButton)->setText(tr("Submitting")); |
| 61 | button(QWizard::CancelButton)->setVisible(false); | 61 | button(CancelButton)->setVisible(false); |
| 62 | 62 | ||
| 63 | testcase_watcher.setFuture(QtConcurrent::run( | 63 | testcase_watcher.setFuture(QtConcurrent::run( |
| 64 | [] { return Core::System::GetInstance().TelemetrySession().SubmitTestcase(); })); | 64 | [] { return Core::System::GetInstance().TelemetrySession().SubmitTestcase(); })); |
| @@ -74,12 +74,12 @@ void CompatDB::OnTestcaseSubmitted() { | |||
| 74 | tr("An error occured while sending the Testcase")); | 74 | tr("An error occured while sending the Testcase")); |
| 75 | button(NextButton)->setEnabled(true); | 75 | button(NextButton)->setEnabled(true); |
| 76 | button(NextButton)->setText(tr("Next")); | 76 | button(NextButton)->setText(tr("Next")); |
| 77 | button(QWizard::CancelButton)->setVisible(true); | 77 | button(CancelButton)->setVisible(true); |
| 78 | } else { | 78 | } else { |
| 79 | next(); | 79 | next(); |
| 80 | // older versions of QT don't support the "NoCancelButtonOnLastPage" option, this is a | 80 | // older versions of QT don't support the "NoCancelButtonOnLastPage" option, this is a |
| 81 | // workaround | 81 | // workaround |
| 82 | button(QWizard::CancelButton)->setVisible(false); | 82 | button(CancelButton)->setVisible(false); |
| 83 | } | 83 | } |
| 84 | } | 84 | } |
| 85 | 85 | ||
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 6c6f047d8..b1942bedc 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #include "core/hle/service/hid/controllers/npad.h" | 11 | #include "core/hle/service/hid/controllers/npad.h" |
| 12 | #include "input_common/main.h" | 12 | #include "input_common/main.h" |
| 13 | #include "yuzu/configuration/config.h" | 13 | #include "yuzu/configuration/config.h" |
| 14 | #include "yuzu/ui_settings.h" | ||
| 14 | 15 | ||
| 15 | Config::Config() { | 16 | Config::Config() { |
| 16 | // TODO: Don't hardcode the path; let the frontend decide where to put the config files. | 17 | // 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 | |||
| 206 | }; | 207 | }; |
| 207 | 208 | ||
| 208 | // This shouldn't have anything except static initializers (no functions). So | 209 | // This shouldn't have anything except static initializers (no functions). So |
| 209 | // QKeySequnce(...).toString() is NOT ALLOWED HERE. | 210 | // QKeySequence(...).toString() is NOT ALLOWED HERE. |
| 210 | // This must be in alphabetical order according to action name as it must have the same order as | 211 | // This must be in alphabetical order according to action name as it must have the same order as |
| 211 | // UISetting::values.shortcuts, which is alphabetically ordered. | 212 | // UISetting::values.shortcuts, which is alphabetically ordered. |
| 212 | const std::array<UISettings::Shortcut, 15> Config::default_hotkeys{ | 213 | // clang-format off |
| 213 | {{"Capture Screenshot", "Main Window", {"Ctrl+P", Qt::ApplicationShortcut}}, | 214 | const std::array<UISettings::Shortcut, 15> default_hotkeys{{ |
| 214 | {"Continue/Pause Emulation", "Main Window", {"F4", Qt::WindowShortcut}}, | 215 | {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::ApplicationShortcut}}, |
| 215 | {"Decrease Speed Limit", "Main Window", {"-", Qt::ApplicationShortcut}}, | 216 | {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}}, |
| 216 | {"Exit yuzu", "Main Window", {"Ctrl+Q", Qt::WindowShortcut}}, | 217 | {QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}}, |
| 217 | {"Exit Fullscreen", "Main Window", {"Esc", Qt::WindowShortcut}}, | 218 | {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}}, |
| 218 | {"Fullscreen", "Main Window", {"F11", Qt::WindowShortcut}}, | 219 | {QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), Qt::WindowShortcut}}, |
| 219 | {"Increase Speed Limit", "Main Window", {"+", Qt::ApplicationShortcut}}, | 220 | {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}}, |
| 220 | {"Load Amiibo", "Main Window", {"F2", Qt::ApplicationShortcut}}, | 221 | {QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}}, |
| 221 | {"Load File", "Main Window", {"Ctrl+O", Qt::WindowShortcut}}, | 222 | {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::ApplicationShortcut}}, |
| 222 | {"Restart Emulation", "Main Window", {"F6", Qt::WindowShortcut}}, | 223 | {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WindowShortcut}}, |
| 223 | {"Stop Emulation", "Main Window", {"F5", Qt::WindowShortcut}}, | 224 | {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}}, |
| 224 | {"Toggle Filter Bar", "Main Window", {"Ctrl+F", Qt::WindowShortcut}}, | 225 | {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}}, |
| 225 | {"Toggle Speed Limit", "Main Window", {"Ctrl+Z", Qt::ApplicationShortcut}}, | 226 | {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}}, |
| 226 | {"Toggle Status Bar", "Main Window", {"Ctrl+S", Qt::WindowShortcut}}, | 227 | {QStringLiteral("Toggle Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}}, |
| 227 | {"Change Docked Mode", "Main Window", {"F10", Qt::ApplicationShortcut}}}}; | 228 | {QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}}, |
| 229 | {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}}, | ||
| 230 | }}; | ||
| 231 | // clang-format on | ||
| 228 | 232 | ||
| 229 | void Config::ReadPlayerValues() { | 233 | void Config::ReadPlayerValues() { |
| 230 | for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { | 234 | for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { |
| 231 | auto& player = Settings::values.players[p]; | 235 | auto& player = Settings::values.players[p]; |
| 232 | 236 | ||
| 233 | player.connected = ReadSetting(QString("player_%1_connected").arg(p), false).toBool(); | 237 | player.connected = |
| 238 | ReadSetting(QStringLiteral("player_%1_connected").arg(p), false).toBool(); | ||
| 234 | 239 | ||
| 235 | player.type = static_cast<Settings::ControllerType>( | 240 | player.type = static_cast<Settings::ControllerType>( |
| 236 | qt_config | 241 | qt_config |
| 237 | ->value(QString("player_%1_type").arg(p), | 242 | ->value(QStringLiteral("player_%1_type").arg(p), |
| 238 | static_cast<u8>(Settings::ControllerType::DualJoycon)) | 243 | static_cast<u8>(Settings::ControllerType::DualJoycon)) |
| 239 | .toUInt()); | 244 | .toUInt()); |
| 240 | 245 | ||
| 241 | player.body_color_left = qt_config | 246 | player.body_color_left = qt_config |
| 242 | ->value(QString("player_%1_body_color_left").arg(p), | 247 | ->value(QStringLiteral("player_%1_body_color_left").arg(p), |
| 243 | Settings::JOYCON_BODY_NEON_BLUE) | 248 | Settings::JOYCON_BODY_NEON_BLUE) |
| 244 | .toUInt(); | 249 | .toUInt(); |
| 245 | player.body_color_right = qt_config | 250 | player.body_color_right = qt_config |
| 246 | ->value(QString("player_%1_body_color_right").arg(p), | 251 | ->value(QStringLiteral("player_%1_body_color_right").arg(p), |
| 247 | Settings::JOYCON_BODY_NEON_RED) | 252 | Settings::JOYCON_BODY_NEON_RED) |
| 248 | .toUInt(); | 253 | .toUInt(); |
| 249 | player.button_color_left = qt_config | 254 | player.button_color_left = qt_config |
| 250 | ->value(QString("player_%1_button_color_left").arg(p), | 255 | ->value(QStringLiteral("player_%1_button_color_left").arg(p), |
| 251 | Settings::JOYCON_BUTTONS_NEON_BLUE) | 256 | Settings::JOYCON_BUTTONS_NEON_BLUE) |
| 252 | .toUInt(); | 257 | .toUInt(); |
| 253 | player.button_color_right = qt_config | 258 | player.button_color_right = |
| 254 | ->value(QString("player_%1_button_color_right").arg(p), | 259 | qt_config |
| 255 | Settings::JOYCON_BUTTONS_NEON_RED) | 260 | ->value(QStringLiteral("player_%1_button_color_right").arg(p), |
| 256 | .toUInt(); | 261 | Settings::JOYCON_BUTTONS_NEON_RED) |
| 262 | .toUInt(); | ||
| 257 | 263 | ||
| 258 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | 264 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { |
| 259 | std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); | 265 | const std::string default_param = |
| 260 | player.buttons[i] = | 266 | InputCommon::GenerateKeyboardParam(default_buttons[i]); |
| 261 | qt_config | 267 | auto& player_buttons = player.buttons[i]; |
| 262 | ->value(QString("player_%1_").arg(p) + Settings::NativeButton::mapping[i], | 268 | |
| 263 | QString::fromStdString(default_param)) | 269 | player_buttons = qt_config |
| 264 | .toString() | 270 | ->value(QStringLiteral("player_%1_").arg(p) + |
| 265 | .toStdString(); | 271 | QString::fromUtf8(Settings::NativeButton::mapping[i]), |
| 266 | if (player.buttons[i].empty()) | 272 | QString::fromStdString(default_param)) |
| 267 | player.buttons[i] = default_param; | 273 | .toString() |
| 274 | .toStdString(); | ||
| 275 | if (player_buttons.empty()) { | ||
| 276 | player_buttons = default_param; | ||
| 277 | } | ||
| 268 | } | 278 | } |
| 269 | 279 | ||
| 270 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | 280 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { |
| 271 | std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | 281 | const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( |
| 272 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], | 282 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], |
| 273 | default_analogs[i][3], default_analogs[i][4], 0.5f); | 283 | default_analogs[i][3], default_analogs[i][4], 0.5f); |
| 274 | player.analogs[i] = | 284 | auto& player_analogs = player.analogs[i]; |
| 275 | qt_config | 285 | |
| 276 | ->value(QString("player_%1_").arg(p) + Settings::NativeAnalog::mapping[i], | 286 | player_analogs = qt_config |
| 277 | QString::fromStdString(default_param)) | 287 | ->value(QStringLiteral("player_%1_").arg(p) + |
| 278 | .toString() | 288 | QString::fromUtf8(Settings::NativeAnalog::mapping[i]), |
| 279 | .toStdString(); | 289 | QString::fromStdString(default_param)) |
| 280 | if (player.analogs[i].empty()) | 290 | .toString() |
| 281 | player.analogs[i] = default_param; | 291 | .toStdString(); |
| 292 | if (player_analogs.empty()) { | ||
| 293 | player_analogs = default_param; | ||
| 294 | } | ||
| 282 | } | 295 | } |
| 283 | } | 296 | } |
| 284 | 297 | ||
| @@ -290,36 +303,45 @@ void Config::ReadPlayerValues() { | |||
| 290 | } | 303 | } |
| 291 | 304 | ||
| 292 | void Config::ReadDebugValues() { | 305 | void Config::ReadDebugValues() { |
| 293 | Settings::values.debug_pad_enabled = ReadSetting("debug_pad_enabled", false).toBool(); | 306 | Settings::values.debug_pad_enabled = |
| 307 | ReadSetting(QStringLiteral("debug_pad_enabled"), false).toBool(); | ||
| 308 | |||
| 294 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | 309 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { |
| 295 | std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); | 310 | const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); |
| 296 | Settings::values.debug_pad_buttons[i] = | 311 | auto& debug_pad_buttons = Settings::values.debug_pad_buttons[i]; |
| 297 | qt_config | 312 | |
| 298 | ->value(QString("debug_pad_") + Settings::NativeButton::mapping[i], | 313 | debug_pad_buttons = qt_config |
| 299 | QString::fromStdString(default_param)) | 314 | ->value(QStringLiteral("debug_pad_") + |
| 300 | .toString() | 315 | QString::fromUtf8(Settings::NativeButton::mapping[i]), |
| 301 | .toStdString(); | 316 | QString::fromStdString(default_param)) |
| 302 | if (Settings::values.debug_pad_buttons[i].empty()) | 317 | .toString() |
| 303 | Settings::values.debug_pad_buttons[i] = default_param; | 318 | .toStdString(); |
| 319 | if (debug_pad_buttons.empty()) { | ||
| 320 | debug_pad_buttons = default_param; | ||
| 321 | } | ||
| 304 | } | 322 | } |
| 305 | 323 | ||
| 306 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | 324 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { |
| 307 | std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | 325 | const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( |
| 308 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], | 326 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], |
| 309 | default_analogs[i][3], default_analogs[i][4], 0.5f); | 327 | default_analogs[i][3], default_analogs[i][4], 0.5f); |
| 310 | Settings::values.debug_pad_analogs[i] = | 328 | auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i]; |
| 311 | qt_config | 329 | |
| 312 | ->value(QString("debug_pad_") + Settings::NativeAnalog::mapping[i], | 330 | debug_pad_analogs = qt_config |
| 313 | QString::fromStdString(default_param)) | 331 | ->value(QStringLiteral("debug_pad_") + |
| 314 | .toString() | 332 | QString::fromUtf8(Settings::NativeAnalog::mapping[i]), |
| 315 | .toStdString(); | 333 | QString::fromStdString(default_param)) |
| 316 | if (Settings::values.debug_pad_analogs[i].empty()) | 334 | .toString() |
| 317 | Settings::values.debug_pad_analogs[i] = default_param; | 335 | .toStdString(); |
| 336 | if (debug_pad_analogs.empty()) { | ||
| 337 | debug_pad_analogs = default_param; | ||
| 338 | } | ||
| 318 | } | 339 | } |
| 319 | } | 340 | } |
| 320 | 341 | ||
| 321 | void Config::ReadKeyboardValues() { | 342 | void Config::ReadKeyboardValues() { |
| 322 | Settings::values.keyboard_enabled = ReadSetting("keyboard_enabled", false).toBool(); | 343 | Settings::values.keyboard_enabled = |
| 344 | ReadSetting(QStringLiteral("keyboard_enabled"), false).toBool(); | ||
| 323 | 345 | ||
| 324 | std::transform(default_keyboard_keys.begin(), default_keyboard_keys.end(), | 346 | std::transform(default_keyboard_keys.begin(), default_keyboard_keys.end(), |
| 325 | Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam); | 347 | Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam); |
| @@ -332,31 +354,41 @@ void Config::ReadKeyboardValues() { | |||
| 332 | } | 354 | } |
| 333 | 355 | ||
| 334 | void Config::ReadMouseValues() { | 356 | void Config::ReadMouseValues() { |
| 335 | Settings::values.mouse_enabled = ReadSetting("mouse_enabled", false).toBool(); | 357 | Settings::values.mouse_enabled = ReadSetting(QStringLiteral("mouse_enabled"), false).toBool(); |
| 336 | 358 | ||
| 337 | for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { | 359 | for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { |
| 338 | std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]); | 360 | const std::string default_param = |
| 339 | Settings::values.mouse_buttons[i] = | 361 | InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]); |
| 340 | qt_config | 362 | auto& mouse_buttons = Settings::values.mouse_buttons[i]; |
| 341 | ->value(QString("mouse_") + Settings::NativeMouseButton::mapping[i], | 363 | |
| 342 | QString::fromStdString(default_param)) | 364 | mouse_buttons = qt_config |
| 343 | .toString() | 365 | ->value(QStringLiteral("mouse_") + |
| 344 | .toStdString(); | 366 | QString::fromUtf8(Settings::NativeMouseButton::mapping[i]), |
| 345 | if (Settings::values.mouse_buttons[i].empty()) | 367 | QString::fromStdString(default_param)) |
| 346 | Settings::values.mouse_buttons[i] = default_param; | 368 | .toString() |
| 369 | .toStdString(); | ||
| 370 | if (mouse_buttons.empty()) { | ||
| 371 | mouse_buttons = default_param; | ||
| 372 | } | ||
| 347 | } | 373 | } |
| 348 | } | 374 | } |
| 349 | 375 | ||
| 350 | void Config::ReadTouchscreenValues() { | 376 | void Config::ReadTouchscreenValues() { |
| 351 | Settings::values.touchscreen.enabled = ReadSetting("touchscreen_enabled", true).toBool(); | 377 | Settings::values.touchscreen.enabled = |
| 378 | ReadSetting(QStringLiteral("touchscreen_enabled"), true).toBool(); | ||
| 352 | Settings::values.touchscreen.device = | 379 | Settings::values.touchscreen.device = |
| 353 | ReadSetting("touchscreen_device", "engine:emu_window").toString().toStdString(); | 380 | ReadSetting(QStringLiteral("touchscreen_device"), QStringLiteral("engine:emu_window")) |
| 381 | .toString() | ||
| 382 | .toStdString(); | ||
| 354 | 383 | ||
| 355 | Settings::values.touchscreen.finger = ReadSetting("touchscreen_finger", 0).toUInt(); | 384 | Settings::values.touchscreen.finger = |
| 356 | Settings::values.touchscreen.rotation_angle = ReadSetting("touchscreen_angle", 0).toUInt(); | 385 | ReadSetting(QStringLiteral("touchscreen_finger"), 0).toUInt(); |
| 357 | Settings::values.touchscreen.diameter_x = ReadSetting("touchscreen_diameter_x", 15).toUInt(); | 386 | Settings::values.touchscreen.rotation_angle = |
| 358 | Settings::values.touchscreen.diameter_y = ReadSetting("touchscreen_diameter_y", 15).toUInt(); | 387 | ReadSetting(QStringLiteral("touchscreen_angle"), 0).toUInt(); |
| 359 | qt_config->endGroup(); | 388 | Settings::values.touchscreen.diameter_x = |
| 389 | ReadSetting(QStringLiteral("touchscreen_diameter_x"), 15).toUInt(); | ||
| 390 | Settings::values.touchscreen.diameter_y = | ||
| 391 | ReadSetting(QStringLiteral("touchscreen_diameter_y"), 15).toUInt(); | ||
| 360 | } | 392 | } |
| 361 | 393 | ||
| 362 | void Config::ApplyDefaultProfileIfInputInvalid() { | 394 | void Config::ApplyDefaultProfileIfInputInvalid() { |
| @@ -366,8 +398,25 @@ void Config::ApplyDefaultProfileIfInputInvalid() { | |||
| 366 | } | 398 | } |
| 367 | } | 399 | } |
| 368 | 400 | ||
| 369 | void Config::ReadValues() { | 401 | void Config::ReadAudioValues() { |
| 370 | qt_config->beginGroup("Controls"); | 402 | qt_config->beginGroup(QStringLiteral("Audio")); |
| 403 | |||
| 404 | Settings::values.sink_id = ReadSetting(QStringLiteral("output_engine"), QStringLiteral("auto")) | ||
| 405 | .toString() | ||
| 406 | .toStdString(); | ||
| 407 | Settings::values.enable_audio_stretching = | ||
| 408 | ReadSetting(QStringLiteral("enable_audio_stretching"), true).toBool(); | ||
| 409 | Settings::values.audio_device_id = | ||
| 410 | ReadSetting(QStringLiteral("output_device"), QStringLiteral("auto")) | ||
| 411 | .toString() | ||
| 412 | .toStdString(); | ||
| 413 | Settings::values.volume = ReadSetting(QStringLiteral("volume"), 1).toFloat(); | ||
| 414 | |||
| 415 | qt_config->endGroup(); | ||
| 416 | } | ||
| 417 | |||
| 418 | void Config::ReadControlValues() { | ||
| 419 | qt_config->beginGroup(QStringLiteral("Controls")); | ||
| 371 | 420 | ||
| 372 | ReadPlayerValues(); | 421 | ReadPlayerValues(); |
| 373 | ReadDebugValues(); | 422 | ReadDebugValues(); |
| @@ -376,220 +425,310 @@ void Config::ReadValues() { | |||
| 376 | ReadTouchscreenValues(); | 425 | ReadTouchscreenValues(); |
| 377 | 426 | ||
| 378 | Settings::values.motion_device = | 427 | Settings::values.motion_device = |
| 379 | ReadSetting("motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01") | 428 | ReadSetting(QStringLiteral("motion_device"), |
| 429 | QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01")) | ||
| 380 | .toString() | 430 | .toString() |
| 381 | .toStdString(); | 431 | .toStdString(); |
| 382 | 432 | ||
| 383 | qt_config->beginGroup("Core"); | ||
| 384 | Settings::values.use_cpu_jit = ReadSetting("use_cpu_jit", true).toBool(); | ||
| 385 | Settings::values.use_multi_core = ReadSetting("use_multi_core", false).toBool(); | ||
| 386 | qt_config->endGroup(); | 433 | qt_config->endGroup(); |
| 434 | } | ||
| 387 | 435 | ||
| 388 | qt_config->beginGroup("Renderer"); | 436 | void Config::ReadCoreValues() { |
| 389 | Settings::values.resolution_factor = ReadSetting("resolution_factor", 1.0).toFloat(); | 437 | qt_config->beginGroup(QStringLiteral("Core")); |
| 390 | Settings::values.use_frame_limit = ReadSetting("use_frame_limit", true).toBool(); | ||
| 391 | Settings::values.frame_limit = ReadSetting("frame_limit", 100).toInt(); | ||
| 392 | Settings::values.use_compatibility_profile = | ||
| 393 | ReadSetting("use_compatibility_profile", true).toBool(); | ||
| 394 | Settings::values.use_disk_shader_cache = ReadSetting("use_disk_shader_cache", true).toBool(); | ||
| 395 | Settings::values.use_accurate_gpu_emulation = | ||
| 396 | ReadSetting("use_accurate_gpu_emulation", false).toBool(); | ||
| 397 | Settings::values.use_asynchronous_gpu_emulation = | ||
| 398 | ReadSetting("use_asynchronous_gpu_emulation", false).toBool(); | ||
| 399 | Settings::values.force_30fps_mode = ReadSetting("force_30fps_mode", false).toBool(); | ||
| 400 | 438 | ||
| 401 | Settings::values.bg_red = ReadSetting("bg_red", 0.0).toFloat(); | 439 | Settings::values.use_cpu_jit = ReadSetting(QStringLiteral("use_cpu_jit"), true).toBool(); |
| 402 | Settings::values.bg_green = ReadSetting("bg_green", 0.0).toFloat(); | 440 | Settings::values.use_multi_core = ReadSetting(QStringLiteral("use_multi_core"), false).toBool(); |
| 403 | Settings::values.bg_blue = ReadSetting("bg_blue", 0.0).toFloat(); | ||
| 404 | qt_config->endGroup(); | ||
| 405 | 441 | ||
| 406 | qt_config->beginGroup("Audio"); | ||
| 407 | Settings::values.sink_id = ReadSetting("output_engine", "auto").toString().toStdString(); | ||
| 408 | Settings::values.enable_audio_stretching = | ||
| 409 | ReadSetting("enable_audio_stretching", true).toBool(); | ||
| 410 | Settings::values.audio_device_id = | ||
| 411 | ReadSetting("output_device", "auto").toString().toStdString(); | ||
| 412 | Settings::values.volume = ReadSetting("volume", 1).toFloat(); | ||
| 413 | qt_config->endGroup(); | 442 | qt_config->endGroup(); |
| 443 | } | ||
| 444 | |||
| 445 | void Config::ReadDataStorageValues() { | ||
| 446 | qt_config->beginGroup(QStringLiteral("Data Storage")); | ||
| 414 | 447 | ||
| 415 | qt_config->beginGroup("Data Storage"); | 448 | Settings::values.use_virtual_sd = ReadSetting(QStringLiteral("use_virtual_sd"), true).toBool(); |
| 416 | Settings::values.use_virtual_sd = ReadSetting("use_virtual_sd", true).toBool(); | ||
| 417 | FileUtil::GetUserPath( | 449 | FileUtil::GetUserPath( |
| 418 | FileUtil::UserPath::NANDDir, | 450 | FileUtil::UserPath::NANDDir, |
| 419 | qt_config | 451 | qt_config |
| 420 | ->value("nand_directory", | 452 | ->value(QStringLiteral("nand_directory"), |
| 421 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))) | 453 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))) |
| 422 | .toString() | 454 | .toString() |
| 423 | .toStdString()); | 455 | .toStdString()); |
| 424 | FileUtil::GetUserPath( | 456 | FileUtil::GetUserPath( |
| 425 | FileUtil::UserPath::SDMCDir, | 457 | FileUtil::UserPath::SDMCDir, |
| 426 | qt_config | 458 | qt_config |
| 427 | ->value("sdmc_directory", | 459 | ->value(QStringLiteral("sdmc_directory"), |
| 428 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))) | 460 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))) |
| 429 | .toString() | 461 | .toString() |
| 430 | .toStdString()); | 462 | .toStdString()); |
| 431 | qt_config->endGroup(); | ||
| 432 | 463 | ||
| 433 | qt_config->beginGroup("Core"); | ||
| 434 | Settings::values.use_cpu_jit = ReadSetting("use_cpu_jit", true).toBool(); | ||
| 435 | Settings::values.use_multi_core = ReadSetting("use_multi_core", false).toBool(); | ||
| 436 | qt_config->endGroup(); | 464 | qt_config->endGroup(); |
| 465 | } | ||
| 437 | 466 | ||
| 438 | qt_config->beginGroup("System"); | 467 | void Config::ReadDebuggingValues() { |
| 439 | Settings::values.use_docked_mode = ReadSetting("use_docked_mode", false).toBool(); | 468 | qt_config->beginGroup(QStringLiteral("Debugging")); |
| 440 | |||
| 441 | Settings::values.current_user = | ||
| 442 | std::clamp<int>(ReadSetting("current_user", 0).toInt(), 0, Service::Account::MAX_USERS - 1); | ||
| 443 | |||
| 444 | Settings::values.language_index = ReadSetting("language_index", 1).toInt(); | ||
| 445 | |||
| 446 | const auto rng_seed_enabled = ReadSetting("rng_seed_enabled", false).toBool(); | ||
| 447 | if (rng_seed_enabled) { | ||
| 448 | Settings::values.rng_seed = ReadSetting("rng_seed", 0).toULongLong(); | ||
| 449 | } else { | ||
| 450 | Settings::values.rng_seed = std::nullopt; | ||
| 451 | } | ||
| 452 | |||
| 453 | const auto custom_rtc_enabled = ReadSetting("custom_rtc_enabled", false).toBool(); | ||
| 454 | if (custom_rtc_enabled) { | ||
| 455 | Settings::values.custom_rtc = | ||
| 456 | std::chrono::seconds(ReadSetting("custom_rtc", 0).toULongLong()); | ||
| 457 | } else { | ||
| 458 | Settings::values.custom_rtc = std::nullopt; | ||
| 459 | } | ||
| 460 | |||
| 461 | qt_config->endGroup(); | ||
| 462 | 469 | ||
| 463 | qt_config->beginGroup("Miscellaneous"); | 470 | Settings::values.use_gdbstub = ReadSetting(QStringLiteral("use_gdbstub"), false).toBool(); |
| 464 | Settings::values.log_filter = ReadSetting("log_filter", "*:Info").toString().toStdString(); | 471 | Settings::values.gdbstub_port = ReadSetting(QStringLiteral("gdbstub_port"), 24689).toInt(); |
| 465 | Settings::values.use_dev_keys = ReadSetting("use_dev_keys", false).toBool(); | 472 | Settings::values.program_args = |
| 466 | qt_config->endGroup(); | 473 | ReadSetting(QStringLiteral("program_args"), QStringLiteral("")).toString().toStdString(); |
| 474 | Settings::values.dump_exefs = ReadSetting(QStringLiteral("dump_exefs"), false).toBool(); | ||
| 475 | Settings::values.dump_nso = ReadSetting(QStringLiteral("dump_nso"), false).toBool(); | ||
| 467 | 476 | ||
| 468 | qt_config->beginGroup("Debugging"); | ||
| 469 | Settings::values.use_gdbstub = ReadSetting("use_gdbstub", false).toBool(); | ||
| 470 | Settings::values.gdbstub_port = ReadSetting("gdbstub_port", 24689).toInt(); | ||
| 471 | Settings::values.program_args = ReadSetting("program_args", "").toString().toStdString(); | ||
| 472 | Settings::values.dump_exefs = ReadSetting("dump_exefs", false).toBool(); | ||
| 473 | Settings::values.dump_nso = ReadSetting("dump_nso", false).toBool(); | ||
| 474 | qt_config->endGroup(); | 477 | qt_config->endGroup(); |
| 478 | } | ||
| 475 | 479 | ||
| 476 | qt_config->beginGroup("WebService"); | 480 | void Config::ReadDisabledAddOnValues() { |
| 477 | Settings::values.enable_telemetry = ReadSetting("enable_telemetry", true).toBool(); | 481 | const auto size = qt_config->beginReadArray(QStringLiteral("DisabledAddOns")); |
| 478 | Settings::values.web_api_url = | ||
| 479 | ReadSetting("web_api_url", "https://api.yuzu-emu.org").toString().toStdString(); | ||
| 480 | Settings::values.yuzu_username = ReadSetting("yuzu_username").toString().toStdString(); | ||
| 481 | Settings::values.yuzu_token = ReadSetting("yuzu_token").toString().toStdString(); | ||
| 482 | qt_config->endGroup(); | ||
| 483 | 482 | ||
| 484 | const auto size = qt_config->beginReadArray("DisabledAddOns"); | ||
| 485 | for (int i = 0; i < size; ++i) { | 483 | for (int i = 0; i < size; ++i) { |
| 486 | qt_config->setArrayIndex(i); | 484 | qt_config->setArrayIndex(i); |
| 487 | const auto title_id = ReadSetting("title_id", 0).toULongLong(); | 485 | const auto title_id = ReadSetting(QStringLiteral("title_id"), 0).toULongLong(); |
| 488 | std::vector<std::string> out; | 486 | std::vector<std::string> out; |
| 489 | const auto d_size = qt_config->beginReadArray("disabled"); | 487 | const auto d_size = qt_config->beginReadArray(QStringLiteral("disabled")); |
| 490 | for (int j = 0; j < d_size; ++j) { | 488 | for (int j = 0; j < d_size; ++j) { |
| 491 | qt_config->setArrayIndex(j); | 489 | qt_config->setArrayIndex(j); |
| 492 | out.push_back(ReadSetting("d", "").toString().toStdString()); | 490 | out.push_back( |
| 491 | ReadSetting(QStringLiteral("d"), QStringLiteral("")).toString().toStdString()); | ||
| 493 | } | 492 | } |
| 494 | qt_config->endArray(); | 493 | qt_config->endArray(); |
| 495 | Settings::values.disabled_addons.insert_or_assign(title_id, out); | 494 | Settings::values.disabled_addons.insert_or_assign(title_id, out); |
| 496 | } | 495 | } |
| 496 | |||
| 497 | qt_config->endArray(); | 497 | qt_config->endArray(); |
| 498 | } | ||
| 499 | |||
| 500 | void Config::ReadMiscellaneousValues() { | ||
| 501 | qt_config->beginGroup(QStringLiteral("Miscellaneous")); | ||
| 502 | |||
| 503 | Settings::values.log_filter = | ||
| 504 | ReadSetting(QStringLiteral("log_filter"), QStringLiteral("*:Info")) | ||
| 505 | .toString() | ||
| 506 | .toStdString(); | ||
| 507 | Settings::values.use_dev_keys = ReadSetting(QStringLiteral("use_dev_keys"), false).toBool(); | ||
| 498 | 508 | ||
| 499 | qt_config->beginGroup("UI"); | ||
| 500 | UISettings::values.theme = ReadSetting("theme", UISettings::themes[0].second).toString(); | ||
| 501 | UISettings::values.enable_discord_presence = | ||
| 502 | ReadSetting("enable_discord_presence", true).toBool(); | ||
| 503 | UISettings::values.screenshot_resolution_factor = | ||
| 504 | static_cast<u16>(ReadSetting("screenshot_resolution_factor", 0).toUInt()); | ||
| 505 | UISettings::values.select_user_on_boot = ReadSetting("select_user_on_boot", false).toBool(); | ||
| 506 | |||
| 507 | qt_config->beginGroup("UIGameList"); | ||
| 508 | UISettings::values.show_unknown = ReadSetting("show_unknown", true).toBool(); | ||
| 509 | UISettings::values.show_add_ons = ReadSetting("show_add_ons", true).toBool(); | ||
| 510 | UISettings::values.icon_size = ReadSetting("icon_size", 64).toUInt(); | ||
| 511 | UISettings::values.row_1_text_id = ReadSetting("row_1_text_id", 3).toUInt(); | ||
| 512 | UISettings::values.row_2_text_id = ReadSetting("row_2_text_id", 2).toUInt(); | ||
| 513 | qt_config->endGroup(); | 509 | qt_config->endGroup(); |
| 510 | } | ||
| 511 | |||
| 512 | void Config::ReadPathValues() { | ||
| 513 | qt_config->beginGroup(QStringLiteral("Paths")); | ||
| 514 | |||
| 515 | UISettings::values.roms_path = ReadSetting(QStringLiteral("romsPath")).toString(); | ||
| 516 | UISettings::values.symbols_path = ReadSetting(QStringLiteral("symbolsPath")).toString(); | ||
| 517 | UISettings::values.game_directory_path = | ||
| 518 | ReadSetting(QStringLiteral("gameListRootDir"), QStringLiteral(".")).toString(); | ||
| 519 | UISettings::values.game_directory_deepscan = | ||
| 520 | ReadSetting(QStringLiteral("gameListDeepScan"), false).toBool(); | ||
| 521 | UISettings::values.recent_files = ReadSetting(QStringLiteral("recentFiles")).toStringList(); | ||
| 514 | 522 | ||
| 515 | qt_config->beginGroup("UILayout"); | ||
| 516 | UISettings::values.geometry = ReadSetting("geometry").toByteArray(); | ||
| 517 | UISettings::values.state = ReadSetting("state").toByteArray(); | ||
| 518 | UISettings::values.renderwindow_geometry = ReadSetting("geometryRenderWindow").toByteArray(); | ||
| 519 | UISettings::values.gamelist_header_state = ReadSetting("gameListHeaderState").toByteArray(); | ||
| 520 | UISettings::values.microprofile_geometry = | ||
| 521 | ReadSetting("microProfileDialogGeometry").toByteArray(); | ||
| 522 | UISettings::values.microprofile_visible = | ||
| 523 | ReadSetting("microProfileDialogVisible", false).toBool(); | ||
| 524 | qt_config->endGroup(); | 523 | qt_config->endGroup(); |
| 524 | } | ||
| 525 | |||
| 526 | void Config::ReadRendererValues() { | ||
| 527 | qt_config->beginGroup(QStringLiteral("Renderer")); | ||
| 528 | |||
| 529 | Settings::values.resolution_factor = | ||
| 530 | ReadSetting(QStringLiteral("resolution_factor"), 1.0).toFloat(); | ||
| 531 | Settings::values.use_frame_limit = | ||
| 532 | ReadSetting(QStringLiteral("use_frame_limit"), true).toBool(); | ||
| 533 | Settings::values.frame_limit = ReadSetting(QStringLiteral("frame_limit"), 100).toInt(); | ||
| 534 | Settings::values.use_compatibility_profile = | ||
| 535 | ReadSetting(QStringLiteral("use_compatibility_profile"), true).toBool(); | ||
| 536 | Settings::values.use_disk_shader_cache = | ||
| 537 | ReadSetting(QStringLiteral("use_disk_shader_cache"), true).toBool(); | ||
| 538 | Settings::values.use_accurate_gpu_emulation = | ||
| 539 | ReadSetting(QStringLiteral("use_accurate_gpu_emulation"), false).toBool(); | ||
| 540 | Settings::values.use_asynchronous_gpu_emulation = | ||
| 541 | ReadSetting(QStringLiteral("use_asynchronous_gpu_emulation"), false).toBool(); | ||
| 542 | Settings::values.force_30fps_mode = | ||
| 543 | ReadSetting(QStringLiteral("force_30fps_mode"), false).toBool(); | ||
| 544 | |||
| 545 | Settings::values.bg_red = ReadSetting(QStringLiteral("bg_red"), 0.0).toFloat(); | ||
| 546 | Settings::values.bg_green = ReadSetting(QStringLiteral("bg_green"), 0.0).toFloat(); | ||
| 547 | Settings::values.bg_blue = ReadSetting(QStringLiteral("bg_blue"), 0.0).toFloat(); | ||
| 525 | 548 | ||
| 526 | qt_config->beginGroup("Paths"); | ||
| 527 | UISettings::values.roms_path = ReadSetting("romsPath").toString(); | ||
| 528 | UISettings::values.symbols_path = ReadSetting("symbolsPath").toString(); | ||
| 529 | UISettings::values.game_directory_path = ReadSetting("gameListRootDir", ".").toString(); | ||
| 530 | UISettings::values.game_directory_deepscan = ReadSetting("gameListDeepScan", false).toBool(); | ||
| 531 | UISettings::values.recent_files = ReadSetting("recentFiles").toStringList(); | ||
| 532 | qt_config->endGroup(); | 549 | qt_config->endGroup(); |
| 550 | } | ||
| 551 | |||
| 552 | void Config::ReadShortcutValues() { | ||
| 553 | qt_config->beginGroup(QStringLiteral("Shortcuts")); | ||
| 533 | 554 | ||
| 534 | qt_config->beginGroup("Shortcuts"); | 555 | for (const auto& [name, group, shortcut] : default_hotkeys) { |
| 535 | for (auto [name, group, shortcut] : default_hotkeys) { | 556 | const auto& [keyseq, context] = shortcut; |
| 536 | auto [keyseq, context] = shortcut; | ||
| 537 | qt_config->beginGroup(group); | 557 | qt_config->beginGroup(group); |
| 538 | qt_config->beginGroup(name); | 558 | qt_config->beginGroup(name); |
| 539 | UISettings::values.shortcuts.push_back( | 559 | UISettings::values.shortcuts.push_back( |
| 540 | {name, | 560 | {name, |
| 541 | group, | 561 | group, |
| 542 | {ReadSetting("KeySeq", keyseq).toString(), ReadSetting("Context", context).toInt()}}); | 562 | {ReadSetting(QStringLiteral("KeySeq"), keyseq).toString(), |
| 563 | ReadSetting(QStringLiteral("Context"), context).toInt()}}); | ||
| 543 | qt_config->endGroup(); | 564 | qt_config->endGroup(); |
| 544 | qt_config->endGroup(); | 565 | qt_config->endGroup(); |
| 545 | } | 566 | } |
| 567 | |||
| 568 | qt_config->endGroup(); | ||
| 569 | } | ||
| 570 | |||
| 571 | void Config::ReadSystemValues() { | ||
| 572 | qt_config->beginGroup(QStringLiteral("System")); | ||
| 573 | |||
| 574 | Settings::values.use_docked_mode = | ||
| 575 | ReadSetting(QStringLiteral("use_docked_mode"), false).toBool(); | ||
| 576 | |||
| 577 | Settings::values.current_user = std::clamp<int>( | ||
| 578 | ReadSetting(QStringLiteral("current_user"), 0).toInt(), 0, Service::Account::MAX_USERS - 1); | ||
| 579 | |||
| 580 | Settings::values.language_index = ReadSetting(QStringLiteral("language_index"), 1).toInt(); | ||
| 581 | |||
| 582 | const auto rng_seed_enabled = ReadSetting(QStringLiteral("rng_seed_enabled"), false).toBool(); | ||
| 583 | if (rng_seed_enabled) { | ||
| 584 | Settings::values.rng_seed = ReadSetting(QStringLiteral("rng_seed"), 0).toULongLong(); | ||
| 585 | } else { | ||
| 586 | Settings::values.rng_seed = std::nullopt; | ||
| 587 | } | ||
| 588 | |||
| 589 | const auto custom_rtc_enabled = | ||
| 590 | ReadSetting(QStringLiteral("custom_rtc_enabled"), false).toBool(); | ||
| 591 | if (custom_rtc_enabled) { | ||
| 592 | Settings::values.custom_rtc = | ||
| 593 | std::chrono::seconds(ReadSetting(QStringLiteral("custom_rtc"), 0).toULongLong()); | ||
| 594 | } else { | ||
| 595 | Settings::values.custom_rtc = std::nullopt; | ||
| 596 | } | ||
| 597 | |||
| 546 | qt_config->endGroup(); | 598 | qt_config->endGroup(); |
| 599 | } | ||
| 600 | |||
| 601 | void Config::ReadUIValues() { | ||
| 602 | qt_config->beginGroup(QStringLiteral("UI")); | ||
| 547 | 603 | ||
| 548 | UISettings::values.single_window_mode = ReadSetting("singleWindowMode", true).toBool(); | 604 | UISettings::values.theme = |
| 549 | UISettings::values.fullscreen = ReadSetting("fullscreen", false).toBool(); | 605 | ReadSetting(QStringLiteral("theme"), QString::fromUtf8(UISettings::themes[0].second)) |
| 550 | UISettings::values.display_titlebar = ReadSetting("displayTitleBars", true).toBool(); | 606 | .toString(); |
| 551 | UISettings::values.show_filter_bar = ReadSetting("showFilterBar", true).toBool(); | 607 | UISettings::values.enable_discord_presence = |
| 552 | UISettings::values.show_status_bar = ReadSetting("showStatusBar", true).toBool(); | 608 | ReadSetting(QStringLiteral("enable_discord_presence"), true).toBool(); |
| 553 | UISettings::values.confirm_before_closing = ReadSetting("confirmClose", true).toBool(); | 609 | UISettings::values.screenshot_resolution_factor = |
| 554 | UISettings::values.first_start = ReadSetting("firstStart", true).toBool(); | 610 | static_cast<u16>(ReadSetting(QStringLiteral("screenshot_resolution_factor"), 0).toUInt()); |
| 555 | UISettings::values.callout_flags = ReadSetting("calloutFlags", 0).toUInt(); | 611 | UISettings::values.select_user_on_boot = |
| 556 | UISettings::values.show_console = ReadSetting("showConsole", false).toBool(); | 612 | ReadSetting(QStringLiteral("select_user_on_boot"), false).toBool(); |
| 557 | UISettings::values.profile_index = ReadSetting("profileIndex", 0).toUInt(); | 613 | |
| 614 | ReadUIGamelistValues(); | ||
| 615 | ReadUILayoutValues(); | ||
| 616 | ReadPathValues(); | ||
| 617 | ReadShortcutValues(); | ||
| 618 | |||
| 619 | UISettings::values.single_window_mode = | ||
| 620 | ReadSetting(QStringLiteral("singleWindowMode"), true).toBool(); | ||
| 621 | UISettings::values.fullscreen = ReadSetting(QStringLiteral("fullscreen"), false).toBool(); | ||
| 622 | UISettings::values.display_titlebar = | ||
| 623 | ReadSetting(QStringLiteral("displayTitleBars"), true).toBool(); | ||
| 624 | UISettings::values.show_filter_bar = | ||
| 625 | ReadSetting(QStringLiteral("showFilterBar"), true).toBool(); | ||
| 626 | UISettings::values.show_status_bar = | ||
| 627 | ReadSetting(QStringLiteral("showStatusBar"), true).toBool(); | ||
| 628 | UISettings::values.confirm_before_closing = | ||
| 629 | ReadSetting(QStringLiteral("confirmClose"), true).toBool(); | ||
| 630 | UISettings::values.first_start = ReadSetting(QStringLiteral("firstStart"), true).toBool(); | ||
| 631 | UISettings::values.callout_flags = ReadSetting(QStringLiteral("calloutFlags"), 0).toUInt(); | ||
| 632 | UISettings::values.show_console = ReadSetting(QStringLiteral("showConsole"), false).toBool(); | ||
| 633 | UISettings::values.profile_index = ReadSetting(QStringLiteral("profileIndex"), 0).toUInt(); | ||
| 558 | 634 | ||
| 559 | ApplyDefaultProfileIfInputInvalid(); | 635 | ApplyDefaultProfileIfInputInvalid(); |
| 560 | 636 | ||
| 561 | qt_config->endGroup(); | 637 | qt_config->endGroup(); |
| 562 | } | 638 | } |
| 563 | 639 | ||
| 640 | void Config::ReadUIGamelistValues() { | ||
| 641 | qt_config->beginGroup(QStringLiteral("UIGameList")); | ||
| 642 | |||
| 643 | UISettings::values.show_unknown = ReadSetting(QStringLiteral("show_unknown"), true).toBool(); | ||
| 644 | UISettings::values.show_add_ons = ReadSetting(QStringLiteral("show_add_ons"), true).toBool(); | ||
| 645 | UISettings::values.icon_size = ReadSetting(QStringLiteral("icon_size"), 64).toUInt(); | ||
| 646 | UISettings::values.row_1_text_id = ReadSetting(QStringLiteral("row_1_text_id"), 3).toUInt(); | ||
| 647 | UISettings::values.row_2_text_id = ReadSetting(QStringLiteral("row_2_text_id"), 2).toUInt(); | ||
| 648 | UISettings::values.cache_game_list = | ||
| 649 | ReadSetting(QStringLiteral("cache_game_list"), true).toBool(); | ||
| 650 | |||
| 651 | qt_config->endGroup(); | ||
| 652 | } | ||
| 653 | |||
| 654 | void Config::ReadUILayoutValues() { | ||
| 655 | qt_config->beginGroup(QStringLiteral("UILayout")); | ||
| 656 | |||
| 657 | UISettings::values.geometry = ReadSetting(QStringLiteral("geometry")).toByteArray(); | ||
| 658 | UISettings::values.state = ReadSetting(QStringLiteral("state")).toByteArray(); | ||
| 659 | UISettings::values.renderwindow_geometry = | ||
| 660 | ReadSetting(QStringLiteral("geometryRenderWindow")).toByteArray(); | ||
| 661 | UISettings::values.gamelist_header_state = | ||
| 662 | ReadSetting(QStringLiteral("gameListHeaderState")).toByteArray(); | ||
| 663 | UISettings::values.microprofile_geometry = | ||
| 664 | ReadSetting(QStringLiteral("microProfileDialogGeometry")).toByteArray(); | ||
| 665 | UISettings::values.microprofile_visible = | ||
| 666 | ReadSetting(QStringLiteral("microProfileDialogVisible"), false).toBool(); | ||
| 667 | |||
| 668 | qt_config->endGroup(); | ||
| 669 | } | ||
| 670 | |||
| 671 | void Config::ReadWebServiceValues() { | ||
| 672 | qt_config->beginGroup(QStringLiteral("WebService")); | ||
| 673 | |||
| 674 | Settings::values.enable_telemetry = | ||
| 675 | ReadSetting(QStringLiteral("enable_telemetry"), true).toBool(); | ||
| 676 | Settings::values.web_api_url = | ||
| 677 | ReadSetting(QStringLiteral("web_api_url"), QStringLiteral("https://api.yuzu-emu.org")) | ||
| 678 | .toString() | ||
| 679 | .toStdString(); | ||
| 680 | Settings::values.yuzu_username = | ||
| 681 | ReadSetting(QStringLiteral("yuzu_username")).toString().toStdString(); | ||
| 682 | Settings::values.yuzu_token = | ||
| 683 | ReadSetting(QStringLiteral("yuzu_token")).toString().toStdString(); | ||
| 684 | |||
| 685 | qt_config->endGroup(); | ||
| 686 | } | ||
| 687 | |||
| 688 | void Config::ReadValues() { | ||
| 689 | ReadControlValues(); | ||
| 690 | ReadCoreValues(); | ||
| 691 | ReadRendererValues(); | ||
| 692 | ReadAudioValues(); | ||
| 693 | ReadDataStorageValues(); | ||
| 694 | ReadSystemValues(); | ||
| 695 | ReadMiscellaneousValues(); | ||
| 696 | ReadDebugValues(); | ||
| 697 | ReadWebServiceValues(); | ||
| 698 | ReadDisabledAddOnValues(); | ||
| 699 | ReadUIValues(); | ||
| 700 | } | ||
| 701 | |||
| 564 | void Config::SavePlayerValues() { | 702 | void Config::SavePlayerValues() { |
| 565 | for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { | 703 | for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { |
| 566 | const auto& player = Settings::values.players[p]; | 704 | const auto& player = Settings::values.players[p]; |
| 567 | 705 | ||
| 568 | WriteSetting(QString("player_%1_connected").arg(p), player.connected, false); | 706 | WriteSetting(QStringLiteral("player_%1_connected").arg(p), player.connected, false); |
| 569 | WriteSetting(QString("player_%1_type").arg(p), static_cast<u8>(player.type), | 707 | WriteSetting(QStringLiteral("player_%1_type").arg(p), static_cast<u8>(player.type), |
| 570 | static_cast<u8>(Settings::ControllerType::DualJoycon)); | 708 | static_cast<u8>(Settings::ControllerType::DualJoycon)); |
| 571 | 709 | ||
| 572 | WriteSetting(QString("player_%1_body_color_left").arg(p), player.body_color_left, | 710 | WriteSetting(QStringLiteral("player_%1_body_color_left").arg(p), player.body_color_left, |
| 573 | Settings::JOYCON_BODY_NEON_BLUE); | 711 | Settings::JOYCON_BODY_NEON_BLUE); |
| 574 | WriteSetting(QString("player_%1_body_color_right").arg(p), player.body_color_right, | 712 | WriteSetting(QStringLiteral("player_%1_body_color_right").arg(p), player.body_color_right, |
| 575 | Settings::JOYCON_BODY_NEON_RED); | 713 | Settings::JOYCON_BODY_NEON_RED); |
| 576 | WriteSetting(QString("player_%1_button_color_left").arg(p), player.button_color_left, | 714 | WriteSetting(QStringLiteral("player_%1_button_color_left").arg(p), player.button_color_left, |
| 577 | Settings::JOYCON_BUTTONS_NEON_BLUE); | 715 | Settings::JOYCON_BUTTONS_NEON_BLUE); |
| 578 | WriteSetting(QString("player_%1_button_color_right").arg(p), player.button_color_right, | 716 | WriteSetting(QStringLiteral("player_%1_button_color_right").arg(p), |
| 579 | Settings::JOYCON_BUTTONS_NEON_RED); | 717 | player.button_color_right, Settings::JOYCON_BUTTONS_NEON_RED); |
| 580 | 718 | ||
| 581 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | 719 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { |
| 582 | std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); | 720 | const std::string default_param = |
| 583 | WriteSetting(QString("player_%1_").arg(p) + | 721 | InputCommon::GenerateKeyboardParam(default_buttons[i]); |
| 722 | WriteSetting(QStringLiteral("player_%1_").arg(p) + | ||
| 584 | QString::fromStdString(Settings::NativeButton::mapping[i]), | 723 | QString::fromStdString(Settings::NativeButton::mapping[i]), |
| 585 | QString::fromStdString(player.buttons[i]), | 724 | QString::fromStdString(player.buttons[i]), |
| 586 | QString::fromStdString(default_param)); | 725 | QString::fromStdString(default_param)); |
| 587 | } | 726 | } |
| 588 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | 727 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { |
| 589 | std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | 728 | const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( |
| 590 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], | 729 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], |
| 591 | default_analogs[i][3], default_analogs[i][4], 0.5f); | 730 | default_analogs[i][3], default_analogs[i][4], 0.5f); |
| 592 | WriteSetting(QString("player_%1_").arg(p) + | 731 | WriteSetting(QStringLiteral("player_%1_").arg(p) + |
| 593 | QString::fromStdString(Settings::NativeAnalog::mapping[i]), | 732 | QString::fromStdString(Settings::NativeAnalog::mapping[i]), |
| 594 | QString::fromStdString(player.analogs[i]), | 733 | QString::fromStdString(player.analogs[i]), |
| 595 | QString::fromStdString(default_param)); | 734 | QString::fromStdString(default_param)); |
| @@ -598,19 +737,19 @@ void Config::SavePlayerValues() { | |||
| 598 | } | 737 | } |
| 599 | 738 | ||
| 600 | void Config::SaveDebugValues() { | 739 | void Config::SaveDebugValues() { |
| 601 | WriteSetting("debug_pad_enabled", Settings::values.debug_pad_enabled, false); | 740 | WriteSetting(QStringLiteral("debug_pad_enabled"), Settings::values.debug_pad_enabled, false); |
| 602 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | 741 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { |
| 603 | std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); | 742 | const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); |
| 604 | WriteSetting(QString("debug_pad_") + | 743 | WriteSetting(QStringLiteral("debug_pad_") + |
| 605 | QString::fromStdString(Settings::NativeButton::mapping[i]), | 744 | QString::fromStdString(Settings::NativeButton::mapping[i]), |
| 606 | QString::fromStdString(Settings::values.debug_pad_buttons[i]), | 745 | QString::fromStdString(Settings::values.debug_pad_buttons[i]), |
| 607 | QString::fromStdString(default_param)); | 746 | QString::fromStdString(default_param)); |
| 608 | } | 747 | } |
| 609 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | 748 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { |
| 610 | std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | 749 | const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( |
| 611 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], | 750 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], |
| 612 | default_analogs[i][3], default_analogs[i][4], 0.5f); | 751 | default_analogs[i][3], default_analogs[i][4], 0.5f); |
| 613 | WriteSetting(QString("debug_pad_") + | 752 | WriteSetting(QStringLiteral("debug_pad_") + |
| 614 | QString::fromStdString(Settings::NativeAnalog::mapping[i]), | 753 | QString::fromStdString(Settings::NativeAnalog::mapping[i]), |
| 615 | QString::fromStdString(Settings::values.debug_pad_analogs[i]), | 754 | QString::fromStdString(Settings::values.debug_pad_analogs[i]), |
| 616 | QString::fromStdString(default_param)); | 755 | QString::fromStdString(default_param)); |
| @@ -618,11 +757,12 @@ void Config::SaveDebugValues() { | |||
| 618 | } | 757 | } |
| 619 | 758 | ||
| 620 | void Config::SaveMouseValues() { | 759 | void Config::SaveMouseValues() { |
| 621 | WriteSetting("mouse_enabled", Settings::values.mouse_enabled, false); | 760 | WriteSetting(QStringLiteral("mouse_enabled"), Settings::values.mouse_enabled, false); |
| 622 | 761 | ||
| 623 | for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { | 762 | for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { |
| 624 | std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]); | 763 | const std::string default_param = |
| 625 | WriteSetting(QString("mouse_") + | 764 | InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]); |
| 765 | WriteSetting(QStringLiteral("mouse_") + | ||
| 626 | QString::fromStdString(Settings::NativeMouseButton::mapping[i]), | 766 | QString::fromStdString(Settings::NativeMouseButton::mapping[i]), |
| 627 | QString::fromStdString(Settings::values.mouse_buttons[i]), | 767 | QString::fromStdString(Settings::values.mouse_buttons[i]), |
| 628 | QString::fromStdString(default_param)); | 768 | QString::fromStdString(default_param)); |
| @@ -630,178 +770,278 @@ void Config::SaveMouseValues() { | |||
| 630 | } | 770 | } |
| 631 | 771 | ||
| 632 | void Config::SaveTouchscreenValues() { | 772 | void Config::SaveTouchscreenValues() { |
| 633 | WriteSetting("touchscreen_enabled", Settings::values.touchscreen.enabled, true); | 773 | const auto& touchscreen = Settings::values.touchscreen; |
| 634 | WriteSetting("touchscreen_device", QString::fromStdString(Settings::values.touchscreen.device), | 774 | |
| 635 | "engine:emu_window"); | 775 | WriteSetting(QStringLiteral("touchscreen_enabled"), touchscreen.enabled, true); |
| 776 | WriteSetting(QStringLiteral("touchscreen_device"), QString::fromStdString(touchscreen.device), | ||
| 777 | QStringLiteral("engine:emu_window")); | ||
| 636 | 778 | ||
| 637 | WriteSetting("touchscreen_finger", Settings::values.touchscreen.finger, 0); | 779 | WriteSetting(QStringLiteral("touchscreen_finger"), touchscreen.finger, 0); |
| 638 | WriteSetting("touchscreen_angle", Settings::values.touchscreen.rotation_angle, 0); | 780 | WriteSetting(QStringLiteral("touchscreen_angle"), touchscreen.rotation_angle, 0); |
| 639 | WriteSetting("touchscreen_diameter_x", Settings::values.touchscreen.diameter_x, 15); | 781 | WriteSetting(QStringLiteral("touchscreen_diameter_x"), touchscreen.diameter_x, 15); |
| 640 | WriteSetting("touchscreen_diameter_y", Settings::values.touchscreen.diameter_y, 15); | 782 | WriteSetting(QStringLiteral("touchscreen_diameter_y"), touchscreen.diameter_y, 15); |
| 641 | } | 783 | } |
| 642 | 784 | ||
| 643 | void Config::SaveValues() { | 785 | void Config::SaveValues() { |
| 644 | qt_config->beginGroup("Controls"); | 786 | SaveControlValues(); |
| 787 | SaveCoreValues(); | ||
| 788 | SaveRendererValues(); | ||
| 789 | SaveAudioValues(); | ||
| 790 | SaveDataStorageValues(); | ||
| 791 | SaveSystemValues(); | ||
| 792 | SaveMiscellaneousValues(); | ||
| 793 | SaveDebuggingValues(); | ||
| 794 | SaveWebServiceValues(); | ||
| 795 | SaveDisabledAddOnValues(); | ||
| 796 | SaveUIValues(); | ||
| 797 | } | ||
| 798 | |||
| 799 | void Config::SaveAudioValues() { | ||
| 800 | qt_config->beginGroup(QStringLiteral("Audio")); | ||
| 801 | |||
| 802 | WriteSetting(QStringLiteral("output_engine"), QString::fromStdString(Settings::values.sink_id), | ||
| 803 | QStringLiteral("auto")); | ||
| 804 | WriteSetting(QStringLiteral("enable_audio_stretching"), | ||
| 805 | Settings::values.enable_audio_stretching, true); | ||
| 806 | WriteSetting(QStringLiteral("output_device"), | ||
| 807 | QString::fromStdString(Settings::values.audio_device_id), QStringLiteral("auto")); | ||
| 808 | WriteSetting(QStringLiteral("volume"), Settings::values.volume, 1.0f); | ||
| 809 | |||
| 810 | qt_config->endGroup(); | ||
| 811 | } | ||
| 812 | |||
| 813 | void Config::SaveControlValues() { | ||
| 814 | qt_config->beginGroup(QStringLiteral("Controls")); | ||
| 645 | 815 | ||
| 646 | SavePlayerValues(); | 816 | SavePlayerValues(); |
| 647 | SaveDebugValues(); | 817 | SaveDebugValues(); |
| 648 | SaveMouseValues(); | 818 | SaveMouseValues(); |
| 649 | SaveTouchscreenValues(); | 819 | SaveTouchscreenValues(); |
| 650 | 820 | ||
| 651 | WriteSetting("motion_device", QString::fromStdString(Settings::values.motion_device), | 821 | WriteSetting(QStringLiteral("motion_device"), |
| 652 | "engine:motion_emu,update_period:100,sensitivity:0.01"); | 822 | QString::fromStdString(Settings::values.motion_device), |
| 653 | WriteSetting("keyboard_enabled", Settings::values.keyboard_enabled, false); | 823 | QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01")); |
| 824 | WriteSetting(QStringLiteral("keyboard_enabled"), Settings::values.keyboard_enabled, false); | ||
| 654 | 825 | ||
| 655 | qt_config->endGroup(); | 826 | qt_config->endGroup(); |
| 827 | } | ||
| 656 | 828 | ||
| 657 | qt_config->beginGroup("Core"); | 829 | void Config::SaveCoreValues() { |
| 658 | WriteSetting("use_cpu_jit", Settings::values.use_cpu_jit, true); | 830 | qt_config->beginGroup(QStringLiteral("Core")); |
| 659 | WriteSetting("use_multi_core", Settings::values.use_multi_core, false); | ||
| 660 | qt_config->endGroup(); | ||
| 661 | 831 | ||
| 662 | qt_config->beginGroup("Renderer"); | 832 | WriteSetting(QStringLiteral("use_cpu_jit"), Settings::values.use_cpu_jit, true); |
| 663 | WriteSetting("resolution_factor", (double)Settings::values.resolution_factor, 1.0); | 833 | WriteSetting(QStringLiteral("use_multi_core"), Settings::values.use_multi_core, false); |
| 664 | WriteSetting("use_frame_limit", Settings::values.use_frame_limit, true); | ||
| 665 | WriteSetting("frame_limit", Settings::values.frame_limit, 100); | ||
| 666 | WriteSetting("use_compatibility_profile", Settings::values.use_compatibility_profile, true); | ||
| 667 | WriteSetting("use_disk_shader_cache", Settings::values.use_disk_shader_cache, true); | ||
| 668 | WriteSetting("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation, false); | ||
| 669 | WriteSetting("use_asynchronous_gpu_emulation", Settings::values.use_asynchronous_gpu_emulation, | ||
| 670 | false); | ||
| 671 | WriteSetting("force_30fps_mode", Settings::values.force_30fps_mode, false); | ||
| 672 | 834 | ||
| 673 | // Cast to double because Qt's written float values are not human-readable | ||
| 674 | WriteSetting("bg_red", (double)Settings::values.bg_red, 0.0); | ||
| 675 | WriteSetting("bg_green", (double)Settings::values.bg_green, 0.0); | ||
| 676 | WriteSetting("bg_blue", (double)Settings::values.bg_blue, 0.0); | ||
| 677 | qt_config->endGroup(); | 835 | qt_config->endGroup(); |
| 836 | } | ||
| 678 | 837 | ||
| 679 | qt_config->beginGroup("Audio"); | 838 | void Config::SaveDataStorageValues() { |
| 680 | WriteSetting("output_engine", QString::fromStdString(Settings::values.sink_id), "auto"); | 839 | qt_config->beginGroup(QStringLiteral("Data Storage")); |
| 681 | WriteSetting("enable_audio_stretching", Settings::values.enable_audio_stretching, true); | ||
| 682 | WriteSetting("output_device", QString::fromStdString(Settings::values.audio_device_id), "auto"); | ||
| 683 | WriteSetting("volume", Settings::values.volume, 1.0f); | ||
| 684 | qt_config->endGroup(); | ||
| 685 | 840 | ||
| 686 | qt_config->beginGroup("Data Storage"); | 841 | WriteSetting(QStringLiteral("use_virtual_sd"), Settings::values.use_virtual_sd, true); |
| 687 | WriteSetting("use_virtual_sd", Settings::values.use_virtual_sd, true); | 842 | WriteSetting(QStringLiteral("nand_directory"), |
| 688 | WriteSetting("nand_directory", | ||
| 689 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)), | 843 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)), |
| 690 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))); | 844 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))); |
| 691 | WriteSetting("sdmc_directory", | 845 | WriteSetting(QStringLiteral("sdmc_directory"), |
| 692 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)), | 846 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)), |
| 693 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); | 847 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); |
| 694 | qt_config->endGroup(); | ||
| 695 | |||
| 696 | qt_config->beginGroup("System"); | ||
| 697 | WriteSetting("use_docked_mode", Settings::values.use_docked_mode, false); | ||
| 698 | WriteSetting("current_user", Settings::values.current_user, 0); | ||
| 699 | WriteSetting("language_index", Settings::values.language_index, 1); | ||
| 700 | |||
| 701 | WriteSetting("rng_seed_enabled", Settings::values.rng_seed.has_value(), false); | ||
| 702 | WriteSetting("rng_seed", Settings::values.rng_seed.value_or(0), 0); | ||
| 703 | |||
| 704 | WriteSetting("custom_rtc_enabled", Settings::values.custom_rtc.has_value(), false); | ||
| 705 | WriteSetting("custom_rtc", | ||
| 706 | QVariant::fromValue<long long>( | ||
| 707 | Settings::values.custom_rtc.value_or(std::chrono::seconds{}).count()), | ||
| 708 | 0); | ||
| 709 | 848 | ||
| 710 | qt_config->endGroup(); | 849 | qt_config->endGroup(); |
| 850 | } | ||
| 711 | 851 | ||
| 712 | qt_config->beginGroup("Miscellaneous"); | 852 | void Config::SaveDebuggingValues() { |
| 713 | WriteSetting("log_filter", QString::fromStdString(Settings::values.log_filter), "*:Info"); | 853 | qt_config->beginGroup(QStringLiteral("Debugging")); |
| 714 | WriteSetting("use_dev_keys", Settings::values.use_dev_keys, false); | ||
| 715 | qt_config->endGroup(); | ||
| 716 | 854 | ||
| 717 | qt_config->beginGroup("Debugging"); | 855 | WriteSetting(QStringLiteral("use_gdbstub"), Settings::values.use_gdbstub, false); |
| 718 | WriteSetting("use_gdbstub", Settings::values.use_gdbstub, false); | 856 | WriteSetting(QStringLiteral("gdbstub_port"), Settings::values.gdbstub_port, 24689); |
| 719 | WriteSetting("gdbstub_port", Settings::values.gdbstub_port, 24689); | 857 | WriteSetting(QStringLiteral("program_args"), |
| 720 | WriteSetting("program_args", QString::fromStdString(Settings::values.program_args), ""); | 858 | QString::fromStdString(Settings::values.program_args), QStringLiteral("")); |
| 721 | WriteSetting("dump_exefs", Settings::values.dump_exefs, false); | 859 | WriteSetting(QStringLiteral("dump_exefs"), Settings::values.dump_exefs, false); |
| 722 | WriteSetting("dump_nso", Settings::values.dump_nso, false); | 860 | WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false); |
| 723 | qt_config->endGroup(); | ||
| 724 | 861 | ||
| 725 | qt_config->beginGroup("WebService"); | ||
| 726 | WriteSetting("enable_telemetry", Settings::values.enable_telemetry, true); | ||
| 727 | WriteSetting("web_api_url", QString::fromStdString(Settings::values.web_api_url), | ||
| 728 | "https://api.yuzu-emu.org"); | ||
| 729 | WriteSetting("yuzu_username", QString::fromStdString(Settings::values.yuzu_username)); | ||
| 730 | WriteSetting("yuzu_token", QString::fromStdString(Settings::values.yuzu_token)); | ||
| 731 | qt_config->endGroup(); | 862 | qt_config->endGroup(); |
| 863 | } | ||
| 864 | |||
| 865 | void Config::SaveDisabledAddOnValues() { | ||
| 866 | qt_config->beginWriteArray(QStringLiteral("DisabledAddOns")); | ||
| 732 | 867 | ||
| 733 | qt_config->beginWriteArray("DisabledAddOns"); | ||
| 734 | int i = 0; | 868 | int i = 0; |
| 735 | for (const auto& elem : Settings::values.disabled_addons) { | 869 | for (const auto& elem : Settings::values.disabled_addons) { |
| 736 | qt_config->setArrayIndex(i); | 870 | qt_config->setArrayIndex(i); |
| 737 | WriteSetting("title_id", QVariant::fromValue<u64>(elem.first), 0); | 871 | WriteSetting(QStringLiteral("title_id"), QVariant::fromValue<u64>(elem.first), 0); |
| 738 | qt_config->beginWriteArray("disabled"); | 872 | qt_config->beginWriteArray(QStringLiteral("disabled")); |
| 739 | for (std::size_t j = 0; j < elem.second.size(); ++j) { | 873 | for (std::size_t j = 0; j < elem.second.size(); ++j) { |
| 740 | qt_config->setArrayIndex(static_cast<int>(j)); | 874 | qt_config->setArrayIndex(static_cast<int>(j)); |
| 741 | WriteSetting("d", QString::fromStdString(elem.second[j]), ""); | 875 | WriteSetting(QStringLiteral("d"), QString::fromStdString(elem.second[j]), |
| 876 | QStringLiteral("")); | ||
| 742 | } | 877 | } |
| 743 | qt_config->endArray(); | 878 | qt_config->endArray(); |
| 744 | ++i; | 879 | ++i; |
| 745 | } | 880 | } |
| 881 | |||
| 746 | qt_config->endArray(); | 882 | qt_config->endArray(); |
| 883 | } | ||
| 884 | |||
| 885 | void Config::SaveMiscellaneousValues() { | ||
| 886 | qt_config->beginGroup(QStringLiteral("Miscellaneous")); | ||
| 887 | |||
| 888 | WriteSetting(QStringLiteral("log_filter"), QString::fromStdString(Settings::values.log_filter), | ||
| 889 | QStringLiteral("*:Info")); | ||
| 890 | WriteSetting(QStringLiteral("use_dev_keys"), Settings::values.use_dev_keys, false); | ||
| 747 | 891 | ||
| 748 | qt_config->beginGroup("UI"); | ||
| 749 | WriteSetting("theme", UISettings::values.theme, UISettings::themes[0].second); | ||
| 750 | WriteSetting("enable_discord_presence", UISettings::values.enable_discord_presence, true); | ||
| 751 | WriteSetting("screenshot_resolution_factor", UISettings::values.screenshot_resolution_factor, | ||
| 752 | 0); | ||
| 753 | WriteSetting("select_user_on_boot", UISettings::values.select_user_on_boot, false); | ||
| 754 | |||
| 755 | qt_config->beginGroup("UIGameList"); | ||
| 756 | WriteSetting("show_unknown", UISettings::values.show_unknown, true); | ||
| 757 | WriteSetting("show_add_ons", UISettings::values.show_add_ons, true); | ||
| 758 | WriteSetting("icon_size", UISettings::values.icon_size, 64); | ||
| 759 | WriteSetting("row_1_text_id", UISettings::values.row_1_text_id, 3); | ||
| 760 | WriteSetting("row_2_text_id", UISettings::values.row_2_text_id, 2); | ||
| 761 | qt_config->endGroup(); | 892 | qt_config->endGroup(); |
| 893 | } | ||
| 894 | |||
| 895 | void Config::SavePathValues() { | ||
| 896 | qt_config->beginGroup(QStringLiteral("Paths")); | ||
| 897 | |||
| 898 | WriteSetting(QStringLiteral("romsPath"), UISettings::values.roms_path); | ||
| 899 | WriteSetting(QStringLiteral("symbolsPath"), UISettings::values.symbols_path); | ||
| 900 | WriteSetting(QStringLiteral("screenshotPath"), UISettings::values.screenshot_path); | ||
| 901 | WriteSetting(QStringLiteral("gameListRootDir"), UISettings::values.game_directory_path, | ||
| 902 | QStringLiteral(".")); | ||
| 903 | WriteSetting(QStringLiteral("gameListDeepScan"), UISettings::values.game_directory_deepscan, | ||
| 904 | false); | ||
| 905 | WriteSetting(QStringLiteral("recentFiles"), UISettings::values.recent_files); | ||
| 762 | 906 | ||
| 763 | qt_config->beginGroup("UILayout"); | ||
| 764 | WriteSetting("geometry", UISettings::values.geometry); | ||
| 765 | WriteSetting("state", UISettings::values.state); | ||
| 766 | WriteSetting("geometryRenderWindow", UISettings::values.renderwindow_geometry); | ||
| 767 | WriteSetting("gameListHeaderState", UISettings::values.gamelist_header_state); | ||
| 768 | WriteSetting("microProfileDialogGeometry", UISettings::values.microprofile_geometry); | ||
| 769 | WriteSetting("microProfileDialogVisible", UISettings::values.microprofile_visible, false); | ||
| 770 | qt_config->endGroup(); | 907 | qt_config->endGroup(); |
| 908 | } | ||
| 909 | |||
| 910 | void Config::SaveRendererValues() { | ||
| 911 | qt_config->beginGroup(QStringLiteral("Renderer")); | ||
| 912 | |||
| 913 | WriteSetting(QStringLiteral("resolution_factor"), | ||
| 914 | static_cast<double>(Settings::values.resolution_factor), 1.0); | ||
| 915 | WriteSetting(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true); | ||
| 916 | WriteSetting(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100); | ||
| 917 | WriteSetting(QStringLiteral("use_compatibility_profile"), | ||
| 918 | Settings::values.use_compatibility_profile, true); | ||
| 919 | WriteSetting(QStringLiteral("use_disk_shader_cache"), Settings::values.use_disk_shader_cache, | ||
| 920 | true); | ||
| 921 | WriteSetting(QStringLiteral("use_accurate_gpu_emulation"), | ||
| 922 | Settings::values.use_accurate_gpu_emulation, false); | ||
| 923 | WriteSetting(QStringLiteral("use_asynchronous_gpu_emulation"), | ||
| 924 | Settings::values.use_asynchronous_gpu_emulation, false); | ||
| 925 | WriteSetting(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode, false); | ||
| 926 | |||
| 927 | // Cast to double because Qt's written float values are not human-readable | ||
| 928 | WriteSetting(QStringLiteral("bg_red"), static_cast<double>(Settings::values.bg_red), 0.0); | ||
| 929 | WriteSetting(QStringLiteral("bg_green"), static_cast<double>(Settings::values.bg_green), 0.0); | ||
| 930 | WriteSetting(QStringLiteral("bg_blue"), static_cast<double>(Settings::values.bg_blue), 0.0); | ||
| 771 | 931 | ||
| 772 | qt_config->beginGroup("Paths"); | ||
| 773 | WriteSetting("romsPath", UISettings::values.roms_path); | ||
| 774 | WriteSetting("symbolsPath", UISettings::values.symbols_path); | ||
| 775 | WriteSetting("screenshotPath", UISettings::values.screenshot_path); | ||
| 776 | WriteSetting("gameListRootDir", UISettings::values.game_directory_path, "."); | ||
| 777 | WriteSetting("gameListDeepScan", UISettings::values.game_directory_deepscan, false); | ||
| 778 | WriteSetting("recentFiles", UISettings::values.recent_files); | ||
| 779 | qt_config->endGroup(); | 932 | qt_config->endGroup(); |
| 933 | } | ||
| 934 | |||
| 935 | void Config::SaveShortcutValues() { | ||
| 936 | qt_config->beginGroup(QStringLiteral("Shortcuts")); | ||
| 780 | 937 | ||
| 781 | qt_config->beginGroup("Shortcuts"); | ||
| 782 | // Lengths of UISettings::values.shortcuts & default_hotkeys are same. | 938 | // Lengths of UISettings::values.shortcuts & default_hotkeys are same. |
| 783 | // However, their ordering must also be the same. | 939 | // However, their ordering must also be the same. |
| 784 | for (std::size_t i = 0; i < default_hotkeys.size(); i++) { | 940 | for (std::size_t i = 0; i < default_hotkeys.size(); i++) { |
| 785 | auto [name, group, shortcut] = UISettings::values.shortcuts[i]; | 941 | const auto& [name, group, shortcut] = UISettings::values.shortcuts[i]; |
| 942 | const auto& default_hotkey = default_hotkeys[i].shortcut; | ||
| 943 | |||
| 786 | qt_config->beginGroup(group); | 944 | qt_config->beginGroup(group); |
| 787 | qt_config->beginGroup(name); | 945 | qt_config->beginGroup(name); |
| 788 | WriteSetting("KeySeq", shortcut.first, default_hotkeys[i].shortcut.first); | 946 | WriteSetting(QStringLiteral("KeySeq"), shortcut.first, default_hotkey.first); |
| 789 | WriteSetting("Context", shortcut.second, default_hotkeys[i].shortcut.second); | 947 | WriteSetting(QStringLiteral("Context"), shortcut.second, default_hotkey.second); |
| 790 | qt_config->endGroup(); | 948 | qt_config->endGroup(); |
| 791 | qt_config->endGroup(); | 949 | qt_config->endGroup(); |
| 792 | } | 950 | } |
| 951 | |||
| 952 | qt_config->endGroup(); | ||
| 953 | } | ||
| 954 | |||
| 955 | void Config::SaveSystemValues() { | ||
| 956 | qt_config->beginGroup(QStringLiteral("System")); | ||
| 957 | |||
| 958 | WriteSetting(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false); | ||
| 959 | WriteSetting(QStringLiteral("current_user"), Settings::values.current_user, 0); | ||
| 960 | WriteSetting(QStringLiteral("language_index"), Settings::values.language_index, 1); | ||
| 961 | |||
| 962 | WriteSetting(QStringLiteral("rng_seed_enabled"), Settings::values.rng_seed.has_value(), false); | ||
| 963 | WriteSetting(QStringLiteral("rng_seed"), Settings::values.rng_seed.value_or(0), 0); | ||
| 964 | |||
| 965 | WriteSetting(QStringLiteral("custom_rtc_enabled"), Settings::values.custom_rtc.has_value(), | ||
| 966 | false); | ||
| 967 | WriteSetting(QStringLiteral("custom_rtc"), | ||
| 968 | QVariant::fromValue<long long>( | ||
| 969 | Settings::values.custom_rtc.value_or(std::chrono::seconds{}).count()), | ||
| 970 | 0); | ||
| 971 | |||
| 972 | qt_config->endGroup(); | ||
| 973 | } | ||
| 974 | |||
| 975 | void Config::SaveUIValues() { | ||
| 976 | qt_config->beginGroup(QStringLiteral("UI")); | ||
| 977 | |||
| 978 | WriteSetting(QStringLiteral("theme"), UISettings::values.theme, | ||
| 979 | QString::fromUtf8(UISettings::themes[0].second)); | ||
| 980 | WriteSetting(QStringLiteral("enable_discord_presence"), | ||
| 981 | UISettings::values.enable_discord_presence, true); | ||
| 982 | WriteSetting(QStringLiteral("screenshot_resolution_factor"), | ||
| 983 | UISettings::values.screenshot_resolution_factor, 0); | ||
| 984 | WriteSetting(QStringLiteral("select_user_on_boot"), UISettings::values.select_user_on_boot, | ||
| 985 | false); | ||
| 986 | |||
| 987 | SaveUIGamelistValues(); | ||
| 988 | SaveUILayoutValues(); | ||
| 989 | SavePathValues(); | ||
| 990 | SaveShortcutValues(); | ||
| 991 | |||
| 992 | WriteSetting(QStringLiteral("singleWindowMode"), UISettings::values.single_window_mode, true); | ||
| 993 | WriteSetting(QStringLiteral("fullscreen"), UISettings::values.fullscreen, false); | ||
| 994 | WriteSetting(QStringLiteral("displayTitleBars"), UISettings::values.display_titlebar, true); | ||
| 995 | WriteSetting(QStringLiteral("showFilterBar"), UISettings::values.show_filter_bar, true); | ||
| 996 | WriteSetting(QStringLiteral("showStatusBar"), UISettings::values.show_status_bar, true); | ||
| 997 | WriteSetting(QStringLiteral("confirmClose"), UISettings::values.confirm_before_closing, true); | ||
| 998 | WriteSetting(QStringLiteral("firstStart"), UISettings::values.first_start, true); | ||
| 999 | WriteSetting(QStringLiteral("calloutFlags"), UISettings::values.callout_flags, 0); | ||
| 1000 | WriteSetting(QStringLiteral("showConsole"), UISettings::values.show_console, false); | ||
| 1001 | WriteSetting(QStringLiteral("profileIndex"), UISettings::values.profile_index, 0); | ||
| 1002 | |||
| 1003 | qt_config->endGroup(); | ||
| 1004 | } | ||
| 1005 | |||
| 1006 | void Config::SaveUIGamelistValues() { | ||
| 1007 | qt_config->beginGroup(QStringLiteral("UIGameList")); | ||
| 1008 | |||
| 1009 | WriteSetting(QStringLiteral("show_unknown"), UISettings::values.show_unknown, true); | ||
| 1010 | WriteSetting(QStringLiteral("show_add_ons"), UISettings::values.show_add_ons, true); | ||
| 1011 | WriteSetting(QStringLiteral("icon_size"), UISettings::values.icon_size, 64); | ||
| 1012 | WriteSetting(QStringLiteral("row_1_text_id"), UISettings::values.row_1_text_id, 3); | ||
| 1013 | WriteSetting(QStringLiteral("row_2_text_id"), UISettings::values.row_2_text_id, 2); | ||
| 1014 | WriteSetting(QStringLiteral("cache_game_list"), UISettings::values.cache_game_list, true); | ||
| 1015 | |||
| 1016 | qt_config->endGroup(); | ||
| 1017 | } | ||
| 1018 | |||
| 1019 | void Config::SaveUILayoutValues() { | ||
| 1020 | qt_config->beginGroup(QStringLiteral("UILayout")); | ||
| 1021 | |||
| 1022 | WriteSetting(QStringLiteral("geometry"), UISettings::values.geometry); | ||
| 1023 | WriteSetting(QStringLiteral("state"), UISettings::values.state); | ||
| 1024 | WriteSetting(QStringLiteral("geometryRenderWindow"), UISettings::values.renderwindow_geometry); | ||
| 1025 | WriteSetting(QStringLiteral("gameListHeaderState"), UISettings::values.gamelist_header_state); | ||
| 1026 | WriteSetting(QStringLiteral("microProfileDialogGeometry"), | ||
| 1027 | UISettings::values.microprofile_geometry); | ||
| 1028 | WriteSetting(QStringLiteral("microProfileDialogVisible"), | ||
| 1029 | UISettings::values.microprofile_visible, false); | ||
| 1030 | |||
| 793 | qt_config->endGroup(); | 1031 | qt_config->endGroup(); |
| 1032 | } | ||
| 1033 | |||
| 1034 | void Config::SaveWebServiceValues() { | ||
| 1035 | qt_config->beginGroup(QStringLiteral("WebService")); | ||
| 1036 | |||
| 1037 | WriteSetting(QStringLiteral("enable_telemetry"), Settings::values.enable_telemetry, true); | ||
| 1038 | WriteSetting(QStringLiteral("web_api_url"), | ||
| 1039 | QString::fromStdString(Settings::values.web_api_url), | ||
| 1040 | QStringLiteral("https://api.yuzu-emu.org")); | ||
| 1041 | WriteSetting(QStringLiteral("yuzu_username"), | ||
| 1042 | QString::fromStdString(Settings::values.yuzu_username)); | ||
| 1043 | WriteSetting(QStringLiteral("yuzu_token"), QString::fromStdString(Settings::values.yuzu_token)); | ||
| 794 | 1044 | ||
| 795 | WriteSetting("singleWindowMode", UISettings::values.single_window_mode, true); | ||
| 796 | WriteSetting("fullscreen", UISettings::values.fullscreen, false); | ||
| 797 | WriteSetting("displayTitleBars", UISettings::values.display_titlebar, true); | ||
| 798 | WriteSetting("showFilterBar", UISettings::values.show_filter_bar, true); | ||
| 799 | WriteSetting("showStatusBar", UISettings::values.show_status_bar, true); | ||
| 800 | WriteSetting("confirmClose", UISettings::values.confirm_before_closing, true); | ||
| 801 | WriteSetting("firstStart", UISettings::values.first_start, true); | ||
| 802 | WriteSetting("calloutFlags", UISettings::values.callout_flags, 0); | ||
| 803 | WriteSetting("showConsole", UISettings::values.show_console, false); | ||
| 804 | WriteSetting("profileIndex", UISettings::values.profile_index, 0); | ||
| 805 | qt_config->endGroup(); | 1045 | qt_config->endGroup(); |
| 806 | } | 1046 | } |
| 807 | 1047 | ||
| @@ -811,7 +1051,7 @@ QVariant Config::ReadSetting(const QString& name) const { | |||
| 811 | 1051 | ||
| 812 | QVariant Config::ReadSetting(const QString& name, const QVariant& default_value) const { | 1052 | QVariant Config::ReadSetting(const QString& name, const QVariant& default_value) const { |
| 813 | QVariant result; | 1053 | QVariant result; |
| 814 | if (qt_config->value(name + "/default", false).toBool()) { | 1054 | if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) { |
| 815 | result = default_value; | 1055 | result = default_value; |
| 816 | } else { | 1056 | } else { |
| 817 | result = qt_config->value(name, default_value); | 1057 | result = qt_config->value(name, default_value); |
| @@ -825,7 +1065,7 @@ void Config::WriteSetting(const QString& name, const QVariant& value) { | |||
| 825 | 1065 | ||
| 826 | void Config::WriteSetting(const QString& name, const QVariant& value, | 1066 | void Config::WriteSetting(const QString& name, const QVariant& value, |
| 827 | const QVariant& default_value) { | 1067 | const QVariant& default_value) { |
| 828 | qt_config->setValue(name + "/default", value == default_value); | 1068 | qt_config->setValue(name + QStringLiteral("/default"), value == default_value); |
| 829 | qt_config->setValue(name, value); | 1069 | qt_config->setValue(name, value); |
| 830 | } | 1070 | } |
| 831 | 1071 | ||
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 @@ | |||
| 9 | #include <string> | 9 | #include <string> |
| 10 | #include <QVariant> | 10 | #include <QVariant> |
| 11 | #include "core/settings.h" | 11 | #include "core/settings.h" |
| 12 | #include "yuzu/ui_settings.h" | ||
| 13 | 12 | ||
| 14 | class QSettings; | 13 | class QSettings; |
| 15 | 14 | ||
| @@ -37,19 +36,51 @@ private: | |||
| 37 | void ReadTouchscreenValues(); | 36 | void ReadTouchscreenValues(); |
| 38 | void ApplyDefaultProfileIfInputInvalid(); | 37 | void ApplyDefaultProfileIfInputInvalid(); |
| 39 | 38 | ||
| 39 | // Read functions bases off the respective config section names. | ||
| 40 | void ReadAudioValues(); | ||
| 41 | void ReadControlValues(); | ||
| 42 | void ReadCoreValues(); | ||
| 43 | void ReadDataStorageValues(); | ||
| 44 | void ReadDebuggingValues(); | ||
| 45 | void ReadDisabledAddOnValues(); | ||
| 46 | void ReadMiscellaneousValues(); | ||
| 47 | void ReadPathValues(); | ||
| 48 | void ReadRendererValues(); | ||
| 49 | void ReadShortcutValues(); | ||
| 50 | void ReadSystemValues(); | ||
| 51 | void ReadUIValues(); | ||
| 52 | void ReadUIGamelistValues(); | ||
| 53 | void ReadUILayoutValues(); | ||
| 54 | void ReadWebServiceValues(); | ||
| 55 | |||
| 40 | void SaveValues(); | 56 | void SaveValues(); |
| 41 | void SavePlayerValues(); | 57 | void SavePlayerValues(); |
| 42 | void SaveDebugValues(); | 58 | void SaveDebugValues(); |
| 43 | void SaveMouseValues(); | 59 | void SaveMouseValues(); |
| 44 | void SaveTouchscreenValues(); | 60 | void SaveTouchscreenValues(); |
| 45 | 61 | ||
| 62 | // Save functions based off the respective config section names. | ||
| 63 | void SaveAudioValues(); | ||
| 64 | void SaveControlValues(); | ||
| 65 | void SaveCoreValues(); | ||
| 66 | void SaveDataStorageValues(); | ||
| 67 | void SaveDebuggingValues(); | ||
| 68 | void SaveDisabledAddOnValues(); | ||
| 69 | void SaveMiscellaneousValues(); | ||
| 70 | void SavePathValues(); | ||
| 71 | void SaveRendererValues(); | ||
| 72 | void SaveShortcutValues(); | ||
| 73 | void SaveSystemValues(); | ||
| 74 | void SaveUIValues(); | ||
| 75 | void SaveUIGamelistValues(); | ||
| 76 | void SaveUILayoutValues(); | ||
| 77 | void SaveWebServiceValues(); | ||
| 78 | |||
| 46 | QVariant ReadSetting(const QString& name) const; | 79 | QVariant ReadSetting(const QString& name) const; |
| 47 | QVariant ReadSetting(const QString& name, const QVariant& default_value) const; | 80 | QVariant ReadSetting(const QString& name, const QVariant& default_value) const; |
| 48 | void WriteSetting(const QString& name, const QVariant& value); | 81 | void WriteSetting(const QString& name, const QVariant& value); |
| 49 | void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value); | 82 | void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value); |
| 50 | 83 | ||
| 51 | static const std::array<UISettings::Shortcut, 15> default_hotkeys; | ||
| 52 | |||
| 53 | std::unique_ptr<QSettings> qt_config; | 84 | std::unique_ptr<QSettings> qt_config; |
| 54 | std::string qt_config_loc; | 85 | std::string qt_config_loc; |
| 55 | }; | 86 | }; |
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp index 5d9ccc6e8..b0f9b814d 100644 --- a/src/yuzu/configuration/configure_audio.cpp +++ b/src/yuzu/configuration/configure_audio.cpp | |||
| @@ -16,21 +16,21 @@ ConfigureAudio::ConfigureAudio(QWidget* parent) | |||
| 16 | ui->setupUi(this); | 16 | ui->setupUi(this); |
| 17 | 17 | ||
| 18 | ui->output_sink_combo_box->clear(); | 18 | ui->output_sink_combo_box->clear(); |
| 19 | ui->output_sink_combo_box->addItem("auto"); | 19 | ui->output_sink_combo_box->addItem(QString::fromUtf8(AudioCore::auto_device_name)); |
| 20 | for (const char* id : AudioCore::GetSinkIDs()) { | 20 | for (const char* id : AudioCore::GetSinkIDs()) { |
| 21 | ui->output_sink_combo_box->addItem(id); | 21 | ui->output_sink_combo_box->addItem(QString::fromUtf8(id)); |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | connect(ui->volume_slider, &QSlider::valueChanged, this, | 24 | connect(ui->volume_slider, &QSlider::valueChanged, this, |
| 25 | &ConfigureAudio::setVolumeIndicatorText); | 25 | &ConfigureAudio::setVolumeIndicatorText); |
| 26 | 26 | ||
| 27 | this->setConfiguration(); | 27 | this->setConfiguration(); |
| 28 | connect(ui->output_sink_combo_box, | 28 | connect(ui->output_sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this, |
| 29 | static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, | ||
| 30 | &ConfigureAudio::updateAudioDevices); | 29 | &ConfigureAudio::updateAudioDevices); |
| 31 | 30 | ||
| 32 | ui->output_sink_combo_box->setEnabled(!Core::System::GetInstance().IsPoweredOn()); | 31 | const bool is_powered_on = Core::System::GetInstance().IsPoweredOn(); |
| 33 | ui->audio_device_combo_box->setEnabled(!Core::System::GetInstance().IsPoweredOn()); | 32 | ui->output_sink_combo_box->setEnabled(!is_powered_on); |
| 33 | ui->audio_device_combo_box->setEnabled(!is_powered_on); | ||
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | ConfigureAudio::~ConfigureAudio() = default; | 36 | ConfigureAudio::~ConfigureAudio() = default; |
| @@ -94,7 +94,7 @@ void ConfigureAudio::applyConfiguration() { | |||
| 94 | 94 | ||
| 95 | void ConfigureAudio::updateAudioDevices(int sink_index) { | 95 | void ConfigureAudio::updateAudioDevices(int sink_index) { |
| 96 | ui->audio_device_combo_box->clear(); | 96 | ui->audio_device_combo_box->clear(); |
| 97 | ui->audio_device_combo_box->addItem(AudioCore::auto_device_name); | 97 | ui->audio_device_combo_box->addItem(QString::fromUtf8(AudioCore::auto_device_name)); |
| 98 | 98 | ||
| 99 | const std::string sink_id = ui->output_sink_combo_box->itemText(sink_index).toStdString(); | 99 | const std::string sink_id = ui->output_sink_combo_box->itemText(sink_index).toStdString(); |
| 100 | for (const auto& device : AudioCore::GetDeviceListForSink(sink_id)) { | 100 | for (const auto& device : AudioCore::GetDeviceListForSink(sink_id)) { |
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index a5218b051..8086f9d6b 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp | |||
| @@ -17,13 +17,14 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry) | |||
| 17 | ui->hotkeysTab->Populate(registry); | 17 | ui->hotkeysTab->Populate(registry); |
| 18 | this->setConfiguration(); | 18 | this->setConfiguration(); |
| 19 | this->PopulateSelectionList(); | 19 | this->PopulateSelectionList(); |
| 20 | |||
| 21 | setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); | ||
| 22 | |||
| 20 | connect(ui->selectorList, &QListWidget::itemSelectionChanged, this, | 23 | connect(ui->selectorList, &QListWidget::itemSelectionChanged, this, |
| 21 | &ConfigureDialog::UpdateVisibleTabs); | 24 | &ConfigureDialog::UpdateVisibleTabs); |
| 25 | |||
| 22 | adjustSize(); | 26 | adjustSize(); |
| 23 | ui->selectorList->setCurrentRow(0); | 27 | ui->selectorList->setCurrentRow(0); |
| 24 | |||
| 25 | // Synchronise lists upon initialisation | ||
| 26 | ui->hotkeysTab->EmitHotkeysChanged(); | ||
| 27 | } | 28 | } |
| 28 | 29 | ||
| 29 | ConfigureDialog::~ConfigureDialog() = default; | 30 | ConfigureDialog::~ConfigureDialog() = default; |
diff --git a/src/yuzu/configuration/configure_gamelist.cpp b/src/yuzu/configuration/configure_gamelist.cpp index ae8cac243..6f0d75605 100644 --- a/src/yuzu/configuration/configure_gamelist.cpp +++ b/src/yuzu/configuration/configure_gamelist.cpp | |||
| @@ -100,13 +100,15 @@ void ConfigureGameList::RetranslateUI() { | |||
| 100 | 100 | ||
| 101 | void ConfigureGameList::InitializeIconSizeComboBox() { | 101 | void ConfigureGameList::InitializeIconSizeComboBox() { |
| 102 | for (const auto& size : default_icon_sizes) { | 102 | for (const auto& size : default_icon_sizes) { |
| 103 | ui->icon_size_combobox->addItem(size.second, size.first); | 103 | ui->icon_size_combobox->addItem(QString::fromUtf8(size.second), size.first); |
| 104 | } | 104 | } |
| 105 | } | 105 | } |
| 106 | 106 | ||
| 107 | void ConfigureGameList::InitializeRowComboBoxes() { | 107 | void ConfigureGameList::InitializeRowComboBoxes() { |
| 108 | for (std::size_t i = 0; i < row_text_names.size(); ++i) { | 108 | for (std::size_t i = 0; i < row_text_names.size(); ++i) { |
| 109 | ui->row_1_text_combobox->addItem(row_text_names[i], QVariant::fromValue(i)); | 109 | const QString row_text_name = QString::fromUtf8(row_text_names[i]); |
| 110 | ui->row_2_text_combobox->addItem(row_text_names[i], QVariant::fromValue(i)); | 110 | |
| 111 | ui->row_1_text_combobox->addItem(row_text_name, QVariant::fromValue(i)); | ||
| 112 | ui->row_2_text_combobox->addItem(row_text_name, QVariant::fromValue(i)); | ||
| 111 | } | 113 | } |
| 112 | } | 114 | } |
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index e48f4f5a3..dd25dc6e1 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp | |||
| @@ -14,7 +14,8 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) | |||
| 14 | ui->setupUi(this); | 14 | ui->setupUi(this); |
| 15 | 15 | ||
| 16 | for (const auto& theme : UISettings::themes) { | 16 | for (const auto& theme : UISettings::themes) { |
| 17 | ui->theme_combobox->addItem(theme.first, theme.second); | 17 | ui->theme_combobox->addItem(QString::fromUtf8(theme.first), |
| 18 | QString::fromUtf8(theme.second)); | ||
| 18 | } | 19 | } |
| 19 | 20 | ||
| 20 | this->setConfiguration(); | 21 | this->setConfiguration(); |
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index c299c0b5b..08ea41b0f 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp | |||
| @@ -69,16 +69,20 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent) | |||
| 69 | ConfigureGraphics::~ConfigureGraphics() = default; | 69 | ConfigureGraphics::~ConfigureGraphics() = default; |
| 70 | 70 | ||
| 71 | void ConfigureGraphics::setConfiguration() { | 71 | void ConfigureGraphics::setConfiguration() { |
| 72 | const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); | ||
| 73 | |||
| 72 | ui->resolution_factor_combobox->setCurrentIndex( | 74 | ui->resolution_factor_combobox->setCurrentIndex( |
| 73 | static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor))); | 75 | static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor))); |
| 74 | ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit); | 76 | ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit); |
| 75 | ui->frame_limit->setValue(Settings::values.frame_limit); | 77 | ui->frame_limit->setValue(Settings::values.frame_limit); |
| 78 | ui->use_compatibility_profile->setEnabled(runtime_lock); | ||
| 76 | ui->use_compatibility_profile->setChecked(Settings::values.use_compatibility_profile); | 79 | ui->use_compatibility_profile->setChecked(Settings::values.use_compatibility_profile); |
| 80 | ui->use_disk_shader_cache->setEnabled(runtime_lock); | ||
| 77 | ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache); | 81 | ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache); |
| 78 | ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation); | 82 | ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation); |
| 79 | ui->use_asynchronous_gpu_emulation->setEnabled(!Core::System::GetInstance().IsPoweredOn()); | 83 | ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock); |
| 80 | ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation); | 84 | ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation); |
| 81 | ui->force_30fps_mode->setEnabled(!Core::System::GetInstance().IsPoweredOn()); | 85 | ui->force_30fps_mode->setEnabled(runtime_lock); |
| 82 | ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode); | 86 | ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode); |
| 83 | UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, | 87 | UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, |
| 84 | Settings::values.bg_blue)); | 88 | Settings::values.bg_blue)); |
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) | |||
| 31 | 31 | ||
| 32 | ConfigureHotkeys::~ConfigureHotkeys() = default; | 32 | ConfigureHotkeys::~ConfigureHotkeys() = default; |
| 33 | 33 | ||
| 34 | void ConfigureHotkeys::EmitHotkeysChanged() { | ||
| 35 | emit HotkeysChanged(GetUsedKeyList()); | ||
| 36 | } | ||
| 37 | |||
| 38 | QList<QKeySequence> ConfigureHotkeys::GetUsedKeyList() const { | ||
| 39 | QList<QKeySequence> list; | ||
| 40 | for (int r = 0; r < model->rowCount(); r++) { | ||
| 41 | const QStandardItem* parent = model->item(r, 0); | ||
| 42 | for (int r2 = 0; r2 < parent->rowCount(); r2++) { | ||
| 43 | const QStandardItem* keyseq = parent->child(r2, 1); | ||
| 44 | list << QKeySequence::fromString(keyseq->text(), QKeySequence::NativeText); | ||
| 45 | } | ||
| 46 | } | ||
| 47 | return list; | ||
| 48 | } | ||
| 49 | |||
| 50 | void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) { | 34 | void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) { |
| 51 | for (const auto& group : registry.hotkey_groups) { | 35 | for (const auto& group : registry.hotkey_groups) { |
| 52 | auto* parent_item = new QStandardItem(group.first); | 36 | auto* parent_item = new QStandardItem(group.first); |
| @@ -83,16 +67,29 @@ void ConfigureHotkeys::Configure(QModelIndex index) { | |||
| 83 | } | 67 | } |
| 84 | 68 | ||
| 85 | if (IsUsedKey(key_sequence) && key_sequence != QKeySequence(previous_key.toString())) { | 69 | if (IsUsedKey(key_sequence) && key_sequence != QKeySequence(previous_key.toString())) { |
| 86 | QMessageBox::critical(this, tr("Error in inputted key"), | 70 | QMessageBox::warning(this, tr("Conflicting Key Sequence"), |
| 87 | tr("You're using a key that's already bound.")); | 71 | tr("The entered key sequence is already assigned to another hotkey.")); |
| 88 | } else { | 72 | } else { |
| 89 | model->setData(index, key_sequence.toString(QKeySequence::NativeText)); | 73 | model->setData(index, key_sequence.toString(QKeySequence::NativeText)); |
| 90 | EmitHotkeysChanged(); | ||
| 91 | } | 74 | } |
| 92 | } | 75 | } |
| 93 | 76 | ||
| 94 | bool ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence) const { | 77 | bool ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence) const { |
| 95 | return GetUsedKeyList().contains(key_sequence); | 78 | for (int r = 0; r < model->rowCount(); r++) { |
| 79 | const QStandardItem* const parent = model->item(r, 0); | ||
| 80 | |||
| 81 | for (int r2 = 0; r2 < parent->rowCount(); r2++) { | ||
| 82 | const QStandardItem* const key_seq_item = parent->child(r2, 1); | ||
| 83 | const auto key_seq_str = key_seq_item->text(); | ||
| 84 | const auto key_seq = QKeySequence::fromString(key_seq_str, QKeySequence::NativeText); | ||
| 85 | |||
| 86 | if (key_sequence == key_seq) { | ||
| 87 | return true; | ||
| 88 | } | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | return false; | ||
| 96 | } | 93 | } |
| 97 | 94 | ||
| 98 | void ConfigureHotkeys::applyConfiguration(HotkeyRegistry& registry) { | 95 | void ConfigureHotkeys::applyConfiguration(HotkeyRegistry& registry) { |
| @@ -114,7 +111,6 @@ void ConfigureHotkeys::applyConfiguration(HotkeyRegistry& registry) { | |||
| 114 | } | 111 | } |
| 115 | 112 | ||
| 116 | registry.SaveHotkeys(); | 113 | registry.SaveHotkeys(); |
| 117 | Settings::Apply(); | ||
| 118 | } | 114 | } |
| 119 | 115 | ||
| 120 | void ConfigureHotkeys::retranslateUi() { | 116 | 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: | |||
| 24 | void applyConfiguration(HotkeyRegistry& registry); | 24 | void applyConfiguration(HotkeyRegistry& registry); |
| 25 | void retranslateUi(); | 25 | void retranslateUi(); |
| 26 | 26 | ||
| 27 | void EmitHotkeysChanged(); | ||
| 28 | |||
| 29 | /** | 27 | /** |
| 30 | * Populates the hotkey list widget using data from the provided registry. | 28 | * Populates the hotkey list widget using data from the provided registry. |
| 31 | * Called everytime the Configure dialog is opened. | 29 | * Called everytime the Configure dialog is opened. |
| @@ -33,13 +31,9 @@ public: | |||
| 33 | */ | 31 | */ |
| 34 | void Populate(const HotkeyRegistry& registry); | 32 | void Populate(const HotkeyRegistry& registry); |
| 35 | 33 | ||
| 36 | signals: | ||
| 37 | void HotkeysChanged(QList<QKeySequence> new_key_list); | ||
| 38 | |||
| 39 | private: | 34 | private: |
| 40 | void Configure(QModelIndex index); | 35 | void Configure(QModelIndex index); |
| 41 | bool IsUsedKey(QKeySequence key_sequence) const; | 36 | bool IsUsedKey(QKeySequence key_sequence) const; |
| 42 | QList<QKeySequence> GetUsedKeyList() const; | ||
| 43 | 37 | ||
| 44 | std::unique_ptr<Ui::ConfigureHotkeys> ui; | 38 | std::unique_ptr<Ui::ConfigureHotkeys> ui; |
| 45 | 39 | ||
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index f39d57998..87e459714 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp | |||
| @@ -75,8 +75,8 @@ ConfigureInput::ConfigureInput(QWidget* parent) | |||
| 75 | }; | 75 | }; |
| 76 | 76 | ||
| 77 | for (auto* controller_box : players_controller) { | 77 | for (auto* controller_box : players_controller) { |
| 78 | controller_box->addItems({"None", "Pro Controller", "Dual Joycons", "Single Right Joycon", | 78 | controller_box->addItems({tr("None"), tr("Pro Controller"), tr("Dual Joycons"), |
| 79 | "Single Left Joycon"}); | 79 | tr("Single Right Joycon"), tr("Single Left Joycon")}); |
| 80 | } | 80 | } |
| 81 | 81 | ||
| 82 | this->loadConfiguration(); | 82 | this->loadConfiguration(); |
| @@ -85,9 +85,10 @@ ConfigureInput::ConfigureInput(QWidget* parent) | |||
| 85 | connect(ui->restore_defaults_button, &QPushButton::pressed, this, | 85 | connect(ui->restore_defaults_button, &QPushButton::pressed, this, |
| 86 | &ConfigureInput::restoreDefaults); | 86 | &ConfigureInput::restoreDefaults); |
| 87 | 87 | ||
| 88 | for (auto* enabled : players_controller) | 88 | for (auto* enabled : players_controller) { |
| 89 | connect(enabled, QOverload<int>::of(&QComboBox::currentIndexChanged), this, | 89 | connect(enabled, QOverload<int>::of(&QComboBox::currentIndexChanged), this, |
| 90 | &ConfigureInput::updateUIEnabled); | 90 | &ConfigureInput::updateUIEnabled); |
| 91 | } | ||
| 91 | connect(ui->use_docked_mode, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled); | 92 | connect(ui->use_docked_mode, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled); |
| 92 | connect(ui->handheld_connected, &QCheckBox::stateChanged, this, | 93 | connect(ui->handheld_connected, &QCheckBox::stateChanged, this, |
| 93 | &ConfigureInput::updateUIEnabled); | 94 | &ConfigureInput::updateUIEnabled); |
| @@ -147,10 +148,12 @@ void ConfigureInput::updateUIEnabled() { | |||
| 147 | bool hit_disabled = false; | 148 | bool hit_disabled = false; |
| 148 | for (auto* player : players_controller) { | 149 | for (auto* player : players_controller) { |
| 149 | player->setDisabled(hit_disabled); | 150 | player->setDisabled(hit_disabled); |
| 150 | if (hit_disabled) | 151 | if (hit_disabled) { |
| 151 | player->setCurrentIndex(0); | 152 | player->setCurrentIndex(0); |
| 152 | if (!hit_disabled && player->currentIndex() == 0) | 153 | } |
| 154 | if (!hit_disabled && player->currentIndex() == 0) { | ||
| 153 | hit_disabled = true; | 155 | hit_disabled = true; |
| 156 | } | ||
| 154 | } | 157 | } |
| 155 | 158 | ||
| 156 | for (std::size_t i = 0; i < players_controller.size(); ++i) { | 159 | for (std::size_t i = 0; i < players_controller.size(); ++i) { |
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index c5a245ebe..95b0a656a 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp | |||
| @@ -45,7 +45,7 @@ static QString GetKeyName(int key_code) { | |||
| 45 | case Qt::Key_Alt: | 45 | case Qt::Key_Alt: |
| 46 | return QObject::tr("Alt"); | 46 | return QObject::tr("Alt"); |
| 47 | case Qt::Key_Meta: | 47 | case Qt::Key_Meta: |
| 48 | return ""; | 48 | return {}; |
| 49 | default: | 49 | default: |
| 50 | return QKeySequence(key_code).toString(); | 50 | return QKeySequence(key_code).toString(); |
| 51 | } | 51 | } |
| @@ -65,46 +65,70 @@ static void SetAnalogButton(const Common::ParamPackage& input_param, | |||
| 65 | static QString ButtonToText(const Common::ParamPackage& param) { | 65 | static QString ButtonToText(const Common::ParamPackage& param) { |
| 66 | if (!param.Has("engine")) { | 66 | if (!param.Has("engine")) { |
| 67 | return QObject::tr("[not set]"); | 67 | return QObject::tr("[not set]"); |
| 68 | } else if (param.Get("engine", "") == "keyboard") { | 68 | } |
| 69 | |||
| 70 | if (param.Get("engine", "") == "keyboard") { | ||
| 69 | return GetKeyName(param.Get("code", 0)); | 71 | return GetKeyName(param.Get("code", 0)); |
| 70 | } else if (param.Get("engine", "") == "sdl") { | 72 | } |
| 73 | |||
| 74 | if (param.Get("engine", "") == "sdl") { | ||
| 71 | if (param.Has("hat")) { | 75 | if (param.Has("hat")) { |
| 72 | return QString(QObject::tr("Hat %1 %2")) | 76 | const QString hat_str = QString::fromStdString(param.Get("hat", "")); |
| 73 | .arg(param.Get("hat", "").c_str(), param.Get("direction", "").c_str()); | 77 | const QString direction_str = QString::fromStdString(param.Get("direction", "")); |
| 78 | |||
| 79 | return QObject::tr("Hat %1 %2").arg(hat_str, direction_str); | ||
| 74 | } | 80 | } |
| 81 | |||
| 75 | if (param.Has("axis")) { | 82 | if (param.Has("axis")) { |
| 76 | return QString(QObject::tr("Axis %1%2")) | 83 | const QString axis_str = QString::fromStdString(param.Get("axis", "")); |
| 77 | .arg(param.Get("axis", "").c_str(), param.Get("direction", "").c_str()); | 84 | const QString direction_str = QString::fromStdString(param.Get("direction", "")); |
| 85 | |||
| 86 | return QObject::tr("Axis %1%2").arg(axis_str, direction_str); | ||
| 78 | } | 87 | } |
| 88 | |||
| 79 | if (param.Has("button")) { | 89 | if (param.Has("button")) { |
| 80 | return QString(QObject::tr("Button %1")).arg(param.Get("button", "").c_str()); | 90 | const QString button_str = QString::fromStdString(param.Get("button", "")); |
| 91 | |||
| 92 | return QObject::tr("Button %1").arg(button_str); | ||
| 81 | } | 93 | } |
| 82 | return QString(); | 94 | |
| 83 | } else { | 95 | return {}; |
| 84 | return QObject::tr("[unknown]"); | ||
| 85 | } | 96 | } |
| 86 | }; | 97 | |
| 98 | return QObject::tr("[unknown]"); | ||
| 99 | } | ||
| 87 | 100 | ||
| 88 | static QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) { | 101 | static QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) { |
| 89 | if (!param.Has("engine")) { | 102 | if (!param.Has("engine")) { |
| 90 | return QObject::tr("[not set]"); | 103 | return QObject::tr("[not set]"); |
| 91 | } else if (param.Get("engine", "") == "analog_from_button") { | 104 | } |
| 105 | |||
| 106 | if (param.Get("engine", "") == "analog_from_button") { | ||
| 92 | return ButtonToText(Common::ParamPackage{param.Get(dir, "")}); | 107 | return ButtonToText(Common::ParamPackage{param.Get(dir, "")}); |
| 93 | } else if (param.Get("engine", "") == "sdl") { | 108 | } |
| 109 | |||
| 110 | if (param.Get("engine", "") == "sdl") { | ||
| 94 | if (dir == "modifier") { | 111 | if (dir == "modifier") { |
| 95 | return QString(QObject::tr("[unused]")); | 112 | return QObject::tr("[unused]"); |
| 96 | } | 113 | } |
| 97 | 114 | ||
| 98 | if (dir == "left" || dir == "right") { | 115 | if (dir == "left" || dir == "right") { |
| 99 | return QString(QObject::tr("Axis %1")).arg(param.Get("axis_x", "").c_str()); | 116 | const QString axis_x_str = QString::fromStdString(param.Get("axis_x", "")); |
| 100 | } else if (dir == "up" || dir == "down") { | 117 | |
| 101 | return QString(QObject::tr("Axis %1")).arg(param.Get("axis_y", "").c_str()); | 118 | return QObject::tr("Axis %1").arg(axis_x_str); |
| 102 | } | 119 | } |
| 103 | return QString(); | 120 | |
| 104 | } else { | 121 | if (dir == "up" || dir == "down") { |
| 105 | return QObject::tr("[unknown]"); | 122 | const QString axis_y_str = QString::fromStdString(param.Get("axis_y", "")); |
| 123 | |||
| 124 | return QObject::tr("Axis %1").arg(axis_y_str); | ||
| 125 | } | ||
| 126 | |||
| 127 | return {}; | ||
| 106 | } | 128 | } |
| 107 | }; | 129 | |
| 130 | return QObject::tr("[unknown]"); | ||
| 131 | } | ||
| 108 | 132 | ||
| 109 | ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index, bool debug) | 133 | ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index, bool debug) |
| 110 | : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index), | 134 | : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index), |
| @@ -214,38 +238,42 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 214 | analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog}; | 238 | analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog}; |
| 215 | 239 | ||
| 216 | for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { | 240 | for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { |
| 217 | if (!button_map[button_id]) | 241 | auto* const button = button_map[button_id]; |
| 242 | if (button == nullptr) { | ||
| 218 | continue; | 243 | continue; |
| 219 | button_map[button_id]->setContextMenuPolicy(Qt::CustomContextMenu); | 244 | } |
| 220 | connect(button_map[button_id], &QPushButton::released, [=]() { | 245 | |
| 246 | button->setContextMenuPolicy(Qt::CustomContextMenu); | ||
| 247 | connect(button, &QPushButton::released, [=] { | ||
| 221 | handleClick( | 248 | handleClick( |
| 222 | button_map[button_id], | 249 | button_map[button_id], |
| 223 | [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; }, | 250 | [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; }, |
| 224 | InputCommon::Polling::DeviceType::Button); | 251 | InputCommon::Polling::DeviceType::Button); |
| 225 | }); | 252 | }); |
| 226 | connect(button_map[button_id], &QPushButton::customContextMenuRequested, | 253 | connect(button, &QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) { |
| 227 | [=](const QPoint& menu_location) { | 254 | QMenu context_menu; |
| 228 | QMenu context_menu; | 255 | context_menu.addAction(tr("Clear"), [&] { |
| 229 | context_menu.addAction(tr("Clear"), [&] { | 256 | buttons_param[button_id].Clear(); |
| 230 | buttons_param[button_id].Clear(); | 257 | button_map[button_id]->setText(tr("[not set]")); |
| 231 | button_map[button_id]->setText(tr("[not set]")); | 258 | }); |
| 232 | }); | 259 | context_menu.addAction(tr("Restore Default"), [&] { |
| 233 | context_menu.addAction(tr("Restore Default"), [&] { | 260 | buttons_param[button_id] = Common::ParamPackage{ |
| 234 | buttons_param[button_id] = Common::ParamPackage{ | 261 | InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])}; |
| 235 | InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])}; | 262 | button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); |
| 236 | button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); | 263 | }); |
| 237 | }); | 264 | context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); |
| 238 | context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); | 265 | }); |
| 239 | }); | ||
| 240 | } | 266 | } |
| 241 | 267 | ||
| 242 | for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { | 268 | for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { |
| 243 | for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { | 269 | for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { |
| 244 | if (!analog_map_buttons[analog_id][sub_button_id]) | 270 | auto* const analog_button = analog_map_buttons[analog_id][sub_button_id]; |
| 271 | if (analog_button == nullptr) { | ||
| 245 | continue; | 272 | continue; |
| 246 | analog_map_buttons[analog_id][sub_button_id]->setContextMenuPolicy( | 273 | } |
| 247 | Qt::CustomContextMenu); | 274 | |
| 248 | connect(analog_map_buttons[analog_id][sub_button_id], &QPushButton::released, [=]() { | 275 | analog_button->setContextMenuPolicy(Qt::CustomContextMenu); |
| 276 | connect(analog_button, &QPushButton::released, [=]() { | ||
| 249 | handleClick(analog_map_buttons[analog_id][sub_button_id], | 277 | handleClick(analog_map_buttons[analog_id][sub_button_id], |
| 250 | [=](const Common::ParamPackage& params) { | 278 | [=](const Common::ParamPackage& params) { |
| 251 | SetAnalogButton(params, analogs_param[analog_id], | 279 | SetAnalogButton(params, analogs_param[analog_id], |
| @@ -253,8 +281,8 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 253 | }, | 281 | }, |
| 254 | InputCommon::Polling::DeviceType::Button); | 282 | InputCommon::Polling::DeviceType::Button); |
| 255 | }); | 283 | }); |
| 256 | connect(analog_map_buttons[analog_id][sub_button_id], | 284 | connect(analog_button, &QPushButton::customContextMenuRequested, |
| 257 | &QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) { | 285 | [=](const QPoint& menu_location) { |
| 258 | QMenu context_menu; | 286 | QMenu context_menu; |
| 259 | context_menu.addAction(tr("Clear"), [&] { | 287 | context_menu.addAction(tr("Clear"), [&] { |
| 260 | analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]); | 288 | analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]); |
| @@ -272,7 +300,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 272 | menu_location)); | 300 | menu_location)); |
| 273 | }); | 301 | }); |
| 274 | } | 302 | } |
| 275 | connect(analog_map_stick[analog_id], &QPushButton::released, [=]() { | 303 | connect(analog_map_stick[analog_id], &QPushButton::released, [=] { |
| 276 | QMessageBox::information(this, tr("Information"), | 304 | QMessageBox::information(this, tr("Information"), |
| 277 | tr("After pressing OK, first move your joystick horizontally, " | 305 | tr("After pressing OK, first move your joystick horizontally, " |
| 278 | "and then vertically.")); | 306 | "and then vertically.")); |
| @@ -351,7 +379,7 @@ void ConfigureInputPlayer::OnControllerButtonClick(int i) { | |||
| 351 | return; | 379 | return; |
| 352 | controller_colors[i] = new_bg_color; | 380 | controller_colors[i] = new_bg_color; |
| 353 | controller_color_buttons[i]->setStyleSheet( | 381 | controller_color_buttons[i]->setStyleSheet( |
| 354 | QString("QPushButton { background-color: %1 }").arg(controller_colors[i].name())); | 382 | QStringLiteral("QPushButton { background-color: %1 }").arg(controller_colors[i].name())); |
| 355 | } | 383 | } |
| 356 | 384 | ||
| 357 | void ConfigureInputPlayer::loadConfiguration() { | 385 | void ConfigureInputPlayer::loadConfiguration() { |
| @@ -388,7 +416,8 @@ void ConfigureInputPlayer::loadConfiguration() { | |||
| 388 | 416 | ||
| 389 | for (std::size_t i = 0; i < colors.size(); ++i) { | 417 | for (std::size_t i = 0; i < colors.size(); ++i) { |
| 390 | controller_color_buttons[i]->setStyleSheet( | 418 | controller_color_buttons[i]->setStyleSheet( |
| 391 | QString("QPushButton { background-color: %1 }").arg(controller_colors[i].name())); | 419 | QStringLiteral("QPushButton { background-color: %1 }") |
| 420 | .arg(controller_colors[i].name())); | ||
| 392 | } | 421 | } |
| 393 | } | 422 | } |
| 394 | 423 | ||
| @@ -410,14 +439,22 @@ void ConfigureInputPlayer::restoreDefaults() { | |||
| 410 | 439 | ||
| 411 | void ConfigureInputPlayer::ClearAll() { | 440 | void ConfigureInputPlayer::ClearAll() { |
| 412 | for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { | 441 | for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { |
| 413 | if (button_map[button_id] && button_map[button_id]->isEnabled()) | 442 | const auto* const button = button_map[button_id]; |
| 414 | buttons_param[button_id].Clear(); | 443 | if (button == nullptr || !button->isEnabled()) { |
| 444 | continue; | ||
| 445 | } | ||
| 446 | |||
| 447 | buttons_param[button_id].Clear(); | ||
| 415 | } | 448 | } |
| 449 | |||
| 416 | for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { | 450 | for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { |
| 417 | for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { | 451 | for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { |
| 418 | if (analog_map_buttons[analog_id][sub_button_id] && | 452 | const auto* const analog_button = analog_map_buttons[analog_id][sub_button_id]; |
| 419 | analog_map_buttons[analog_id][sub_button_id]->isEnabled()) | 453 | if (analog_button == nullptr || !analog_button->isEnabled()) { |
| 420 | analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]); | 454 | continue; |
| 455 | } | ||
| 456 | |||
| 457 | analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]); | ||
| 421 | } | 458 | } |
| 422 | } | 459 | } |
| 423 | 460 | ||
| @@ -431,10 +468,14 @@ void ConfigureInputPlayer::updateButtonLabels() { | |||
| 431 | 468 | ||
| 432 | for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { | 469 | for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { |
| 433 | for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { | 470 | for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { |
| 434 | if (analog_map_buttons[analog_id][sub_button_id]) { | 471 | auto* const analog_button = analog_map_buttons[analog_id][sub_button_id]; |
| 435 | analog_map_buttons[analog_id][sub_button_id]->setText( | 472 | |
| 436 | AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id])); | 473 | if (analog_button == nullptr) { |
| 474 | continue; | ||
| 437 | } | 475 | } |
| 476 | |||
| 477 | analog_button->setText( | ||
| 478 | AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id])); | ||
| 438 | } | 479 | } |
| 439 | analog_map_stick[analog_id]->setText(tr("Set Analog Stick")); | 480 | analog_map_stick[analog_id]->setText(tr("Set Analog Stick")); |
| 440 | } | 481 | } |
diff --git a/src/yuzu/configuration/configure_mouse_advanced.cpp b/src/yuzu/configuration/configure_mouse_advanced.cpp index ef857035e..a14bb1475 100644 --- a/src/yuzu/configuration/configure_mouse_advanced.cpp +++ b/src/yuzu/configuration/configure_mouse_advanced.cpp | |||
| @@ -25,7 +25,7 @@ static QString GetKeyName(int key_code) { | |||
| 25 | case Qt::Key_Alt: | 25 | case Qt::Key_Alt: |
| 26 | return QObject::tr("Alt"); | 26 | return QObject::tr("Alt"); |
| 27 | case Qt::Key_Meta: | 27 | case Qt::Key_Meta: |
| 28 | return ""; | 28 | return {}; |
| 29 | default: | 29 | default: |
| 30 | return QKeySequence(key_code).toString(); | 30 | return QKeySequence(key_code).toString(); |
| 31 | } | 31 | } |
| @@ -34,24 +34,36 @@ static QString GetKeyName(int key_code) { | |||
| 34 | static QString ButtonToText(const Common::ParamPackage& param) { | 34 | static QString ButtonToText(const Common::ParamPackage& param) { |
| 35 | if (!param.Has("engine")) { | 35 | if (!param.Has("engine")) { |
| 36 | return QObject::tr("[not set]"); | 36 | return QObject::tr("[not set]"); |
| 37 | } else if (param.Get("engine", "") == "keyboard") { | 37 | } |
| 38 | |||
| 39 | if (param.Get("engine", "") == "keyboard") { | ||
| 38 | return GetKeyName(param.Get("code", 0)); | 40 | return GetKeyName(param.Get("code", 0)); |
| 39 | } else if (param.Get("engine", "") == "sdl") { | 41 | } |
| 42 | |||
| 43 | if (param.Get("engine", "") == "sdl") { | ||
| 40 | if (param.Has("hat")) { | 44 | if (param.Has("hat")) { |
| 41 | return QString(QObject::tr("Hat %1 %2")) | 45 | const QString hat_str = QString::fromStdString(param.Get("hat", "")); |
| 42 | .arg(param.Get("hat", "").c_str(), param.Get("direction", "").c_str()); | 46 | const QString direction_str = QString::fromStdString(param.Get("direction", "")); |
| 47 | |||
| 48 | return QObject::tr("Hat %1 %2").arg(hat_str, direction_str); | ||
| 43 | } | 49 | } |
| 50 | |||
| 44 | if (param.Has("axis")) { | 51 | if (param.Has("axis")) { |
| 45 | return QString(QObject::tr("Axis %1%2")) | 52 | const QString axis_str = QString::fromStdString(param.Get("axis", "")); |
| 46 | .arg(param.Get("axis", "").c_str(), param.Get("direction", "").c_str()); | 53 | const QString direction_str = QString::fromStdString(param.Get("direction", "")); |
| 54 | |||
| 55 | return QObject::tr("Axis %1%2").arg(axis_str, direction_str); | ||
| 47 | } | 56 | } |
| 57 | |||
| 48 | if (param.Has("button")) { | 58 | if (param.Has("button")) { |
| 49 | return QString(QObject::tr("Button %1")).arg(param.Get("button", "").c_str()); | 59 | const QString button_str = QString::fromStdString(param.Get("button", "")); |
| 60 | |||
| 61 | return QObject::tr("Button %1").arg(button_str); | ||
| 50 | } | 62 | } |
| 51 | return QString(); | 63 | return {}; |
| 52 | } else { | ||
| 53 | return QObject::tr("[unknown]"); | ||
| 54 | } | 64 | } |
| 65 | |||
| 66 | return QObject::tr("[unknown]"); | ||
| 55 | } | 67 | } |
| 56 | 68 | ||
| 57 | ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent) | 69 | ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent) |
| @@ -65,30 +77,31 @@ ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent) | |||
| 65 | }; | 77 | }; |
| 66 | 78 | ||
| 67 | for (int button_id = 0; button_id < Settings::NativeMouseButton::NumMouseButtons; button_id++) { | 79 | for (int button_id = 0; button_id < Settings::NativeMouseButton::NumMouseButtons; button_id++) { |
| 68 | if (!button_map[button_id]) | 80 | auto* const button = button_map[button_id]; |
| 81 | if (button == nullptr) { | ||
| 69 | continue; | 82 | continue; |
| 70 | button_map[button_id]->setContextMenuPolicy(Qt::CustomContextMenu); | 83 | } |
| 71 | connect(button_map[button_id], &QPushButton::released, [=]() { | 84 | |
| 85 | button->setContextMenuPolicy(Qt::CustomContextMenu); | ||
| 86 | connect(button, &QPushButton::released, [=] { | ||
| 72 | handleClick( | 87 | handleClick( |
| 73 | button_map[button_id], | 88 | button_map[button_id], |
| 74 | [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; }, | 89 | [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; }, |
| 75 | InputCommon::Polling::DeviceType::Button); | 90 | InputCommon::Polling::DeviceType::Button); |
| 76 | }); | 91 | }); |
| 77 | connect(button_map[button_id], &QPushButton::customContextMenuRequested, | 92 | connect(button, &QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) { |
| 78 | [=](const QPoint& menu_location) { | 93 | QMenu context_menu; |
| 79 | QMenu context_menu; | 94 | context_menu.addAction(tr("Clear"), [&] { |
| 80 | context_menu.addAction(tr("Clear"), [&] { | 95 | buttons_param[button_id].Clear(); |
| 81 | buttons_param[button_id].Clear(); | 96 | button_map[button_id]->setText(tr("[not set]")); |
| 82 | button_map[button_id]->setText(tr("[not set]")); | 97 | }); |
| 83 | }); | 98 | context_menu.addAction(tr("Restore Default"), [&] { |
| 84 | context_menu.addAction(tr("Restore Default"), [&] { | 99 | buttons_param[button_id] = Common::ParamPackage{ |
| 85 | buttons_param[button_id] = | 100 | InputCommon::GenerateKeyboardParam(Config::default_mouse_buttons[button_id])}; |
| 86 | Common::ParamPackage{InputCommon::GenerateKeyboardParam( | 101 | button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); |
| 87 | Config::default_mouse_buttons[button_id])}; | 102 | }); |
| 88 | button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); | 103 | context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); |
| 89 | }); | 104 | }); |
| 90 | context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); | ||
| 91 | }); | ||
| 92 | } | 105 | } |
| 93 | 106 | ||
| 94 | connect(ui->buttonClearAll, &QPushButton::released, [this] { ClearAll(); }); | 107 | connect(ui->buttonClearAll, &QPushButton::released, [this] { ClearAll(); }); |
| @@ -138,8 +151,10 @@ void ConfigureMouseAdvanced::restoreDefaults() { | |||
| 138 | 151 | ||
| 139 | void ConfigureMouseAdvanced::ClearAll() { | 152 | void ConfigureMouseAdvanced::ClearAll() { |
| 140 | for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { | 153 | for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { |
| 141 | if (button_map[i] && button_map[i]->isEnabled()) | 154 | const auto* const button = button_map[i]; |
| 155 | if (button != nullptr && button->isEnabled()) { | ||
| 142 | buttons_param[i].Clear(); | 156 | buttons_param[i].Clear(); |
| 157 | } | ||
| 143 | } | 158 | } |
| 144 | 159 | ||
| 145 | updateButtonLabels(); | 160 | updateButtonLabels(); |
diff --git a/src/yuzu/configuration/configure_per_general.cpp b/src/yuzu/configuration/configure_per_general.cpp index 022b94609..c3e68fdf5 100644 --- a/src/yuzu/configuration/configure_per_general.cpp +++ b/src/yuzu/configuration/configure_per_general.cpp | |||
| @@ -13,6 +13,8 @@ | |||
| 13 | #include <QTimer> | 13 | #include <QTimer> |
| 14 | #include <QTreeView> | 14 | #include <QTreeView> |
| 15 | 15 | ||
| 16 | #include "common/common_paths.h" | ||
| 17 | #include "common/file_util.h" | ||
| 16 | #include "core/file_sys/control_metadata.h" | 18 | #include "core/file_sys/control_metadata.h" |
| 17 | #include "core/file_sys/patch_manager.h" | 19 | #include "core/file_sys/patch_manager.h" |
| 18 | #include "core/file_sys/xts_archive.h" | 20 | #include "core/file_sys/xts_archive.h" |
| @@ -79,6 +81,14 @@ void ConfigurePerGameGeneral::applyConfiguration() { | |||
| 79 | disabled_addons.push_back(item.front()->text().toStdString()); | 81 | disabled_addons.push_back(item.front()->text().toStdString()); |
| 80 | } | 82 | } |
| 81 | 83 | ||
| 84 | auto current = Settings::values.disabled_addons[title_id]; | ||
| 85 | std::sort(disabled_addons.begin(), disabled_addons.end()); | ||
| 86 | std::sort(current.begin(), current.end()); | ||
| 87 | if (disabled_addons != current) { | ||
| 88 | FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + | ||
| 89 | "game_list" + DIR_SEP + fmt::format("{:016X}.pv.txt", title_id)); | ||
| 90 | } | ||
| 91 | |||
| 82 | Settings::values.disabled_addons[title_id] = disabled_addons; | 92 | Settings::values.disabled_addons[title_id] = disabled_addons; |
| 83 | } | 93 | } |
| 84 | 94 | ||
| @@ -88,15 +98,15 @@ void ConfigurePerGameGeneral::loadFromFile(FileSys::VirtualFile file) { | |||
| 88 | } | 98 | } |
| 89 | 99 | ||
| 90 | void ConfigurePerGameGeneral::loadConfiguration() { | 100 | void ConfigurePerGameGeneral::loadConfiguration() { |
| 91 | if (file == nullptr) | 101 | if (file == nullptr) { |
| 92 | return; | 102 | return; |
| 103 | } | ||
| 93 | 104 | ||
| 94 | const auto loader = Loader::GetLoader(file); | 105 | ui->display_title_id->setText(QString::fromStdString(fmt::format("{:016X}", title_id))); |
| 95 | |||
| 96 | ui->display_title_id->setText(fmt::format("{:016X}", title_id).c_str()); | ||
| 97 | 106 | ||
| 98 | FileSys::PatchManager pm{title_id}; | 107 | FileSys::PatchManager pm{title_id}; |
| 99 | const auto control = pm.GetControlMetadata(); | 108 | const auto control = pm.GetControlMetadata(); |
| 109 | const auto loader = Loader::GetLoader(file); | ||
| 100 | 110 | ||
| 101 | if (control.first != nullptr) { | 111 | if (control.first != nullptr) { |
| 102 | ui->display_version->setText(QString::fromStdString(control.first->GetVersionString())); | 112 | ui->display_version->setText(QString::fromStdString(control.first->GetVersionString())); |
| @@ -142,8 +152,10 @@ void ConfigurePerGameGeneral::loadConfiguration() { | |||
| 142 | const auto& disabled = Settings::values.disabled_addons[title_id]; | 152 | const auto& disabled = Settings::values.disabled_addons[title_id]; |
| 143 | 153 | ||
| 144 | for (const auto& patch : pm.GetPatchVersionNames(update_raw)) { | 154 | for (const auto& patch : pm.GetPatchVersionNames(update_raw)) { |
| 145 | QStandardItem* first_item = new QStandardItem; | 155 | const auto name = |
| 146 | const auto name = QString::fromStdString(patch.first).replace("[D] ", ""); | 156 | QString::fromStdString(patch.first).replace(QStringLiteral("[D] "), QString{}); |
| 157 | |||
| 158 | auto* const first_item = new QStandardItem; | ||
| 147 | first_item->setText(name); | 159 | first_item->setText(name); |
| 148 | first_item->setCheckable(true); | 160 | first_item->setCheckable(true); |
| 149 | 161 | ||
diff --git a/src/yuzu/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp index 10f8ba041..6d7d04c98 100644 --- a/src/yuzu/configuration/configure_profile_manager.cpp +++ b/src/yuzu/configuration/configure_profile_manager.cpp | |||
| @@ -97,7 +97,7 @@ ConfigureProfileManager ::ConfigureProfileManager(QWidget* parent) | |||
| 97 | tree_view->setContextMenuPolicy(Qt::NoContextMenu); | 97 | tree_view->setContextMenuPolicy(Qt::NoContextMenu); |
| 98 | 98 | ||
| 99 | item_model->insertColumns(0, 1); | 99 | item_model->insertColumns(0, 1); |
| 100 | item_model->setHeaderData(0, Qt::Horizontal, "Users"); | 100 | item_model->setHeaderData(0, Qt::Horizontal, tr("Users")); |
| 101 | 101 | ||
| 102 | // We must register all custom types with the Qt Automoc system so that we are able to use it | 102 | // We must register all custom types with the Qt Automoc system so that we are able to use it |
| 103 | // with signals/slots. In this case, QList falls under the umbrells of custom types. | 103 | // with signals/slots. In this case, QList falls under the umbrells of custom types. |
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index 10645a2b3..ff18ace40 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp | |||
| @@ -66,8 +66,9 @@ void ConfigureSystem::setConfiguration() { | |||
| 66 | ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.has_value()); | 66 | ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.has_value()); |
| 67 | ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.has_value()); | 67 | ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.has_value()); |
| 68 | 68 | ||
| 69 | const auto rng_seed = | 69 | const auto rng_seed = QStringLiteral("%1") |
| 70 | QString("%1").arg(Settings::values.rng_seed.value_or(0), 8, 16, QLatin1Char{'0'}).toUpper(); | 70 | .arg(Settings::values.rng_seed.value_or(0), 8, 16, QLatin1Char{'0'}) |
| 71 | .toUpper(); | ||
| 71 | ui->rng_seed_edit->setText(rng_seed); | 72 | ui->rng_seed_edit->setText(rng_seed); |
| 72 | 73 | ||
| 73 | ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.has_value()); | 74 | ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.has_value()); |
diff --git a/src/yuzu/configuration/configure_web.cpp b/src/yuzu/configuration/configure_web.cpp index 18566d028..9dc34412d 100644 --- a/src/yuzu/configuration/configure_web.cpp +++ b/src/yuzu/configuration/configure_web.cpp | |||
| @@ -78,12 +78,16 @@ void ConfigureWeb::RefreshTelemetryID() { | |||
| 78 | void ConfigureWeb::OnLoginChanged() { | 78 | void ConfigureWeb::OnLoginChanged() { |
| 79 | if (ui->edit_username->text().isEmpty() && ui->edit_token->text().isEmpty()) { | 79 | if (ui->edit_username->text().isEmpty() && ui->edit_token->text().isEmpty()) { |
| 80 | user_verified = true; | 80 | user_verified = true; |
| 81 | ui->label_username_verified->setPixmap(QIcon::fromTheme("checked").pixmap(16)); | 81 | |
| 82 | ui->label_token_verified->setPixmap(QIcon::fromTheme("checked").pixmap(16)); | 82 | const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("checked")).pixmap(16); |
| 83 | ui->label_username_verified->setPixmap(pixmap); | ||
| 84 | ui->label_token_verified->setPixmap(pixmap); | ||
| 83 | } else { | 85 | } else { |
| 84 | user_verified = false; | 86 | user_verified = false; |
| 85 | ui->label_username_verified->setPixmap(QIcon::fromTheme("failed").pixmap(16)); | 87 | |
| 86 | ui->label_token_verified->setPixmap(QIcon::fromTheme("failed").pixmap(16)); | 88 | const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("failed")).pixmap(16); |
| 89 | ui->label_username_verified->setPixmap(pixmap); | ||
| 90 | ui->label_token_verified->setPixmap(pixmap); | ||
| 87 | } | 91 | } |
| 88 | } | 92 | } |
| 89 | 93 | ||
| @@ -101,11 +105,15 @@ void ConfigureWeb::OnLoginVerified() { | |||
| 101 | ui->button_verify_login->setText(tr("Verify")); | 105 | ui->button_verify_login->setText(tr("Verify")); |
| 102 | if (verify_watcher.result()) { | 106 | if (verify_watcher.result()) { |
| 103 | user_verified = true; | 107 | user_verified = true; |
| 104 | ui->label_username_verified->setPixmap(QIcon::fromTheme("checked").pixmap(16)); | 108 | |
| 105 | ui->label_token_verified->setPixmap(QIcon::fromTheme("checked").pixmap(16)); | 109 | const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("checked")).pixmap(16); |
| 110 | ui->label_username_verified->setPixmap(pixmap); | ||
| 111 | ui->label_token_verified->setPixmap(pixmap); | ||
| 106 | } else { | 112 | } else { |
| 107 | ui->label_username_verified->setPixmap(QIcon::fromTheme("failed").pixmap(16)); | 113 | const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("failed")).pixmap(16); |
| 108 | ui->label_token_verified->setPixmap(QIcon::fromTheme("failed").pixmap(16)); | 114 | ui->label_username_verified->setPixmap(pixmap); |
| 115 | ui->label_token_verified->setPixmap(pixmap); | ||
| 116 | |||
| 109 | QMessageBox::critical( | 117 | QMessageBox::critical( |
| 110 | this, tr("Verification failed"), | 118 | this, tr("Verification failed"), |
| 111 | tr("Verification failed. Check that you have entered your username and token " | 119 | tr("Verification failed. Check that you have entered your username and token " |
diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp index 67ed0ba6d..1c80082a4 100644 --- a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp +++ b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp | |||
| @@ -135,7 +135,7 @@ GraphicsBreakPointsWidget::GraphicsBreakPointsWidget( | |||
| 135 | std::shared_ptr<Tegra::DebugContext> debug_context, QWidget* parent) | 135 | std::shared_ptr<Tegra::DebugContext> debug_context, QWidget* parent) |
| 136 | : QDockWidget(tr("Maxwell Breakpoints"), parent), Tegra::DebugContext::BreakPointObserver( | 136 | : QDockWidget(tr("Maxwell Breakpoints"), parent), Tegra::DebugContext::BreakPointObserver( |
| 137 | debug_context) { | 137 | debug_context) { |
| 138 | setObjectName("TegraBreakPointsWidget"); | 138 | setObjectName(QStringLiteral("TegraBreakPointsWidget")); |
| 139 | 139 | ||
| 140 | status_text = new QLabel(tr("Emulation running")); | 140 | status_text = new QLabel(tr("Emulation running")); |
| 141 | resume_button = new QPushButton(tr("Resume")); | 141 | resume_button = new QPushButton(tr("Resume")); |
diff --git a/src/yuzu/debugger/profiler.cpp b/src/yuzu/debugger/profiler.cpp index 86e03e46d..f594ef076 100644 --- a/src/yuzu/debugger/profiler.cpp +++ b/src/yuzu/debugger/profiler.cpp | |||
| @@ -47,7 +47,7 @@ private: | |||
| 47 | #endif | 47 | #endif |
| 48 | 48 | ||
| 49 | MicroProfileDialog::MicroProfileDialog(QWidget* parent) : QWidget(parent, Qt::Dialog) { | 49 | MicroProfileDialog::MicroProfileDialog(QWidget* parent) : QWidget(parent, Qt::Dialog) { |
| 50 | setObjectName("MicroProfile"); | 50 | setObjectName(QStringLiteral("MicroProfile")); |
| 51 | setWindowTitle(tr("MicroProfile")); | 51 | setWindowTitle(tr("MicroProfile")); |
| 52 | resize(1000, 600); | 52 | resize(1000, 600); |
| 53 | // Remove the "?" button from the titlebar and enable the maximize button | 53 | // Remove the "?" button from the titlebar and enable the maximize button |
| @@ -191,7 +191,7 @@ void MicroProfileDrawText(int x, int y, u32 hex_color, const char* text, u32 tex | |||
| 191 | for (u32 i = 0; i < text_length; ++i) { | 191 | for (u32 i = 0; i < text_length; ++i) { |
| 192 | // Position the text baseline 1 pixel above the bottom of the text cell, this gives nice | 192 | // Position the text baseline 1 pixel above the bottom of the text cell, this gives nice |
| 193 | // vertical alignment of text for a wide range of tested fonts. | 193 | // vertical alignment of text for a wide range of tested fonts. |
| 194 | mp_painter->drawText(x, y + MICROPROFILE_TEXT_HEIGHT - 2, QChar(text[i])); | 194 | mp_painter->drawText(x, y + MICROPROFILE_TEXT_HEIGHT - 2, QString{QLatin1Char{text[i]}}); |
| 195 | x += MICROPROFILE_TEXT_WIDTH + 1; | 195 | x += MICROPROFILE_TEXT_WIDTH + 1; |
| 196 | } | 196 | } |
| 197 | } | 197 | } |
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index 85b095688..cd8180f8b 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp | |||
| @@ -91,19 +91,19 @@ WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address, const Kernel::HandleTa | |||
| 91 | WaitTreeMutexInfo::~WaitTreeMutexInfo() = default; | 91 | WaitTreeMutexInfo::~WaitTreeMutexInfo() = default; |
| 92 | 92 | ||
| 93 | QString WaitTreeMutexInfo::GetText() const { | 93 | QString WaitTreeMutexInfo::GetText() const { |
| 94 | return tr("waiting for mutex 0x%1").arg(mutex_address, 16, 16, QLatin1Char('0')); | 94 | return tr("waiting for mutex 0x%1").arg(mutex_address, 16, 16, QLatin1Char{'0'}); |
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeMutexInfo::GetChildren() const { | 97 | std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeMutexInfo::GetChildren() const { |
| 98 | std::vector<std::unique_ptr<WaitTreeItem>> list; | 98 | const bool has_waiters = (mutex_value & Kernel::Mutex::MutexHasWaitersFlag) != 0; |
| 99 | |||
| 100 | bool has_waiters = (mutex_value & Kernel::Mutex::MutexHasWaitersFlag) != 0; | ||
| 101 | 99 | ||
| 100 | std::vector<std::unique_ptr<WaitTreeItem>> list; | ||
| 102 | list.push_back(std::make_unique<WaitTreeText>(tr("has waiters: %1").arg(has_waiters))); | 101 | list.push_back(std::make_unique<WaitTreeText>(tr("has waiters: %1").arg(has_waiters))); |
| 103 | list.push_back(std::make_unique<WaitTreeText>( | 102 | list.push_back(std::make_unique<WaitTreeText>( |
| 104 | tr("owner handle: 0x%1").arg(owner_handle, 8, 16, QLatin1Char('0')))); | 103 | tr("owner handle: 0x%1").arg(owner_handle, 8, 16, QLatin1Char{'0'}))); |
| 105 | if (owner != nullptr) | 104 | if (owner != nullptr) { |
| 106 | list.push_back(std::make_unique<WaitTreeThread>(*owner)); | 105 | list.push_back(std::make_unique<WaitTreeThread>(*owner)); |
| 106 | } | ||
| 107 | return list; | 107 | return list; |
| 108 | } | 108 | } |
| 109 | 109 | ||
| @@ -121,11 +121,14 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() cons | |||
| 121 | u64 base_pointer = thread.GetContext().cpu_registers[BaseRegister]; | 121 | u64 base_pointer = thread.GetContext().cpu_registers[BaseRegister]; |
| 122 | 122 | ||
| 123 | while (base_pointer != 0) { | 123 | while (base_pointer != 0) { |
| 124 | u64 lr = Memory::Read64(base_pointer + sizeof(u64)); | 124 | const u64 lr = Memory::Read64(base_pointer + sizeof(u64)); |
| 125 | if (lr == 0) | 125 | if (lr == 0) { |
| 126 | break; | 126 | break; |
| 127 | list.push_back( | 127 | } |
| 128 | std::make_unique<WaitTreeText>(tr("0x%1").arg(lr - sizeof(u32), 16, 16, QChar('0')))); | 128 | |
| 129 | list.push_back(std::make_unique<WaitTreeText>( | ||
| 130 | tr("0x%1").arg(lr - sizeof(u32), 16, 16, QLatin1Char{'0'}))); | ||
| 131 | |||
| 129 | base_pointer = Memory::Read64(base_pointer); | 132 | base_pointer = Memory::Read64(base_pointer); |
| 130 | } | 133 | } |
| 131 | 134 | ||
| @@ -174,10 +177,10 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeWaitObject::GetChildren() con | |||
| 174 | 177 | ||
| 175 | QString WaitTreeWaitObject::GetResetTypeQString(Kernel::ResetType reset_type) { | 178 | QString WaitTreeWaitObject::GetResetTypeQString(Kernel::ResetType reset_type) { |
| 176 | switch (reset_type) { | 179 | switch (reset_type) { |
| 177 | case Kernel::ResetType::OneShot: | 180 | case Kernel::ResetType::Automatic: |
| 178 | return tr("one shot"); | 181 | return tr("automatic reset"); |
| 179 | case Kernel::ResetType::Sticky: | 182 | case Kernel::ResetType::Manual: |
| 180 | return tr("sticky"); | 183 | return tr("manual reset"); |
| 181 | } | 184 | } |
| 182 | UNREACHABLE(); | 185 | UNREACHABLE(); |
| 183 | return {}; | 186 | return {}; |
| @@ -249,9 +252,9 @@ QString WaitTreeThread::GetText() const { | |||
| 249 | 252 | ||
| 250 | const auto& context = thread.GetContext(); | 253 | const auto& context = thread.GetContext(); |
| 251 | const QString pc_info = tr(" PC = 0x%1 LR = 0x%2") | 254 | const QString pc_info = tr(" PC = 0x%1 LR = 0x%2") |
| 252 | .arg(context.pc, 8, 16, QLatin1Char('0')) | 255 | .arg(context.pc, 8, 16, QLatin1Char{'0'}) |
| 253 | .arg(context.cpu_registers[30], 8, 16, QLatin1Char('0')); | 256 | .arg(context.cpu_registers[30], 8, 16, QLatin1Char{'0'}); |
| 254 | return WaitTreeWaitObject::GetText() + pc_info + " (" + status + ") "; | 257 | return QStringLiteral("%1%2 (%3) ").arg(WaitTreeWaitObject::GetText(), pc_info, status); |
| 255 | } | 258 | } |
| 256 | 259 | ||
| 257 | QColor WaitTreeThread::GetColor() const { | 260 | QColor WaitTreeThread::GetColor() const { |
| @@ -424,7 +427,7 @@ void WaitTreeModel::InitItems() { | |||
| 424 | } | 427 | } |
| 425 | 428 | ||
| 426 | WaitTreeWidget::WaitTreeWidget(QWidget* parent) : QDockWidget(tr("Wait Tree"), parent) { | 429 | WaitTreeWidget::WaitTreeWidget(QWidget* parent) : QDockWidget(tr("Wait Tree"), parent) { |
| 427 | setObjectName("WaitTreeWidget"); | 430 | setObjectName(QStringLiteral("WaitTreeWidget")); |
| 428 | view = new QTreeView(this); | 431 | view = new QTreeView(this); |
| 429 | view->setHeaderHidden(true); | 432 | view->setHeaderHidden(true); |
| 430 | setWidget(view); | 433 | setWidget(view); |
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 @@ | |||
| 14 | #include <QMenu> | 14 | #include <QMenu> |
| 15 | #include <QThreadPool> | 15 | #include <QThreadPool> |
| 16 | #include <fmt/format.h> | 16 | #include <fmt/format.h> |
| 17 | #include "common/common_paths.h" | ||
| 18 | #include "common/common_types.h" | 17 | #include "common/common_types.h" |
| 19 | #include "common/logging/log.h" | 18 | #include "common/logging/log.h" |
| 20 | #include "core/file_sys/patch_manager.h" | 19 | #include "core/file_sys/patch_manager.h" |
| @@ -48,7 +47,7 @@ bool GameListSearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* eve | |||
| 48 | return QObject::eventFilter(obj, event); | 47 | return QObject::eventFilter(obj, event); |
| 49 | } else { | 48 | } else { |
| 50 | gamelist->search_field->edit_filter->clear(); | 49 | gamelist->search_field->edit_filter->clear(); |
| 51 | edit_filter_text = ""; | 50 | edit_filter_text.clear(); |
| 52 | } | 51 | } |
| 53 | break; | 52 | break; |
| 54 | } | 53 | } |
| @@ -71,9 +70,9 @@ bool GameListSearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* eve | |||
| 71 | } | 70 | } |
| 72 | if (resultCount == 1) { | 71 | if (resultCount == 1) { |
| 73 | // To avoid loading error dialog loops while confirming them using enter | 72 | // To avoid loading error dialog loops while confirming them using enter |
| 74 | // Also users usually want to run a diffrent game after closing one | 73 | // Also users usually want to run a different game after closing one |
| 75 | gamelist->search_field->edit_filter->setText(""); | 74 | gamelist->search_field->edit_filter->clear(); |
| 76 | edit_filter_text = ""; | 75 | edit_filter_text.clear(); |
| 77 | emit gamelist->GameChosen(file_path); | 76 | emit gamelist->GameChosen(file_path); |
| 78 | } else { | 77 | } else { |
| 79 | return QObject::eventFilter(obj, event); | 78 | return QObject::eventFilter(obj, event); |
| @@ -93,7 +92,7 @@ void GameListSearchField::setFilterResult(int visible, int total) { | |||
| 93 | } | 92 | } |
| 94 | 93 | ||
| 95 | void GameListSearchField::clear() { | 94 | void GameListSearchField::clear() { |
| 96 | edit_filter->setText(""); | 95 | edit_filter->clear(); |
| 97 | } | 96 | } |
| 98 | 97 | ||
| 99 | void GameListSearchField::setFocus() { | 98 | void GameListSearchField::setFocus() { |
| @@ -103,25 +102,26 @@ void GameListSearchField::setFocus() { | |||
| 103 | } | 102 | } |
| 104 | 103 | ||
| 105 | GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} { | 104 | GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} { |
| 106 | KeyReleaseEater* keyReleaseEater = new KeyReleaseEater(parent); | 105 | auto* const key_release_eater = new KeyReleaseEater(parent); |
| 107 | layout_filter = new QHBoxLayout; | 106 | layout_filter = new QHBoxLayout; |
| 108 | layout_filter->setMargin(8); | 107 | layout_filter->setMargin(8); |
| 109 | label_filter = new QLabel; | 108 | label_filter = new QLabel; |
| 110 | label_filter->setText(tr("Filter:")); | 109 | label_filter->setText(tr("Filter:")); |
| 111 | edit_filter = new QLineEdit; | 110 | edit_filter = new QLineEdit; |
| 112 | edit_filter->setText(""); | 111 | edit_filter->clear(); |
| 113 | edit_filter->setPlaceholderText(tr("Enter pattern to filter")); | 112 | edit_filter->setPlaceholderText(tr("Enter pattern to filter")); |
| 114 | edit_filter->installEventFilter(keyReleaseEater); | 113 | edit_filter->installEventFilter(key_release_eater); |
| 115 | edit_filter->setClearButtonEnabled(true); | 114 | edit_filter->setClearButtonEnabled(true); |
| 116 | connect(edit_filter, &QLineEdit::textChanged, parent, &GameList::onTextChanged); | 115 | connect(edit_filter, &QLineEdit::textChanged, parent, &GameList::onTextChanged); |
| 117 | label_filter_result = new QLabel; | 116 | label_filter_result = new QLabel; |
| 118 | button_filter_close = new QToolButton(this); | 117 | button_filter_close = new QToolButton(this); |
| 119 | button_filter_close->setText("X"); | 118 | button_filter_close->setText(QStringLiteral("X")); |
| 120 | button_filter_close->setCursor(Qt::ArrowCursor); | 119 | button_filter_close->setCursor(Qt::ArrowCursor); |
| 121 | button_filter_close->setStyleSheet("QToolButton{ border: none; padding: 0px; color: " | 120 | button_filter_close->setStyleSheet( |
| 122 | "#000000; font-weight: bold; background: #F0F0F0; }" | 121 | QStringLiteral("QToolButton{ border: none; padding: 0px; color: " |
| 123 | "QToolButton:hover{ border: none; padding: 0px; color: " | 122 | "#000000; font-weight: bold; background: #F0F0F0; }" |
| 124 | "#EEEEEE; font-weight: bold; background: #E81123}"); | 123 | "QToolButton:hover{ border: none; padding: 0px; color: " |
| 124 | "#EEEEEE; font-weight: bold; background: #E81123}")); | ||
| 125 | connect(button_filter_close, &QToolButton::clicked, parent, &GameList::onFilterCloseClicked); | 125 | connect(button_filter_close, &QToolButton::clicked, parent, &GameList::onFilterCloseClicked); |
| 126 | layout_filter->setSpacing(10); | 126 | layout_filter->setSpacing(10); |
| 127 | layout_filter->addWidget(label_filter); | 127 | layout_filter->addWidget(label_filter); |
| @@ -141,36 +141,34 @@ GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} { | |||
| 141 | */ | 141 | */ |
| 142 | static bool ContainsAllWords(const QString& haystack, const QString& userinput) { | 142 | static bool ContainsAllWords(const QString& haystack, const QString& userinput) { |
| 143 | const QStringList userinput_split = | 143 | const QStringList userinput_split = |
| 144 | userinput.split(' ', QString::SplitBehavior::SkipEmptyParts); | 144 | userinput.split(QLatin1Char{' '}, QString::SplitBehavior::SkipEmptyParts); |
| 145 | 145 | ||
| 146 | return std::all_of(userinput_split.begin(), userinput_split.end(), | 146 | return std::all_of(userinput_split.begin(), userinput_split.end(), |
| 147 | [&haystack](const QString& s) { return haystack.contains(s); }); | 147 | [&haystack](const QString& s) { return haystack.contains(s); }); |
| 148 | } | 148 | } |
| 149 | 149 | ||
| 150 | // Event in order to filter the gamelist after editing the searchfield | 150 | // Event in order to filter the gamelist after editing the searchfield |
| 151 | void GameList::onTextChanged(const QString& newText) { | 151 | void GameList::onTextChanged(const QString& new_text) { |
| 152 | int rowCount = tree_view->model()->rowCount(); | 152 | const int row_count = tree_view->model()->rowCount(); |
| 153 | QString edit_filter_text = newText.toLower(); | 153 | const QString edit_filter_text = new_text.toLower(); |
| 154 | 154 | const QModelIndex root_index = item_model->invisibleRootItem()->index(); | |
| 155 | QModelIndex root_index = item_model->invisibleRootItem()->index(); | ||
| 156 | 155 | ||
| 157 | // If the searchfield is empty every item is visible | 156 | // If the searchfield is empty every item is visible |
| 158 | // Otherwise the filter gets applied | 157 | // Otherwise the filter gets applied |
| 159 | if (edit_filter_text.isEmpty()) { | 158 | if (edit_filter_text.isEmpty()) { |
| 160 | for (int i = 0; i < rowCount; ++i) { | 159 | for (int i = 0; i < row_count; ++i) { |
| 161 | tree_view->setRowHidden(i, root_index, false); | 160 | tree_view->setRowHidden(i, root_index, false); |
| 162 | } | 161 | } |
| 163 | search_field->setFilterResult(rowCount, rowCount); | 162 | search_field->setFilterResult(row_count, row_count); |
| 164 | } else { | 163 | } else { |
| 165 | int result_count = 0; | 164 | int result_count = 0; |
| 166 | for (int i = 0; i < rowCount; ++i) { | 165 | for (int i = 0; i < row_count; ++i) { |
| 167 | const QStandardItem* child_file = item_model->item(i, 0); | 166 | const QStandardItem* child_file = item_model->item(i, 0); |
| 168 | const QString file_path = | 167 | const QString file_path = |
| 169 | child_file->data(GameListItemPath::FullPathRole).toString().toLower(); | 168 | child_file->data(GameListItemPath::FullPathRole).toString().toLower(); |
| 170 | QString file_name = file_path.mid(file_path.lastIndexOf('/') + 1); | ||
| 171 | const QString file_title = | 169 | const QString file_title = |
| 172 | child_file->data(GameListItemPath::TitleRole).toString().toLower(); | 170 | child_file->data(GameListItemPath::TitleRole).toString().toLower(); |
| 173 | const QString file_programmid = | 171 | const QString file_program_id = |
| 174 | child_file->data(GameListItemPath::ProgramIdRole).toString().toLower(); | 172 | child_file->data(GameListItemPath::ProgramIdRole).toString().toLower(); |
| 175 | 173 | ||
| 176 | // Only items which filename in combination with its title contains all words | 174 | // Only items which filename in combination with its title contains all words |
| @@ -178,14 +176,16 @@ void GameList::onTextChanged(const QString& newText) { | |||
| 178 | // The search is case insensitive because of toLower() | 176 | // The search is case insensitive because of toLower() |
| 179 | // I decided not to use Qt::CaseInsensitive in containsAllWords to prevent | 177 | // I decided not to use Qt::CaseInsensitive in containsAllWords to prevent |
| 180 | // multiple conversions of edit_filter_text for each game in the gamelist | 178 | // multiple conversions of edit_filter_text for each game in the gamelist |
| 181 | if (ContainsAllWords(file_name.append(' ').append(file_title), edit_filter_text) || | 179 | const QString file_name = file_path.mid(file_path.lastIndexOf(QLatin1Char{'/'}) + 1) + |
| 182 | (file_programmid.count() == 16 && edit_filter_text.contains(file_programmid))) { | 180 | QLatin1Char{' '} + file_title; |
| 181 | if (ContainsAllWords(file_name, edit_filter_text) || | ||
| 182 | (file_program_id.count() == 16 && edit_filter_text.contains(file_program_id))) { | ||
| 183 | tree_view->setRowHidden(i, root_index, false); | 183 | tree_view->setRowHidden(i, root_index, false); |
| 184 | ++result_count; | 184 | ++result_count; |
| 185 | } else { | 185 | } else { |
| 186 | tree_view->setRowHidden(i, root_index, true); | 186 | tree_view->setRowHidden(i, root_index, true); |
| 187 | } | 187 | } |
| 188 | search_field->setFilterResult(result_count, rowCount); | 188 | search_field->setFilterResult(result_count, row_count); |
| 189 | } | 189 | } |
| 190 | } | 190 | } |
| 191 | } | 191 | } |
| @@ -216,7 +216,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvide | |||
| 216 | tree_view->setEditTriggers(QHeaderView::NoEditTriggers); | 216 | tree_view->setEditTriggers(QHeaderView::NoEditTriggers); |
| 217 | tree_view->setUniformRowHeights(true); | 217 | tree_view->setUniformRowHeights(true); |
| 218 | tree_view->setContextMenuPolicy(Qt::CustomContextMenu); | 218 | tree_view->setContextMenuPolicy(Qt::CustomContextMenu); |
| 219 | tree_view->setStyleSheet("QTreeView{ border: none; }"); | 219 | tree_view->setStyleSheet(QStringLiteral("QTreeView{ border: none; }")); |
| 220 | 220 | ||
| 221 | item_model->insertColumns(0, UISettings::values.show_add_ons ? COLUMN_COUNT : COLUMN_COUNT - 1); | 221 | item_model->insertColumns(0, UISettings::values.show_add_ons ? COLUMN_COUNT : COLUMN_COUNT - 1); |
| 222 | item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name")); | 222 | item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name")); |
| @@ -282,9 +282,9 @@ void GameList::ValidateEntry(const QModelIndex& item) { | |||
| 282 | const QFileInfo file_info{file_path}; | 282 | const QFileInfo file_info{file_path}; |
| 283 | if (file_info.isDir()) { | 283 | if (file_info.isDir()) { |
| 284 | const QDir dir{file_path}; | 284 | const QDir dir{file_path}; |
| 285 | const QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files); | 285 | const QStringList matching_main = dir.entryList({QStringLiteral("main")}, QDir::Files); |
| 286 | if (matching_main.size() == 1) { | 286 | if (matching_main.size() == 1) { |
| 287 | emit GameChosen(dir.path() + DIR_SEP + matching_main[0]); | 287 | emit GameChosen(dir.path() + QDir::separator() + matching_main[0]); |
| 288 | } | 288 | } |
| 289 | return; | 289 | return; |
| 290 | } | 290 | } |
| @@ -360,7 +360,7 @@ void GameList::PopupContextMenu(const QPoint& menu_location) { | |||
| 360 | } | 360 | } |
| 361 | 361 | ||
| 362 | void GameList::LoadCompatibilityList() { | 362 | void GameList::LoadCompatibilityList() { |
| 363 | QFile compat_list{":compatibility_list/compatibility_list.json"}; | 363 | QFile compat_list{QStringLiteral(":compatibility_list/compatibility_list.json")}; |
| 364 | 364 | ||
| 365 | if (!compat_list.open(QFile::ReadOnly | QFile::Text)) { | 365 | if (!compat_list.open(QFile::ReadOnly | QFile::Text)) { |
| 366 | LOG_ERROR(Frontend, "Unable to open game compatibility list"); | 366 | LOG_ERROR(Frontend, "Unable to open game compatibility list"); |
| @@ -378,25 +378,27 @@ void GameList::LoadCompatibilityList() { | |||
| 378 | return; | 378 | return; |
| 379 | } | 379 | } |
| 380 | 380 | ||
| 381 | const QString string_content = content; | 381 | const QJsonDocument json = QJsonDocument::fromJson(content); |
| 382 | QJsonDocument json = QJsonDocument::fromJson(string_content.toUtf8()); | 382 | const QJsonArray arr = json.array(); |
| 383 | QJsonArray arr = json.array(); | ||
| 384 | 383 | ||
| 385 | for (const QJsonValueRef value : arr) { | 384 | for (const QJsonValue value : arr) { |
| 386 | QJsonObject game = value.toObject(); | 385 | const QJsonObject game = value.toObject(); |
| 386 | const QString compatibility_key = QStringLiteral("compatibility"); | ||
| 387 | 387 | ||
| 388 | if (game.contains("compatibility") && game["compatibility"].isDouble()) { | 388 | if (!game.contains(compatibility_key) || !game[compatibility_key].isDouble()) { |
| 389 | int compatibility = game["compatibility"].toInt(); | 389 | continue; |
| 390 | QString directory = game["directory"].toString(); | 390 | } |
| 391 | QJsonArray ids = game["releases"].toArray(); | ||
| 392 | 391 | ||
| 393 | for (const QJsonValueRef id_ref : ids) { | 392 | const int compatibility = game[compatibility_key].toInt(); |
| 394 | QJsonObject id_object = id_ref.toObject(); | 393 | const QString directory = game[QStringLiteral("directory")].toString(); |
| 395 | QString id = id_object["id"].toString(); | 394 | const QJsonArray ids = game[QStringLiteral("releases")].toArray(); |
| 396 | compatibility_list.emplace( | 395 | |
| 397 | id.toUpper().toStdString(), | 396 | for (const QJsonValue id_ref : ids) { |
| 398 | std::make_pair(QString::number(compatibility), directory)); | 397 | const QJsonObject id_object = id_ref.toObject(); |
| 399 | } | 398 | const QString id = id_object[QStringLiteral("id")].toString(); |
| 399 | |||
| 400 | compatibility_list.emplace(id.toUpper().toStdString(), | ||
| 401 | std::make_pair(QString::number(compatibility), directory)); | ||
| 400 | } | 402 | } |
| 401 | } | 403 | } |
| 402 | } | 404 | } |
| @@ -464,7 +466,10 @@ void GameList::LoadInterfaceLayout() { | |||
| 464 | item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder()); | 466 | item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder()); |
| 465 | } | 467 | } |
| 466 | 468 | ||
| 467 | const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci", "nsp"}; | 469 | const QStringList GameList::supported_file_extensions = { |
| 470 | QStringLiteral("nso"), QStringLiteral("nro"), QStringLiteral("nca"), | ||
| 471 | QStringLiteral("xci"), QStringLiteral("nsp"), | ||
| 472 | }; | ||
| 468 | 473 | ||
| 469 | void GameList::RefreshGameDirectory() { | 474 | void GameList::RefreshGameDirectory() { |
| 470 | if (!UISettings::values.game_directory_path.isEmpty() && current_worker != nullptr) { | 475 | 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: | |||
| 76 | void OpenPerGameGeneralRequested(const std::string& file); | 76 | void OpenPerGameGeneralRequested(const std::string& file); |
| 77 | 77 | ||
| 78 | private slots: | 78 | private slots: |
| 79 | void onTextChanged(const QString& newText); | 79 | void onTextChanged(const QString& new_text); |
| 80 | void onFilterCloseClicked(); | 80 | void onFilterCloseClicked(); |
| 81 | 81 | ||
| 82 | private: | 82 | 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 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <algorithm> | ||
| 8 | #include <array> | 7 | #include <array> |
| 9 | #include <map> | 8 | #include <map> |
| 10 | #include <string> | 9 | #include <string> |
| 11 | #include <unordered_map> | ||
| 12 | #include <utility> | 10 | #include <utility> |
| 13 | 11 | ||
| 14 | #include <QCoreApplication> | 12 | #include <QCoreApplication> |
| @@ -25,8 +23,8 @@ | |||
| 25 | #include "yuzu/util/util.h" | 23 | #include "yuzu/util/util.h" |
| 26 | 24 | ||
| 27 | /** | 25 | /** |
| 28 | * Gets the default icon (for games without valid SMDH) | 26 | * Gets the default icon (for games without valid title metadata) |
| 29 | * @param large If true, returns large icon (48x48), otherwise returns small icon (24x24) | 27 | * @param size The desired width and height of the default icon. |
| 30 | * @return QPixmap default icon | 28 | * @return QPixmap default icon |
| 31 | */ | 29 | */ |
| 32 | static QPixmap GetDefaultIcon(u32 size) { | 30 | static QPixmap GetDefaultIcon(u32 size) { |
| @@ -46,7 +44,7 @@ public: | |||
| 46 | * A specialization of GameListItem for path values. | 44 | * A specialization of GameListItem for path values. |
| 47 | * This class ensures that for every full path value it holds, a correct string representation | 45 | * This class ensures that for every full path value it holds, a correct string representation |
| 48 | * of just the filename (with no extension) will be displayed to the user. | 46 | * of just the filename (with no extension) will be displayed to the user. |
| 49 | * If this class receives valid SMDH data, it will also display game icons and titles. | 47 | * If this class receives valid title metadata, it will also display game icons and titles. |
| 50 | */ | 48 | */ |
| 51 | class GameListItemPath : public GameListItem { | 49 | class GameListItemPath : public GameListItem { |
| 52 | public: | 50 | public: |
| @@ -95,7 +93,7 @@ public: | |||
| 95 | if (row2.isEmpty()) | 93 | if (row2.isEmpty()) |
| 96 | return row1; | 94 | return row1; |
| 97 | 95 | ||
| 98 | return QString(row1 + "\n " + row2); | 96 | return QString(row1 + QStringLiteral("\n ") + row2); |
| 99 | } | 97 | } |
| 100 | 98 | ||
| 101 | return GameListItem::data(role); | 99 | return GameListItem::data(role); |
| @@ -115,13 +113,14 @@ public: | |||
| 115 | }; | 113 | }; |
| 116 | // clang-format off | 114 | // clang-format off |
| 117 | static const std::map<QString, CompatStatus> status_data = { | 115 | static const std::map<QString, CompatStatus> status_data = { |
| 118 | {"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.")}}, | 116 | {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.")}}, |
| 119 | {"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.")}}, | 117 | {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.")}}, |
| 120 | {"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.")}}, | 118 | {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.")}}, |
| 121 | {"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.")}}, | 119 | {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.")}}, |
| 122 | {"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.")}}, | 120 | {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.")}}, |
| 123 | {"5", {"#828282", QT_TR_NOOP("Won't Boot"), QT_TR_NOOP("The game crashes when attempting to startup.")}}, | 121 | {QStringLiteral("5"), {QStringLiteral("#828282"), QT_TR_NOOP("Won't Boot"), QT_TR_NOOP("The game crashes when attempting to startup.")}}, |
| 124 | {"99", {"#000000", QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}}}; | 122 | {QStringLiteral("99"), {QStringLiteral("#000000"), QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}}, |
| 123 | }; | ||
| 125 | // clang-format on | 124 | // clang-format on |
| 126 | 125 | ||
| 127 | auto iterator = status_data.find(compatibility); | 126 | auto iterator = status_data.find(compatibility); |
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 8687e7c5a..4d951a4e7 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | 9 | ||
| 10 | #include <QDir> | 10 | #include <QDir> |
| 11 | #include <QFileInfo> | 11 | #include <QFileInfo> |
| 12 | #include <QSettings> | ||
| 12 | 13 | ||
| 13 | #include "common/common_paths.h" | 14 | #include "common/common_paths.h" |
| 14 | #include "common/file_util.h" | 15 | #include "common/file_util.h" |
| @@ -30,13 +31,119 @@ | |||
| 30 | #include "yuzu/ui_settings.h" | 31 | #include "yuzu/ui_settings.h" |
| 31 | 32 | ||
| 32 | namespace { | 33 | namespace { |
| 34 | |||
| 35 | template <typename T> | ||
| 36 | T GetGameListCachedObject(const std::string& filename, const std::string& ext, | ||
| 37 | const std::function<T()>& generator); | ||
| 38 | |||
| 39 | template <> | ||
| 40 | QString GetGameListCachedObject(const std::string& filename, const std::string& ext, | ||
| 41 | const std::function<QString()>& generator) { | ||
| 42 | if (!UISettings::values.cache_game_list || filename == "0000000000000000") { | ||
| 43 | return generator(); | ||
| 44 | } | ||
| 45 | |||
| 46 | const auto path = FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + "game_list" + | ||
| 47 | DIR_SEP + filename + '.' + ext; | ||
| 48 | |||
| 49 | FileUtil::CreateFullPath(path); | ||
| 50 | |||
| 51 | if (!FileUtil::Exists(path)) { | ||
| 52 | const auto str = generator(); | ||
| 53 | |||
| 54 | std::ofstream stream(path); | ||
| 55 | if (stream) { | ||
| 56 | stream << str.toStdString(); | ||
| 57 | } | ||
| 58 | |||
| 59 | return str; | ||
| 60 | } | ||
| 61 | |||
| 62 | std::ifstream stream(path); | ||
| 63 | |||
| 64 | if (stream) { | ||
| 65 | const std::string out(std::istreambuf_iterator<char>{stream}, | ||
| 66 | std::istreambuf_iterator<char>{}); | ||
| 67 | return QString::fromStdString(out); | ||
| 68 | } | ||
| 69 | |||
| 70 | return generator(); | ||
| 71 | } | ||
| 72 | |||
| 73 | template <> | ||
| 74 | std::pair<std::vector<u8>, std::string> GetGameListCachedObject( | ||
| 75 | const std::string& filename, const std::string& ext, | ||
| 76 | const std::function<std::pair<std::vector<u8>, std::string>()>& generator) { | ||
| 77 | if (!UISettings::values.cache_game_list || filename == "0000000000000000") { | ||
| 78 | return generator(); | ||
| 79 | } | ||
| 80 | |||
| 81 | const auto path1 = FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + "game_list" + | ||
| 82 | DIR_SEP + filename + ".jpeg"; | ||
| 83 | const auto path2 = FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + "game_list" + | ||
| 84 | DIR_SEP + filename + ".appname.txt"; | ||
| 85 | |||
| 86 | FileUtil::CreateFullPath(path1); | ||
| 87 | |||
| 88 | if (!FileUtil::Exists(path1) || !FileUtil::Exists(path2)) { | ||
| 89 | const auto [icon, nacp] = generator(); | ||
| 90 | |||
| 91 | FileUtil::IOFile file1(path1, "wb"); | ||
| 92 | if (!file1.IsOpen()) { | ||
| 93 | LOG_ERROR(Frontend, "Failed to open cache file."); | ||
| 94 | return generator(); | ||
| 95 | } | ||
| 96 | |||
| 97 | if (!file1.Resize(icon.size())) { | ||
| 98 | LOG_ERROR(Frontend, "Failed to resize cache file to necessary size."); | ||
| 99 | return generator(); | ||
| 100 | } | ||
| 101 | |||
| 102 | if (file1.WriteBytes(icon.data(), icon.size()) != icon.size()) { | ||
| 103 | LOG_ERROR(Frontend, "Failed to write data to cache file."); | ||
| 104 | return generator(); | ||
| 105 | } | ||
| 106 | |||
| 107 | std::ofstream stream2(path2, std::ios::out); | ||
| 108 | if (stream2) { | ||
| 109 | stream2 << nacp; | ||
| 110 | } | ||
| 111 | |||
| 112 | return std::make_pair(icon, nacp); | ||
| 113 | } | ||
| 114 | |||
| 115 | FileUtil::IOFile file1(path1, "rb"); | ||
| 116 | std::ifstream stream2(path2); | ||
| 117 | |||
| 118 | if (!file1.IsOpen()) { | ||
| 119 | LOG_ERROR(Frontend, "Failed to open cache file for reading."); | ||
| 120 | return generator(); | ||
| 121 | } | ||
| 122 | |||
| 123 | if (!stream2) { | ||
| 124 | LOG_ERROR(Frontend, "Failed to open cache file for reading."); | ||
| 125 | return generator(); | ||
| 126 | } | ||
| 127 | |||
| 128 | std::vector<u8> vec(file1.GetSize()); | ||
| 129 | file1.ReadBytes(vec.data(), vec.size()); | ||
| 130 | |||
| 131 | if (stream2 && !vec.empty()) { | ||
| 132 | const std::string out(std::istreambuf_iterator<char>{stream2}, | ||
| 133 | std::istreambuf_iterator<char>{}); | ||
| 134 | return std::make_pair(vec, out); | ||
| 135 | } | ||
| 136 | |||
| 137 | return generator(); | ||
| 138 | } | ||
| 139 | |||
| 33 | void GetMetadataFromControlNCA(const FileSys::PatchManager& patch_manager, const FileSys::NCA& nca, | 140 | void GetMetadataFromControlNCA(const FileSys::PatchManager& patch_manager, const FileSys::NCA& nca, |
| 34 | std::vector<u8>& icon, std::string& name) { | 141 | std::vector<u8>& icon, std::string& name) { |
| 35 | auto [nacp, icon_file] = patch_manager.ParseControlNCA(nca); | 142 | std::tie(icon, name) = GetGameListCachedObject<std::pair<std::vector<u8>, std::string>>( |
| 36 | if (icon_file != nullptr) | 143 | fmt::format("{:016X}", patch_manager.GetTitleID()), {}, [&patch_manager, &nca] { |
| 37 | icon = icon_file->ReadAllBytes(); | 144 | const auto [nacp, icon_f] = patch_manager.ParseControlNCA(nca); |
| 38 | if (nacp != nullptr) | 145 | return std::make_pair(icon_f->ReadAllBytes(), nacp->GetApplicationName()); |
| 39 | name = nacp->GetApplicationName(); | 146 | }); |
| 40 | } | 147 | } |
| 41 | 148 | ||
| 42 | bool HasSupportedFileExtension(const std::string& file_name) { | 149 | bool HasSupportedFileExtension(const std::string& file_name) { |
| @@ -45,7 +152,7 @@ bool HasSupportedFileExtension(const std::string& file_name) { | |||
| 45 | } | 152 | } |
| 46 | 153 | ||
| 47 | bool IsExtractedNCAMain(const std::string& file_name) { | 154 | bool IsExtractedNCAMain(const std::string& file_name) { |
| 48 | return QFileInfo(QString::fromStdString(file_name)).fileName() == "main"; | 155 | return QFileInfo(QString::fromStdString(file_name)).fileName() == QStringLiteral("main"); |
| 49 | } | 156 | } |
| 50 | 157 | ||
| 51 | QString FormatGameName(const std::string& physical_name) { | 158 | QString FormatGameName(const std::string& physical_name) { |
| @@ -97,7 +204,7 @@ QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::stri | |||
| 97 | const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); | 204 | const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); |
| 98 | 205 | ||
| 99 | // The game list uses this as compatibility number for untested games | 206 | // The game list uses this as compatibility number for untested games |
| 100 | QString compatibility{"99"}; | 207 | QString compatibility{QStringLiteral("99")}; |
| 101 | if (it != compatibility_list.end()) { | 208 | if (it != compatibility_list.end()) { |
| 102 | compatibility = it->second.first; | 209 | compatibility = it->second.first; |
| 103 | } | 210 | } |
| @@ -114,8 +221,11 @@ QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::stri | |||
| 114 | }; | 221 | }; |
| 115 | 222 | ||
| 116 | if (UISettings::values.show_add_ons) { | 223 | if (UISettings::values.show_add_ons) { |
| 117 | list.insert( | 224 | const auto patch_versions = GetGameListCachedObject<QString>( |
| 118 | 2, new GameListItem(FormatPatchNameVersions(patch, loader, loader.IsRomFSUpdatable()))); | 225 | fmt::format("{:016X}", patch.GetTitleID()), "pv.txt", [&patch, &loader] { |
| 226 | return FormatPatchNameVersions(patch, loader, loader.IsRomFSUpdatable()); | ||
| 227 | }); | ||
| 228 | list.insert(2, new GameListItem(patch_versions)); | ||
| 119 | } | 229 | } |
| 120 | 230 | ||
| 121 | return list; | 231 | return list; |
diff --git a/src/yuzu/hotkeys.h b/src/yuzu/hotkeys.h index 4f526dc7e..248fadaf3 100644 --- a/src/yuzu/hotkeys.h +++ b/src/yuzu/hotkeys.h | |||
| @@ -67,8 +67,6 @@ public: | |||
| 67 | 67 | ||
| 68 | private: | 68 | private: |
| 69 | struct Hotkey { | 69 | struct Hotkey { |
| 70 | Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {} | ||
| 71 | |||
| 72 | QKeySequence keyseq; | 70 | QKeySequence keyseq; |
| 73 | QShortcut* shortcut = nullptr; | 71 | QShortcut* shortcut = nullptr; |
| 74 | Qt::ShortcutContext context = Qt::WindowShortcut; | 72 | Qt::ShortcutContext context = Qt::WindowShortcut; |
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 @@ | |||
| 30 | #include <QMovie> | 30 | #include <QMovie> |
| 31 | #endif | 31 | #endif |
| 32 | 32 | ||
| 33 | constexpr const char PROGRESSBAR_STYLE_PREPARE[] = R"( | 33 | constexpr char PROGRESSBAR_STYLE_PREPARE[] = R"( |
| 34 | QProgressBar {} | 34 | QProgressBar {} |
| 35 | QProgressBar::chunk {})"; | 35 | QProgressBar::chunk {})"; |
| 36 | 36 | ||
| 37 | constexpr const char PROGRESSBAR_STYLE_DECOMPILE[] = R"( | 37 | constexpr char PROGRESSBAR_STYLE_DECOMPILE[] = R"( |
| 38 | QProgressBar { | 38 | QProgressBar { |
| 39 | background-color: black; | 39 | background-color: black; |
| 40 | border: 2px solid white; | 40 | border: 2px solid white; |
| @@ -46,7 +46,7 @@ QProgressBar::chunk { | |||
| 46 | width: 1px; | 46 | width: 1px; |
| 47 | })"; | 47 | })"; |
| 48 | 48 | ||
| 49 | constexpr const char PROGRESSBAR_STYLE_BUILD[] = R"( | 49 | constexpr char PROGRESSBAR_STYLE_BUILD[] = R"( |
| 50 | QProgressBar { | 50 | QProgressBar { |
| 51 | background-color: black; | 51 | background-color: black; |
| 52 | border: 2px solid white; | 52 | border: 2px solid white; |
| @@ -58,7 +58,7 @@ QProgressBar::chunk { | |||
| 58 | width: 1px; | 58 | width: 1px; |
| 59 | })"; | 59 | })"; |
| 60 | 60 | ||
| 61 | constexpr const char PROGRESSBAR_STYLE_COMPLETE[] = R"( | 61 | constexpr char PROGRESSBAR_STYLE_COMPLETE[] = R"( |
| 62 | QProgressBar { | 62 | QProgressBar { |
| 63 | background-color: #0ab9e6; | 63 | background-color: #0ab9e6; |
| 64 | border: 2px solid white; | 64 | border: 2px solid white; |
| @@ -149,10 +149,10 @@ void LoadingScreen::OnLoadComplete() { | |||
| 149 | void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, | 149 | void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, |
| 150 | std::size_t total) { | 150 | std::size_t total) { |
| 151 | using namespace std::chrono; | 151 | using namespace std::chrono; |
| 152 | auto now = high_resolution_clock::now(); | 152 | const auto now = high_resolution_clock::now(); |
| 153 | // reset the timer if the stage changes | 153 | // reset the timer if the stage changes |
| 154 | if (stage != previous_stage) { | 154 | if (stage != previous_stage) { |
| 155 | ui->progress_bar->setStyleSheet(progressbar_style[stage]); | 155 | ui->progress_bar->setStyleSheet(QString::fromUtf8(progressbar_style[stage])); |
| 156 | // Hide the progress bar during the prepare stage | 156 | // Hide the progress bar during the prepare stage |
| 157 | if (stage == VideoCore::LoadCallbackStage::Prepare) { | 157 | if (stage == VideoCore::LoadCallbackStage::Prepare) { |
| 158 | ui->progress_bar->hide(); | 158 | ui->progress_bar->hide(); |
| @@ -178,16 +178,16 @@ void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size | |||
| 178 | slow_shader_first_value = value; | 178 | slow_shader_first_value = value; |
| 179 | } | 179 | } |
| 180 | // only calculate an estimate time after a second has passed since stage change | 180 | // only calculate an estimate time after a second has passed since stage change |
| 181 | auto diff = duration_cast<milliseconds>(now - slow_shader_start); | 181 | const auto diff = duration_cast<milliseconds>(now - slow_shader_start); |
| 182 | if (diff > seconds{1}) { | 182 | if (diff > seconds{1}) { |
| 183 | auto eta_mseconds = | 183 | const auto eta_mseconds = |
| 184 | static_cast<long>(static_cast<double>(total - slow_shader_first_value) / | 184 | static_cast<long>(static_cast<double>(total - slow_shader_first_value) / |
| 185 | (value - slow_shader_first_value) * diff.count()); | 185 | (value - slow_shader_first_value) * diff.count()); |
| 186 | estimate = | 186 | estimate = |
| 187 | tr("Estimated Time %1") | 187 | tr("Estimated Time %1") |
| 188 | .arg(QTime(0, 0, 0, 0) | 188 | .arg(QTime(0, 0, 0, 0) |
| 189 | .addMSecs(std::max<long>(eta_mseconds - diff.count() + 1000, 1000)) | 189 | .addMSecs(std::max<long>(eta_mseconds - diff.count() + 1000, 1000)) |
| 190 | .toString("mm:ss")); | 190 | .toString(QStringLiteral("mm:ss"))); |
| 191 | } | 191 | } |
| 192 | } | 192 | } |
| 193 | 193 | ||
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index e33e3aaaf..f8a0daebd 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -198,11 +198,11 @@ GMainWindow::GMainWindow() | |||
| 198 | 198 | ||
| 199 | ConnectMenuEvents(); | 199 | ConnectMenuEvents(); |
| 200 | ConnectWidgetEvents(); | 200 | ConnectWidgetEvents(); |
| 201 | |||
| 201 | LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, | 202 | LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, |
| 202 | Common::g_scm_desc); | 203 | Common::g_scm_desc); |
| 204 | UpdateWindowTitle(); | ||
| 203 | 205 | ||
| 204 | setWindowTitle(QString("yuzu %1| %2-%3") | ||
| 205 | .arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc)); | ||
| 206 | show(); | 206 | show(); |
| 207 | 207 | ||
| 208 | Core::System::GetInstance().SetContentProvider( | 208 | Core::System::GetInstance().SetContentProvider( |
| @@ -281,7 +281,7 @@ void GMainWindow::SoftwareKeyboardInvokeCheckDialog(std::u16string error_message | |||
| 281 | void GMainWindow::WebBrowserOpenPage(std::string_view filename, std::string_view additional_args) { | 281 | void GMainWindow::WebBrowserOpenPage(std::string_view filename, std::string_view additional_args) { |
| 282 | NXInputWebEngineView web_browser_view(this); | 282 | NXInputWebEngineView web_browser_view(this); |
| 283 | 283 | ||
| 284 | // Scope to contain the QProgressDialog for initalization | 284 | // Scope to contain the QProgressDialog for initialization |
| 285 | { | 285 | { |
| 286 | QProgressDialog progress(this); | 286 | QProgressDialog progress(this); |
| 287 | progress.setMinimumDuration(200); | 287 | progress.setMinimumDuration(200); |
| @@ -301,7 +301,7 @@ void GMainWindow::WebBrowserOpenPage(std::string_view filename, std::string_view | |||
| 301 | QWebEngineScript nx_shim; | 301 | QWebEngineScript nx_shim; |
| 302 | nx_shim.setSourceCode(GetNXShimInjectionScript()); | 302 | nx_shim.setSourceCode(GetNXShimInjectionScript()); |
| 303 | nx_shim.setWorldId(QWebEngineScript::MainWorld); | 303 | nx_shim.setWorldId(QWebEngineScript::MainWorld); |
| 304 | nx_shim.setName("nx_inject.js"); | 304 | nx_shim.setName(QStringLiteral("nx_inject.js")); |
| 305 | nx_shim.setInjectionPoint(QWebEngineScript::DocumentCreation); | 305 | nx_shim.setInjectionPoint(QWebEngineScript::DocumentCreation); |
| 306 | nx_shim.setRunsOnSubFrames(true); | 306 | nx_shim.setRunsOnSubFrames(true); |
| 307 | web_browser_view.page()->profile()->scripts()->insert(nx_shim); | 307 | web_browser_view.page()->profile()->scripts()->insert(nx_shim); |
| @@ -347,7 +347,7 @@ void GMainWindow::WebBrowserOpenPage(std::string_view filename, std::string_view | |||
| 347 | const auto fire_js_keypress = [&web_browser_view](u32 key_code) { | 347 | const auto fire_js_keypress = [&web_browser_view](u32 key_code) { |
| 348 | web_browser_view.page()->runJavaScript( | 348 | web_browser_view.page()->runJavaScript( |
| 349 | QStringLiteral("document.dispatchEvent(new KeyboardEvent('keydown', {'key': %1}));") | 349 | QStringLiteral("document.dispatchEvent(new KeyboardEvent('keydown', {'key': %1}));") |
| 350 | .arg(QString::fromStdString(std::to_string(key_code)))); | 350 | .arg(key_code)); |
| 351 | }; | 351 | }; |
| 352 | 352 | ||
| 353 | QMessageBox::information( | 353 | QMessageBox::information( |
| @@ -468,7 +468,7 @@ void GMainWindow::InitializeWidgets() { | |||
| 468 | statusBar()->addPermanentWidget(label, 0); | 468 | statusBar()->addPermanentWidget(label, 0); |
| 469 | } | 469 | } |
| 470 | statusBar()->setVisible(true); | 470 | statusBar()->setVisible(true); |
| 471 | setStyleSheet("QStatusBar::item{border: none;}"); | 471 | setStyleSheet(QStringLiteral("QStatusBar::item{border: none;}")); |
| 472 | } | 472 | } |
| 473 | 473 | ||
| 474 | void GMainWindow::InitializeDebugWidgets() { | 474 | void GMainWindow::InitializeDebugWidgets() { |
| @@ -518,58 +518,67 @@ void GMainWindow::InitializeRecentFileMenuActions() { | |||
| 518 | void GMainWindow::InitializeHotkeys() { | 518 | void GMainWindow::InitializeHotkeys() { |
| 519 | hotkey_registry.LoadHotkeys(); | 519 | hotkey_registry.LoadHotkeys(); |
| 520 | 520 | ||
| 521 | ui.action_Load_File->setShortcut(hotkey_registry.GetKeySequence("Main Window", "Load File")); | 521 | const QString main_window = QStringLiteral("Main Window"); |
| 522 | const QString load_file = QStringLiteral("Load File"); | ||
| 523 | const QString exit_yuzu = QStringLiteral("Exit yuzu"); | ||
| 524 | const QString stop_emulation = QStringLiteral("Stop Emulation"); | ||
| 525 | const QString toggle_filter_bar = QStringLiteral("Toggle Filter Bar"); | ||
| 526 | const QString toggle_status_bar = QStringLiteral("Toggle Status Bar"); | ||
| 527 | const QString fullscreen = QStringLiteral("Fullscreen"); | ||
| 528 | |||
| 529 | ui.action_Load_File->setShortcut(hotkey_registry.GetKeySequence(main_window, load_file)); | ||
| 522 | ui.action_Load_File->setShortcutContext( | 530 | ui.action_Load_File->setShortcutContext( |
| 523 | hotkey_registry.GetShortcutContext("Main Window", "Load File")); | 531 | hotkey_registry.GetShortcutContext(main_window, load_file)); |
| 524 | 532 | ||
| 525 | ui.action_Exit->setShortcut(hotkey_registry.GetKeySequence("Main Window", "Exit yuzu")); | 533 | ui.action_Exit->setShortcut(hotkey_registry.GetKeySequence(main_window, exit_yuzu)); |
| 526 | ui.action_Exit->setShortcutContext( | 534 | ui.action_Exit->setShortcutContext(hotkey_registry.GetShortcutContext(main_window, exit_yuzu)); |
| 527 | hotkey_registry.GetShortcutContext("Main Window", "Exit yuzu")); | ||
| 528 | 535 | ||
| 529 | ui.action_Stop->setShortcut(hotkey_registry.GetKeySequence("Main Window", "Stop Emulation")); | 536 | ui.action_Stop->setShortcut(hotkey_registry.GetKeySequence(main_window, stop_emulation)); |
| 530 | ui.action_Stop->setShortcutContext( | 537 | ui.action_Stop->setShortcutContext( |
| 531 | hotkey_registry.GetShortcutContext("Main Window", "Stop Emulation")); | 538 | hotkey_registry.GetShortcutContext(main_window, stop_emulation)); |
| 532 | 539 | ||
| 533 | ui.action_Show_Filter_Bar->setShortcut( | 540 | ui.action_Show_Filter_Bar->setShortcut( |
| 534 | hotkey_registry.GetKeySequence("Main Window", "Toggle Filter Bar")); | 541 | hotkey_registry.GetKeySequence(main_window, toggle_filter_bar)); |
| 535 | ui.action_Show_Filter_Bar->setShortcutContext( | 542 | ui.action_Show_Filter_Bar->setShortcutContext( |
| 536 | hotkey_registry.GetShortcutContext("Main Window", "Toggle Filter Bar")); | 543 | hotkey_registry.GetShortcutContext(main_window, toggle_filter_bar)); |
| 537 | 544 | ||
| 538 | ui.action_Show_Status_Bar->setShortcut( | 545 | ui.action_Show_Status_Bar->setShortcut( |
| 539 | hotkey_registry.GetKeySequence("Main Window", "Toggle Status Bar")); | 546 | hotkey_registry.GetKeySequence(main_window, toggle_status_bar)); |
| 540 | ui.action_Show_Status_Bar->setShortcutContext( | 547 | ui.action_Show_Status_Bar->setShortcutContext( |
| 541 | hotkey_registry.GetShortcutContext("Main Window", "Toggle Status Bar")); | 548 | hotkey_registry.GetShortcutContext(main_window, toggle_status_bar)); |
| 542 | 549 | ||
| 543 | connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated, | 550 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Load File"), this), |
| 544 | this, &GMainWindow::OnMenuLoadFile); | 551 | &QShortcut::activated, this, &GMainWindow::OnMenuLoadFile); |
| 545 | connect(hotkey_registry.GetHotkey("Main Window", "Continue/Pause Emulation", this), | 552 | connect( |
| 546 | &QShortcut::activated, this, [&] { | 553 | hotkey_registry.GetHotkey(main_window, QStringLiteral("Continue/Pause Emulation"), this), |
| 547 | if (emulation_running) { | 554 | &QShortcut::activated, this, [&] { |
| 548 | if (emu_thread->IsRunning()) { | 555 | if (emulation_running) { |
| 549 | OnPauseGame(); | 556 | if (emu_thread->IsRunning()) { |
| 550 | } else { | 557 | OnPauseGame(); |
| 551 | OnStartGame(); | 558 | } else { |
| 552 | } | 559 | OnStartGame(); |
| 553 | } | 560 | } |
| 554 | }); | 561 | } |
| 555 | connect(hotkey_registry.GetHotkey("Main Window", "Restart Emulation", this), | 562 | }); |
| 563 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Restart Emulation"), this), | ||
| 556 | &QShortcut::activated, this, [this] { | 564 | &QShortcut::activated, this, [this] { |
| 557 | if (!Core::System::GetInstance().IsPoweredOn()) | 565 | if (!Core::System::GetInstance().IsPoweredOn()) { |
| 558 | return; | 566 | return; |
| 559 | BootGame(QString(game_path)); | 567 | } |
| 568 | BootGame(game_path); | ||
| 560 | }); | 569 | }); |
| 561 | connect(hotkey_registry.GetHotkey("Main Window", "Fullscreen", render_window), | 570 | connect(hotkey_registry.GetHotkey(main_window, fullscreen, render_window), |
| 562 | &QShortcut::activated, ui.action_Fullscreen, &QAction::trigger); | 571 | &QShortcut::activated, ui.action_Fullscreen, &QAction::trigger); |
| 563 | connect(hotkey_registry.GetHotkey("Main Window", "Fullscreen", render_window), | 572 | connect(hotkey_registry.GetHotkey(main_window, fullscreen, render_window), |
| 564 | &QShortcut::activatedAmbiguously, ui.action_Fullscreen, &QAction::trigger); | 573 | &QShortcut::activatedAmbiguously, ui.action_Fullscreen, &QAction::trigger); |
| 565 | connect(hotkey_registry.GetHotkey("Main Window", "Exit Fullscreen", this), | 574 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Exit Fullscreen"), this), |
| 566 | &QShortcut::activated, this, [&] { | 575 | &QShortcut::activated, this, [&] { |
| 567 | if (emulation_running) { | 576 | if (emulation_running) { |
| 568 | ui.action_Fullscreen->setChecked(false); | 577 | ui.action_Fullscreen->setChecked(false); |
| 569 | ToggleFullscreen(); | 578 | ToggleFullscreen(); |
| 570 | } | 579 | } |
| 571 | }); | 580 | }); |
| 572 | connect(hotkey_registry.GetHotkey("Main Window", "Toggle Speed Limit", this), | 581 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Speed Limit"), this), |
| 573 | &QShortcut::activated, this, [&] { | 582 | &QShortcut::activated, this, [&] { |
| 574 | Settings::values.use_frame_limit = !Settings::values.use_frame_limit; | 583 | Settings::values.use_frame_limit = !Settings::values.use_frame_limit; |
| 575 | UpdateStatusBar(); | 584 | UpdateStatusBar(); |
| @@ -578,33 +587,33 @@ void GMainWindow::InitializeHotkeys() { | |||
| 578 | // MSVC occurs and we make it a requirement (see: | 587 | // MSVC occurs and we make it a requirement (see: |
| 579 | // https://developercommunity.visualstudio.com/content/problem/93922/constexprs-are-trying-to-be-captured-in-lambda-fun.html) | 588 | // https://developercommunity.visualstudio.com/content/problem/93922/constexprs-are-trying-to-be-captured-in-lambda-fun.html) |
| 580 | static constexpr u16 SPEED_LIMIT_STEP = 5; | 589 | static constexpr u16 SPEED_LIMIT_STEP = 5; |
| 581 | connect(hotkey_registry.GetHotkey("Main Window", "Increase Speed Limit", this), | 590 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Increase Speed Limit"), this), |
| 582 | &QShortcut::activated, this, [&] { | 591 | &QShortcut::activated, this, [&] { |
| 583 | if (Settings::values.frame_limit < 9999 - SPEED_LIMIT_STEP) { | 592 | if (Settings::values.frame_limit < 9999 - SPEED_LIMIT_STEP) { |
| 584 | Settings::values.frame_limit += SPEED_LIMIT_STEP; | 593 | Settings::values.frame_limit += SPEED_LIMIT_STEP; |
| 585 | UpdateStatusBar(); | 594 | UpdateStatusBar(); |
| 586 | } | 595 | } |
| 587 | }); | 596 | }); |
| 588 | connect(hotkey_registry.GetHotkey("Main Window", "Decrease Speed Limit", this), | 597 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Decrease Speed Limit"), this), |
| 589 | &QShortcut::activated, this, [&] { | 598 | &QShortcut::activated, this, [&] { |
| 590 | if (Settings::values.frame_limit > SPEED_LIMIT_STEP) { | 599 | if (Settings::values.frame_limit > SPEED_LIMIT_STEP) { |
| 591 | Settings::values.frame_limit -= SPEED_LIMIT_STEP; | 600 | Settings::values.frame_limit -= SPEED_LIMIT_STEP; |
| 592 | UpdateStatusBar(); | 601 | UpdateStatusBar(); |
| 593 | } | 602 | } |
| 594 | }); | 603 | }); |
| 595 | connect(hotkey_registry.GetHotkey("Main Window", "Load Amiibo", this), &QShortcut::activated, | 604 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Load Amiibo"), this), |
| 596 | this, [&] { | 605 | &QShortcut::activated, this, [&] { |
| 597 | if (ui.action_Load_Amiibo->isEnabled()) { | 606 | if (ui.action_Load_Amiibo->isEnabled()) { |
| 598 | OnLoadAmiibo(); | 607 | OnLoadAmiibo(); |
| 599 | } | 608 | } |
| 600 | }); | 609 | }); |
| 601 | connect(hotkey_registry.GetHotkey("Main Window", "Capture Screenshot", this), | 610 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Capture Screenshot"), this), |
| 602 | &QShortcut::activated, this, [&] { | 611 | &QShortcut::activated, this, [&] { |
| 603 | if (emu_thread->IsRunning()) { | 612 | if (emu_thread->IsRunning()) { |
| 604 | OnCaptureScreenshot(); | 613 | OnCaptureScreenshot(); |
| 605 | } | 614 | } |
| 606 | }); | 615 | }); |
| 607 | connect(hotkey_registry.GetHotkey("Main Window", "Change Docked Mode", this), | 616 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Change Docked Mode"), this), |
| 608 | &QShortcut::activated, this, [&] { | 617 | &QShortcut::activated, this, [&] { |
| 609 | Settings::values.use_docked_mode = !Settings::values.use_docked_mode; | 618 | Settings::values.use_docked_mode = !Settings::values.use_docked_mode; |
| 610 | OnDockedModeChanged(!Settings::values.use_docked_mode, | 619 | OnDockedModeChanged(!Settings::values.use_docked_mode, |
| @@ -705,7 +714,9 @@ void GMainWindow::ConnectMenuEvents() { | |||
| 705 | 714 | ||
| 706 | // Fullscreen | 715 | // Fullscreen |
| 707 | ui.action_Fullscreen->setShortcut( | 716 | ui.action_Fullscreen->setShortcut( |
| 708 | hotkey_registry.GetHotkey("Main Window", "Fullscreen", this)->key()); | 717 | hotkey_registry |
| 718 | .GetHotkey(QStringLiteral("Main Window"), QStringLiteral("Fullscreen"), this) | ||
| 719 | ->key()); | ||
| 709 | connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen); | 720 | connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen); |
| 710 | 721 | ||
| 711 | // Movie | 722 | // Movie |
| @@ -742,25 +753,33 @@ void GMainWindow::OnDisplayTitleBars(bool show) { | |||
| 742 | QStringList GMainWindow::GetUnsupportedGLExtensions() { | 753 | QStringList GMainWindow::GetUnsupportedGLExtensions() { |
| 743 | QStringList unsupported_ext; | 754 | QStringList unsupported_ext; |
| 744 | 755 | ||
| 745 | if (!GLAD_GL_ARB_direct_state_access) | 756 | if (!GLAD_GL_ARB_direct_state_access) { |
| 746 | unsupported_ext.append("ARB_direct_state_access"); | 757 | unsupported_ext.append(QStringLiteral("ARB_direct_state_access")); |
| 747 | if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) | 758 | } |
| 748 | unsupported_ext.append("ARB_vertex_type_10f_11f_11f_rev"); | 759 | if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) { |
| 749 | if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) | 760 | unsupported_ext.append(QStringLiteral("ARB_vertex_type_10f_11f_11f_rev")); |
| 750 | unsupported_ext.append("ARB_texture_mirror_clamp_to_edge"); | 761 | } |
| 751 | if (!GLAD_GL_ARB_multi_bind) | 762 | if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) { |
| 752 | unsupported_ext.append("ARB_multi_bind"); | 763 | unsupported_ext.append(QStringLiteral("ARB_texture_mirror_clamp_to_edge")); |
| 764 | } | ||
| 765 | if (!GLAD_GL_ARB_multi_bind) { | ||
| 766 | unsupported_ext.append(QStringLiteral("ARB_multi_bind")); | ||
| 767 | } | ||
| 753 | 768 | ||
| 754 | // Extensions required to support some texture formats. | 769 | // Extensions required to support some texture formats. |
| 755 | if (!GLAD_GL_EXT_texture_compression_s3tc) | 770 | if (!GLAD_GL_EXT_texture_compression_s3tc) { |
| 756 | unsupported_ext.append("EXT_texture_compression_s3tc"); | 771 | unsupported_ext.append(QStringLiteral("EXT_texture_compression_s3tc")); |
| 757 | if (!GLAD_GL_ARB_texture_compression_rgtc) | 772 | } |
| 758 | unsupported_ext.append("ARB_texture_compression_rgtc"); | 773 | if (!GLAD_GL_ARB_texture_compression_rgtc) { |
| 759 | if (!GLAD_GL_ARB_depth_buffer_float) | 774 | unsupported_ext.append(QStringLiteral("ARB_texture_compression_rgtc")); |
| 760 | unsupported_ext.append("ARB_depth_buffer_float"); | 775 | } |
| 761 | 776 | if (!GLAD_GL_ARB_depth_buffer_float) { | |
| 762 | for (const QString& ext : unsupported_ext) | 777 | unsupported_ext.append(QStringLiteral("ARB_depth_buffer_float")); |
| 778 | } | ||
| 779 | |||
| 780 | for (const QString& ext : unsupported_ext) { | ||
| 763 | LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString()); | 781 | LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString()); |
| 782 | } | ||
| 764 | 783 | ||
| 765 | return unsupported_ext; | 784 | return unsupported_ext; |
| 766 | } | 785 | } |
| @@ -782,13 +801,13 @@ bool GMainWindow::LoadROM(const QString& filename) { | |||
| 782 | } | 801 | } |
| 783 | } | 802 | } |
| 784 | 803 | ||
| 785 | QStringList unsupported_gl_extensions = GetUnsupportedGLExtensions(); | 804 | const QStringList unsupported_gl_extensions = GetUnsupportedGLExtensions(); |
| 786 | if (!unsupported_gl_extensions.empty()) { | 805 | if (!unsupported_gl_extensions.empty()) { |
| 787 | QMessageBox::critical(this, tr("Error while initializing OpenGL Core!"), | 806 | QMessageBox::critical(this, tr("Error while initializing OpenGL Core!"), |
| 788 | tr("Your GPU may not support one or more required OpenGL" | 807 | tr("Your GPU may not support one or more required OpenGL" |
| 789 | "extensions. Please ensure you have the latest graphics " | 808 | "extensions. Please ensure you have the latest graphics " |
| 790 | "driver.<br><br>Unsupported extensions:<br>") + | 809 | "driver.<br><br>Unsupported extensions:<br>") + |
| 791 | unsupported_gl_extensions.join("<br>")); | 810 | unsupported_gl_extensions.join(QStringLiteral("<br>"))); |
| 792 | return false; | 811 | return false; |
| 793 | } | 812 | } |
| 794 | 813 | ||
| @@ -936,9 +955,7 @@ void GMainWindow::BootGame(const QString& filename) { | |||
| 936 | title_name = FileUtil::GetFilename(filename.toStdString()); | 955 | title_name = FileUtil::GetFilename(filename.toStdString()); |
| 937 | } | 956 | } |
| 938 | 957 | ||
| 939 | setWindowTitle(QString("yuzu %1| %4 | %2-%3") | 958 | UpdateWindowTitle(QString::fromStdString(title_name)); |
| 940 | .arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc, | ||
| 941 | QString::fromStdString(title_name))); | ||
| 942 | 959 | ||
| 943 | loading_screen->Prepare(Core::System::GetInstance().GetAppLoader()); | 960 | loading_screen->Prepare(Core::System::GetInstance().GetAppLoader()); |
| 944 | loading_screen->show(); | 961 | loading_screen->show(); |
| @@ -979,8 +996,8 @@ void GMainWindow::ShutdownGame() { | |||
| 979 | loading_screen->Clear(); | 996 | loading_screen->Clear(); |
| 980 | game_list->show(); | 997 | game_list->show(); |
| 981 | game_list->setFilterFocus(); | 998 | game_list->setFilterFocus(); |
| 982 | setWindowTitle(QString("yuzu %1| %2-%3") | 999 | |
| 983 | .arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc)); | 1000 | UpdateWindowTitle(); |
| 984 | 1001 | ||
| 985 | // Disable status bar updates | 1002 | // Disable status bar updates |
| 986 | status_bar_update_timer.stop(); | 1003 | status_bar_update_timer.stop(); |
| @@ -1009,7 +1026,7 @@ void GMainWindow::UpdateRecentFiles() { | |||
| 1009 | std::min(UISettings::values.recent_files.size(), max_recent_files_item); | 1026 | std::min(UISettings::values.recent_files.size(), max_recent_files_item); |
| 1010 | 1027 | ||
| 1011 | for (int i = 0; i < num_recent_files; i++) { | 1028 | for (int i = 0; i < num_recent_files; i++) { |
| 1012 | const QString text = QString("&%1. %2").arg(i + 1).arg( | 1029 | const QString text = QStringLiteral("&%1. %2").arg(i + 1).arg( |
| 1013 | QFileInfo(UISettings::values.recent_files[i]).fileName()); | 1030 | QFileInfo(UISettings::values.recent_files[i]).fileName()); |
| 1014 | actions_recent_files[i]->setText(text); | 1031 | actions_recent_files[i]->setText(text); |
| 1015 | actions_recent_files[i]->setData(UISettings::values.recent_files[i]); | 1032 | actions_recent_files[i]->setData(UISettings::values.recent_files[i]); |
| @@ -1031,10 +1048,10 @@ void GMainWindow::OnGameListLoadFile(QString game_path) { | |||
| 1031 | 1048 | ||
| 1032 | void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target) { | 1049 | void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target) { |
| 1033 | std::string path; | 1050 | std::string path; |
| 1034 | std::string open_target; | 1051 | QString open_target; |
| 1035 | switch (target) { | 1052 | switch (target) { |
| 1036 | case GameListOpenTarget::SaveData: { | 1053 | case GameListOpenTarget::SaveData: { |
| 1037 | open_target = "Save Data"; | 1054 | open_target = tr("Save Data"); |
| 1038 | const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir); | 1055 | const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir); |
| 1039 | ASSERT(program_id != 0); | 1056 | ASSERT(program_id != 0); |
| 1040 | 1057 | ||
| @@ -1071,7 +1088,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target | |||
| 1071 | break; | 1088 | break; |
| 1072 | } | 1089 | } |
| 1073 | case GameListOpenTarget::ModData: { | 1090 | case GameListOpenTarget::ModData: { |
| 1074 | open_target = "Mod Data"; | 1091 | open_target = tr("Mod Data"); |
| 1075 | const auto load_dir = FileUtil::GetUserPath(FileUtil::UserPath::LoadDir); | 1092 | const auto load_dir = FileUtil::GetUserPath(FileUtil::UserPath::LoadDir); |
| 1076 | path = fmt::format("{}{:016X}", load_dir, program_id); | 1093 | path = fmt::format("{}{:016X}", load_dir, program_id); |
| 1077 | break; | 1094 | break; |
| @@ -1081,27 +1098,26 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target | |||
| 1081 | } | 1098 | } |
| 1082 | 1099 | ||
| 1083 | const QString qpath = QString::fromStdString(path); | 1100 | const QString qpath = QString::fromStdString(path); |
| 1084 | |||
| 1085 | const QDir dir(qpath); | 1101 | const QDir dir(qpath); |
| 1086 | if (!dir.exists()) { | 1102 | if (!dir.exists()) { |
| 1087 | QMessageBox::warning(this, | 1103 | QMessageBox::warning(this, tr("Error Opening %1 Folder").arg(open_target), |
| 1088 | tr("Error Opening %1 Folder").arg(QString::fromStdString(open_target)), | ||
| 1089 | tr("Folder does not exist!")); | 1104 | tr("Folder does not exist!")); |
| 1090 | return; | 1105 | return; |
| 1091 | } | 1106 | } |
| 1092 | LOG_INFO(Frontend, "Opening {} path for program_id={:016x}", open_target, program_id); | 1107 | LOG_INFO(Frontend, "Opening {} path for program_id={:016x}", open_target.toStdString(), |
| 1108 | program_id); | ||
| 1093 | QDesktopServices::openUrl(QUrl::fromLocalFile(qpath)); | 1109 | QDesktopServices::openUrl(QUrl::fromLocalFile(qpath)); |
| 1094 | } | 1110 | } |
| 1095 | 1111 | ||
| 1096 | void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) { | 1112 | void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) { |
| 1097 | ASSERT(program_id != 0); | 1113 | ASSERT(program_id != 0); |
| 1098 | 1114 | ||
| 1115 | const QString shader_dir = | ||
| 1116 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)); | ||
| 1099 | const QString tranferable_shader_cache_folder_path = | 1117 | const QString tranferable_shader_cache_folder_path = |
| 1100 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)) + "opengl" + | 1118 | shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable"); |
| 1101 | DIR_SEP + "transferable"; | ||
| 1102 | |||
| 1103 | const QString transferable_shader_cache_file_path = | 1119 | const QString transferable_shader_cache_file_path = |
| 1104 | tranferable_shader_cache_folder_path + DIR_SEP + | 1120 | tranferable_shader_cache_folder_path + QDir::separator() + |
| 1105 | QString::fromStdString(fmt::format("{:016X}.bin", program_id)); | 1121 | QString::fromStdString(fmt::format("{:016X}.bin", program_id)); |
| 1106 | 1122 | ||
| 1107 | if (!QFile::exists(transferable_shader_cache_file_path)) { | 1123 | if (!QFile::exists(transferable_shader_cache_file_path)) { |
| @@ -1218,20 +1234,21 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa | |||
| 1218 | return; | 1234 | return; |
| 1219 | } | 1235 | } |
| 1220 | 1236 | ||
| 1221 | bool ok; | 1237 | bool ok = false; |
| 1238 | const QStringList selections{tr("Full"), tr("Skeleton")}; | ||
| 1222 | const auto res = QInputDialog::getItem( | 1239 | const auto res = QInputDialog::getItem( |
| 1223 | this, tr("Select RomFS Dump Mode"), | 1240 | this, tr("Select RomFS Dump Mode"), |
| 1224 | tr("Please select the how you would like the RomFS dumped.<br>Full will copy all of the " | 1241 | tr("Please select the how you would like the RomFS dumped.<br>Full will copy all of the " |
| 1225 | "files into the new directory while <br>skeleton will only create the directory " | 1242 | "files into the new directory while <br>skeleton will only create the directory " |
| 1226 | "structure."), | 1243 | "structure."), |
| 1227 | {"Full", "Skeleton"}, 0, false, &ok); | 1244 | selections, 0, false, &ok); |
| 1228 | if (!ok) { | 1245 | if (!ok) { |
| 1229 | failed(); | 1246 | failed(); |
| 1230 | vfs->DeleteDirectory(path); | 1247 | vfs->DeleteDirectory(path); |
| 1231 | return; | 1248 | return; |
| 1232 | } | 1249 | } |
| 1233 | 1250 | ||
| 1234 | const auto full = res == "Full"; | 1251 | const auto full = res == selections.constFirst(); |
| 1235 | const auto entry_size = CalculateRomFSEntrySize(extracted, full); | 1252 | const auto entry_size = CalculateRomFSEntrySize(extracted, full); |
| 1236 | 1253 | ||
| 1237 | QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0, | 1254 | QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0, |
| @@ -1261,10 +1278,11 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id, | |||
| 1261 | const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); | 1278 | const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); |
| 1262 | 1279 | ||
| 1263 | QString directory; | 1280 | QString directory; |
| 1264 | if (it != compatibility_list.end()) | 1281 | if (it != compatibility_list.end()) { |
| 1265 | directory = it->second.second; | 1282 | directory = it->second.second; |
| 1283 | } | ||
| 1266 | 1284 | ||
| 1267 | QDesktopServices::openUrl(QUrl("https://yuzu-emu.org/game/" + directory)); | 1285 | QDesktopServices::openUrl(QUrl(QStringLiteral("https://yuzu-emu.org/game/") + directory)); |
| 1268 | } | 1286 | } |
| 1269 | 1287 | ||
| 1270 | void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) { | 1288 | void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) { |
| @@ -1295,7 +1313,9 @@ void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) { | |||
| 1295 | 1313 | ||
| 1296 | void GMainWindow::OnMenuLoadFile() { | 1314 | void GMainWindow::OnMenuLoadFile() { |
| 1297 | const QString extensions = | 1315 | const QString extensions = |
| 1298 | QString("*.").append(GameList::supported_file_extensions.join(" *.")).append(" main"); | 1316 | QStringLiteral("*.") |
| 1317 | .append(GameList::supported_file_extensions.join(QStringLiteral(" *."))) | ||
| 1318 | .append(QStringLiteral(" main")); | ||
| 1299 | const QString file_filter = tr("Switch Executable (%1);;All Files (*.*)", | 1319 | const QString file_filter = tr("Switch Executable (%1);;All Files (*.*)", |
| 1300 | "%1 is an identifier for the Switch executable file extensions.") | 1320 | "%1 is an identifier for the Switch executable file extensions.") |
| 1301 | .arg(extensions); | 1321 | .arg(extensions); |
| @@ -1319,9 +1339,9 @@ void GMainWindow::OnMenuLoadFolder() { | |||
| 1319 | } | 1339 | } |
| 1320 | 1340 | ||
| 1321 | const QDir dir{dir_path}; | 1341 | const QDir dir{dir_path}; |
| 1322 | const QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files); | 1342 | const QStringList matching_main = dir.entryList({QStringLiteral("main")}, QDir::Files); |
| 1323 | if (matching_main.size() == 1) { | 1343 | if (matching_main.size() == 1) { |
| 1324 | BootGame(dir.path() + DIR_SEP + matching_main[0]); | 1344 | BootGame(dir.path() + QDir::separator() + matching_main[0]); |
| 1325 | } else { | 1345 | } else { |
| 1326 | QMessageBox::warning(this, tr("Invalid Directory Selected"), | 1346 | QMessageBox::warning(this, tr("Invalid Directory Selected"), |
| 1327 | tr("The directory you have selected does not contain a 'main' file.")); | 1347 | tr("The directory you have selected does not contain a 'main' file.")); |
| @@ -1376,6 +1396,8 @@ void GMainWindow::OnMenuInstallToNAND() { | |||
| 1376 | tr("The file was successfully installed.")); | 1396 | tr("The file was successfully installed.")); |
| 1377 | game_list->PopulateAsync(UISettings::values.game_directory_path, | 1397 | game_list->PopulateAsync(UISettings::values.game_directory_path, |
| 1378 | UISettings::values.game_directory_deepscan); | 1398 | UISettings::values.game_directory_deepscan); |
| 1399 | FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + | ||
| 1400 | DIR_SEP + "game_list"); | ||
| 1379 | }; | 1401 | }; |
| 1380 | 1402 | ||
| 1381 | const auto failed = [this]() { | 1403 | const auto failed = [this]() { |
| @@ -1393,11 +1415,10 @@ void GMainWindow::OnMenuInstallToNAND() { | |||
| 1393 | QMessageBox::Yes; | 1415 | QMessageBox::Yes; |
| 1394 | }; | 1416 | }; |
| 1395 | 1417 | ||
| 1396 | if (filename.endsWith("xci", Qt::CaseInsensitive) || | 1418 | if (filename.endsWith(QStringLiteral("xci"), Qt::CaseInsensitive) || |
| 1397 | filename.endsWith("nsp", Qt::CaseInsensitive)) { | 1419 | filename.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) { |
| 1398 | |||
| 1399 | std::shared_ptr<FileSys::NSP> nsp; | 1420 | std::shared_ptr<FileSys::NSP> nsp; |
| 1400 | if (filename.endsWith("nsp", Qt::CaseInsensitive)) { | 1421 | if (filename.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) { |
| 1401 | nsp = std::make_shared<FileSys::NSP>( | 1422 | nsp = std::make_shared<FileSys::NSP>( |
| 1402 | vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); | 1423 | vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); |
| 1403 | if (nsp->IsExtractedType()) | 1424 | if (nsp->IsExtractedType()) |
| @@ -1692,9 +1713,9 @@ void GMainWindow::OnConfigure() { | |||
| 1692 | } | 1713 | } |
| 1693 | 1714 | ||
| 1694 | void GMainWindow::OnLoadAmiibo() { | 1715 | void GMainWindow::OnLoadAmiibo() { |
| 1695 | const QString extensions{"*.bin"}; | 1716 | const QString extensions{QStringLiteral("*.bin")}; |
| 1696 | const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions); | 1717 | const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions); |
| 1697 | const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), "", file_filter); | 1718 | const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), {}, file_filter); |
| 1698 | 1719 | ||
| 1699 | if (filename.isEmpty()) { | 1720 | if (filename.isEmpty()) { |
| 1700 | return; | 1721 | return; |
| @@ -1756,7 +1777,7 @@ void GMainWindow::OnCaptureScreenshot() { | |||
| 1756 | QFileDialog png_dialog(this, tr("Capture Screenshot"), UISettings::values.screenshot_path, | 1777 | QFileDialog png_dialog(this, tr("Capture Screenshot"), UISettings::values.screenshot_path, |
| 1757 | tr("PNG Image (*.png)")); | 1778 | tr("PNG Image (*.png)")); |
| 1758 | png_dialog.setAcceptMode(QFileDialog::AcceptSave); | 1779 | png_dialog.setAcceptMode(QFileDialog::AcceptSave); |
| 1759 | png_dialog.setDefaultSuffix("png"); | 1780 | png_dialog.setDefaultSuffix(QStringLiteral("png")); |
| 1760 | if (png_dialog.exec()) { | 1781 | if (png_dialog.exec()) { |
| 1761 | const QString path = png_dialog.selectedFiles().first(); | 1782 | const QString path = png_dialog.selectedFiles().first(); |
| 1762 | if (!path.isEmpty()) { | 1783 | if (!path.isEmpty()) { |
| @@ -1767,6 +1788,19 @@ void GMainWindow::OnCaptureScreenshot() { | |||
| 1767 | OnStartGame(); | 1788 | OnStartGame(); |
| 1768 | } | 1789 | } |
| 1769 | 1790 | ||
| 1791 | void GMainWindow::UpdateWindowTitle(const QString& title_name) { | ||
| 1792 | const QString full_name = QString::fromUtf8(Common::g_build_fullname); | ||
| 1793 | const QString branch_name = QString::fromUtf8(Common::g_scm_branch); | ||
| 1794 | const QString description = QString::fromUtf8(Common::g_scm_desc); | ||
| 1795 | |||
| 1796 | if (title_name.isEmpty()) { | ||
| 1797 | setWindowTitle(QStringLiteral("yuzu %1| %2-%3").arg(full_name, branch_name, description)); | ||
| 1798 | } else { | ||
| 1799 | setWindowTitle(QStringLiteral("yuzu %1| %4 | %2-%3") | ||
| 1800 | .arg(full_name, branch_name, description, title_name)); | ||
| 1801 | } | ||
| 1802 | } | ||
| 1803 | |||
| 1770 | void GMainWindow::UpdateStatusBar() { | 1804 | void GMainWindow::UpdateStatusBar() { |
| 1771 | if (emu_thread == nullptr) { | 1805 | if (emu_thread == nullptr) { |
| 1772 | status_bar_update_timer.stop(); | 1806 | status_bar_update_timer.stop(); |
| @@ -1806,17 +1840,17 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det | |||
| 1806 | "data, or other bugs."); | 1840 | "data, or other bugs."); |
| 1807 | switch (result) { | 1841 | switch (result) { |
| 1808 | case Core::System::ResultStatus::ErrorSystemFiles: { | 1842 | case Core::System::ResultStatus::ErrorSystemFiles: { |
| 1809 | QString message = "yuzu was unable to locate a Switch system archive"; | 1843 | QString message = tr("yuzu was unable to locate a Switch system archive"); |
| 1810 | if (!details.empty()) { | 1844 | if (!details.empty()) { |
| 1811 | message.append(tr(": %1. ").arg(details.c_str())); | 1845 | message.append(tr(": %1. ").arg(QString::fromStdString(details))); |
| 1812 | } else { | 1846 | } else { |
| 1813 | message.append(". "); | 1847 | message.append(tr(". ")); |
| 1814 | } | 1848 | } |
| 1815 | message.append(common_message); | 1849 | message.append(common_message); |
| 1816 | 1850 | ||
| 1817 | answer = QMessageBox::question(this, tr("System Archive Not Found"), message, | 1851 | answer = QMessageBox::question(this, tr("System Archive Not Found"), message, |
| 1818 | QMessageBox::Yes | QMessageBox::No, QMessageBox::No); | 1852 | QMessageBox::Yes | QMessageBox::No, QMessageBox::No); |
| 1819 | status_message = "System Archive Missing"; | 1853 | status_message = tr("System Archive Missing"); |
| 1820 | break; | 1854 | break; |
| 1821 | } | 1855 | } |
| 1822 | 1856 | ||
| @@ -1825,7 +1859,7 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det | |||
| 1825 | message.append(common_message); | 1859 | message.append(common_message); |
| 1826 | answer = QMessageBox::question(this, tr("Shared Fonts Not Found"), message, | 1860 | answer = QMessageBox::question(this, tr("Shared Fonts Not Found"), message, |
| 1827 | QMessageBox::Yes | QMessageBox::No, QMessageBox::No); | 1861 | QMessageBox::Yes | QMessageBox::No, QMessageBox::No); |
| 1828 | status_message = "Shared Font Missing"; | 1862 | status_message = tr("Shared Font Missing"); |
| 1829 | break; | 1863 | break; |
| 1830 | } | 1864 | } |
| 1831 | 1865 | ||
| @@ -1841,7 +1875,7 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det | |||
| 1841 | "Continuing emulation may result in crashes, corrupted save data, or other " | 1875 | "Continuing emulation may result in crashes, corrupted save data, or other " |
| 1842 | "bugs."), | 1876 | "bugs."), |
| 1843 | QMessageBox::Yes | QMessageBox::No, QMessageBox::No); | 1877 | QMessageBox::Yes | QMessageBox::No, QMessageBox::No); |
| 1844 | status_message = "Fatal Error encountered"; | 1878 | status_message = tr("Fatal Error encountered"); |
| 1845 | break; | 1879 | break; |
| 1846 | } | 1880 | } |
| 1847 | 1881 | ||
| @@ -1892,18 +1926,19 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { | |||
| 1892 | }; | 1926 | }; |
| 1893 | 1927 | ||
| 1894 | QString errors; | 1928 | QString errors; |
| 1895 | 1929 | if (!pdm.HasFuses()) { | |
| 1896 | if (!pdm.HasFuses()) | ||
| 1897 | errors += tr("- Missing fuses - Cannot derive SBK\n"); | 1930 | errors += tr("- Missing fuses - Cannot derive SBK\n"); |
| 1898 | if (!pdm.HasBoot0()) | 1931 | } |
| 1932 | if (!pdm.HasBoot0()) { | ||
| 1899 | errors += tr("- Missing BOOT0 - Cannot derive master keys\n"); | 1933 | errors += tr("- Missing BOOT0 - Cannot derive master keys\n"); |
| 1900 | if (!pdm.HasPackage2()) | 1934 | } |
| 1935 | if (!pdm.HasPackage2()) { | ||
| 1901 | errors += tr("- Missing BCPKG2-1-Normal-Main - Cannot derive general keys\n"); | 1936 | errors += tr("- Missing BCPKG2-1-Normal-Main - Cannot derive general keys\n"); |
| 1902 | if (!pdm.HasProdInfo()) | 1937 | } |
| 1938 | if (!pdm.HasProdInfo()) { | ||
| 1903 | errors += tr("- Missing PRODINFO - Cannot derive title keys\n"); | 1939 | errors += tr("- Missing PRODINFO - Cannot derive title keys\n"); |
| 1904 | 1940 | } | |
| 1905 | if (!errors.isEmpty()) { | 1941 | if (!errors.isEmpty()) { |
| 1906 | |||
| 1907 | QMessageBox::warning( | 1942 | QMessageBox::warning( |
| 1908 | this, tr("Warning Missing Derivation Components"), | 1943 | this, tr("Warning Missing Derivation Components"), |
| 1909 | tr("The following are missing from your configuration that may hinder key " | 1944 | tr("The following are missing from your configuration that may hinder key " |
| @@ -1953,13 +1988,15 @@ std::optional<u64> GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProv | |||
| 1953 | 1988 | ||
| 1954 | std::vector<u64> romfs_tids; | 1989 | std::vector<u64> romfs_tids; |
| 1955 | romfs_tids.push_back(program_id); | 1990 | romfs_tids.push_back(program_id); |
| 1956 | for (const auto& entry : dlc_match) | 1991 | for (const auto& entry : dlc_match) { |
| 1957 | romfs_tids.push_back(entry.title_id); | 1992 | romfs_tids.push_back(entry.title_id); |
| 1993 | } | ||
| 1958 | 1994 | ||
| 1959 | if (romfs_tids.size() > 1) { | 1995 | if (romfs_tids.size() > 1) { |
| 1960 | QStringList list{"Base"}; | 1996 | QStringList list{QStringLiteral("Base")}; |
| 1961 | for (std::size_t i = 1; i < romfs_tids.size(); ++i) | 1997 | for (std::size_t i = 1; i < romfs_tids.size(); ++i) { |
| 1962 | list.push_back(QStringLiteral("DLC %1").arg(romfs_tids[i] & 0x7FF)); | 1998 | list.push_back(QStringLiteral("DLC %1").arg(romfs_tids[i] & 0x7FF)); |
| 1999 | } | ||
| 1963 | 2000 | ||
| 1964 | bool ok; | 2001 | bool ok; |
| 1965 | const auto res = QInputDialog::getItem( | 2002 | const auto res = QInputDialog::getItem( |
| @@ -2071,26 +2108,32 @@ void GMainWindow::filterBarSetChecked(bool state) { | |||
| 2071 | } | 2108 | } |
| 2072 | 2109 | ||
| 2073 | void GMainWindow::UpdateUITheme() { | 2110 | void GMainWindow::UpdateUITheme() { |
| 2111 | const QString default_icons = QStringLiteral(":/icons/default"); | ||
| 2112 | const QString& current_theme = UISettings::values.theme; | ||
| 2113 | const bool is_default_theme = current_theme == QString::fromUtf8(UISettings::themes[0].second); | ||
| 2074 | QStringList theme_paths(default_theme_paths); | 2114 | QStringList theme_paths(default_theme_paths); |
| 2075 | if (UISettings::values.theme != UISettings::themes[0].second && | 2115 | |
| 2076 | !UISettings::values.theme.isEmpty()) { | 2116 | if (is_default_theme || current_theme.isEmpty()) { |
| 2077 | const QString theme_uri(":" + UISettings::values.theme + "/style.qss"); | 2117 | qApp->setStyleSheet({}); |
| 2118 | setStyleSheet({}); | ||
| 2119 | theme_paths.append(default_icons); | ||
| 2120 | QIcon::setThemeName(default_icons); | ||
| 2121 | } else { | ||
| 2122 | const QString theme_uri(QLatin1Char{':'} + current_theme + QStringLiteral("/style.qss")); | ||
| 2078 | QFile f(theme_uri); | 2123 | QFile f(theme_uri); |
| 2079 | if (f.open(QFile::ReadOnly | QFile::Text)) { | 2124 | if (f.open(QFile::ReadOnly | QFile::Text)) { |
| 2080 | QTextStream ts(&f); | 2125 | QTextStream ts(&f); |
| 2081 | qApp->setStyleSheet(ts.readAll()); | 2126 | qApp->setStyleSheet(ts.readAll()); |
| 2082 | GMainWindow::setStyleSheet(ts.readAll()); | 2127 | setStyleSheet(ts.readAll()); |
| 2083 | } else { | 2128 | } else { |
| 2084 | LOG_ERROR(Frontend, "Unable to set style, stylesheet file not found"); | 2129 | LOG_ERROR(Frontend, "Unable to set style, stylesheet file not found"); |
| 2085 | } | 2130 | } |
| 2086 | theme_paths.append(QStringList{":/icons/default", ":/icons/" + UISettings::values.theme}); | 2131 | |
| 2087 | QIcon::setThemeName(":/icons/" + UISettings::values.theme); | 2132 | const QString theme_name = QStringLiteral(":/icons/") + current_theme; |
| 2088 | } else { | 2133 | theme_paths.append({default_icons, theme_name}); |
| 2089 | qApp->setStyleSheet(""); | 2134 | QIcon::setThemeName(theme_name); |
| 2090 | GMainWindow::setStyleSheet(""); | ||
| 2091 | theme_paths.append(QStringList{":/icons/default"}); | ||
| 2092 | QIcon::setThemeName(":/icons/default"); | ||
| 2093 | } | 2135 | } |
| 2136 | |||
| 2094 | QIcon::setThemeSearchPaths(theme_paths); | 2137 | QIcon::setThemeSearchPaths(theme_paths); |
| 2095 | emit UpdateThemedIcons(); | 2138 | emit UpdateThemedIcons(); |
| 2096 | } | 2139 | } |
| @@ -2118,8 +2161,8 @@ int main(int argc, char* argv[]) { | |||
| 2118 | SCOPE_EXIT({ MicroProfileShutdown(); }); | 2161 | SCOPE_EXIT({ MicroProfileShutdown(); }); |
| 2119 | 2162 | ||
| 2120 | // Init settings params | 2163 | // Init settings params |
| 2121 | QCoreApplication::setOrganizationName("yuzu team"); | 2164 | QCoreApplication::setOrganizationName(QStringLiteral("yuzu team")); |
| 2122 | QCoreApplication::setApplicationName("yuzu"); | 2165 | QCoreApplication::setApplicationName(QStringLiteral("yuzu")); |
| 2123 | 2166 | ||
| 2124 | // Enables the core to make the qt created contexts current on std::threads | 2167 | // Enables the core to make the qt created contexts current on std::threads |
| 2125 | QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); | 2168 | QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index c4566ed2c..1137bbc7a 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -209,6 +209,7 @@ private slots: | |||
| 209 | 209 | ||
| 210 | private: | 210 | private: |
| 211 | std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); | 211 | std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); |
| 212 | void UpdateWindowTitle(const QString& title_name = {}); | ||
| 212 | void UpdateStatusBar(); | 213 | void UpdateStatusBar(); |
| 213 | 214 | ||
| 214 | Ui::MainWindow ui; | 215 | Ui::MainWindow ui; |
diff --git a/src/yuzu/ui_settings.h b/src/yuzu/ui_settings.h index dbd318e20..a62cd6911 100644 --- a/src/yuzu/ui_settings.h +++ b/src/yuzu/ui_settings.h | |||
| @@ -79,6 +79,7 @@ struct Values { | |||
| 79 | uint8_t row_1_text_id; | 79 | uint8_t row_1_text_id; |
| 80 | uint8_t row_2_text_id; | 80 | uint8_t row_2_text_id; |
| 81 | std::atomic_bool is_game_list_reload_pending{false}; | 81 | std::atomic_bool is_game_list_reload_pending{false}; |
| 82 | bool cache_game_list; | ||
| 82 | }; | 83 | }; |
| 83 | 84 | ||
| 84 | extern Values values; | 85 | extern Values values; |
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 @@ | |||
| 9 | 9 | ||
| 10 | SequenceDialog::SequenceDialog(QWidget* parent) : QDialog(parent) { | 10 | SequenceDialog::SequenceDialog(QWidget* parent) : QDialog(parent) { |
| 11 | setWindowTitle(tr("Enter a hotkey")); | 11 | setWindowTitle(tr("Enter a hotkey")); |
| 12 | auto* layout = new QVBoxLayout(this); | 12 | setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); |
| 13 | |||
| 13 | key_sequence = new QKeySequenceEdit; | 14 | key_sequence = new QKeySequenceEdit; |
| 14 | layout->addWidget(key_sequence); | 15 | |
| 15 | auto* buttons = | 16 | auto* const buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); |
| 16 | new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); | ||
| 17 | buttons->setCenterButtons(true); | 17 | buttons->setCenterButtons(true); |
| 18 | |||
| 19 | auto* const layout = new QVBoxLayout(this); | ||
| 20 | layout->addWidget(key_sequence); | ||
| 18 | layout->addWidget(buttons); | 21 | layout->addWidget(buttons); |
| 22 | |||
| 19 | connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); | 23 | connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); |
| 20 | connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); | 24 | connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); |
| 21 | setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); | ||
| 22 | } | 25 | } |
| 23 | 26 | ||
| 24 | SequenceDialog::~SequenceDialog() = default; | 27 | SequenceDialog::~SequenceDialog() = default; |
diff --git a/src/yuzu/util/spinbox.cpp b/src/yuzu/util/spinbox.cpp deleted file mode 100644 index 14ef1e884..000000000 --- a/src/yuzu/util/spinbox.cpp +++ /dev/null | |||
| @@ -1,278 +0,0 @@ | |||
| 1 | // Licensed under GPLv2 or any later version | ||
| 2 | // Refer to the license.txt file included. | ||
| 3 | |||
| 4 | // Copyright 2014 Tony Wasserka | ||
| 5 | // All rights reserved. | ||
| 6 | // | ||
| 7 | // Redistribution and use in source and binary forms, with or without | ||
| 8 | // modification, are permitted provided that the following conditions are met: | ||
| 9 | // | ||
| 10 | // * Redistributions of source code must retain the above copyright | ||
| 11 | // notice, this list of conditions and the following disclaimer. | ||
| 12 | // * Redistributions in binary form must reproduce the above copyright | ||
| 13 | // notice, this list of conditions and the following disclaimer in the | ||
| 14 | // documentation and/or other materials provided with the distribution. | ||
| 15 | // * Neither the name of the owner nor the names of its contributors may | ||
| 16 | // be used to endorse or promote products derived from this software | ||
| 17 | // without specific prior written permission. | ||
| 18 | // | ||
| 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 30 | |||
| 31 | #include <cstdlib> | ||
| 32 | #include <QLineEdit> | ||
| 33 | #include <QRegExpValidator> | ||
| 34 | #include "common/assert.h" | ||
| 35 | #include "yuzu/util/spinbox.h" | ||
| 36 | |||
| 37 | CSpinBox::CSpinBox(QWidget* parent) | ||
| 38 | : QAbstractSpinBox(parent), min_value(-100), max_value(100), value(0), base(10), num_digits(0) { | ||
| 39 | // TODO: Might be nice to not immediately call the slot. | ||
| 40 | // Think of an address that is being replaced by a different one, in which case a lot | ||
| 41 | // invalid intermediate addresses would be read from during editing. | ||
| 42 | connect(lineEdit(), &QLineEdit::textEdited, this, &CSpinBox::OnEditingFinished); | ||
| 43 | |||
| 44 | UpdateText(); | ||
| 45 | } | ||
| 46 | |||
| 47 | void CSpinBox::SetValue(qint64 val) { | ||
| 48 | auto old_value = value; | ||
| 49 | value = std::max(std::min(val, max_value), min_value); | ||
| 50 | |||
| 51 | if (old_value != value) { | ||
| 52 | UpdateText(); | ||
| 53 | emit ValueChanged(value); | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | void CSpinBox::SetRange(qint64 min, qint64 max) { | ||
| 58 | min_value = min; | ||
| 59 | max_value = max; | ||
| 60 | |||
| 61 | SetValue(value); | ||
| 62 | UpdateText(); | ||
| 63 | } | ||
| 64 | |||
| 65 | void CSpinBox::stepBy(int steps) { | ||
| 66 | auto new_value = value; | ||
| 67 | // Scale number of steps by the currently selected digit | ||
| 68 | // TODO: Move this code elsewhere and enable it. | ||
| 69 | // TODO: Support for num_digits==0, too | ||
| 70 | // TODO: Support base!=16, too | ||
| 71 | // TODO: Make the cursor not jump back to the end of the line... | ||
| 72 | /*if (base == 16 && num_digits > 0) { | ||
| 73 | int digit = num_digits - (lineEdit()->cursorPosition() - prefix.length()) - 1; | ||
| 74 | digit = std::max(0, std::min(digit, num_digits - 1)); | ||
| 75 | steps <<= digit * 4; | ||
| 76 | }*/ | ||
| 77 | |||
| 78 | // Increment "new_value" by "steps", and perform annoying overflow checks, too. | ||
| 79 | if (steps < 0 && new_value + steps > new_value) { | ||
| 80 | new_value = std::numeric_limits<qint64>::min(); | ||
| 81 | } else if (steps > 0 && new_value + steps < new_value) { | ||
| 82 | new_value = std::numeric_limits<qint64>::max(); | ||
| 83 | } else { | ||
| 84 | new_value += steps; | ||
| 85 | } | ||
| 86 | |||
| 87 | SetValue(new_value); | ||
| 88 | UpdateText(); | ||
| 89 | } | ||
| 90 | |||
| 91 | QAbstractSpinBox::StepEnabled CSpinBox::stepEnabled() const { | ||
| 92 | StepEnabled ret = StepNone; | ||
| 93 | |||
| 94 | if (value > min_value) | ||
| 95 | ret |= StepDownEnabled; | ||
| 96 | |||
| 97 | if (value < max_value) | ||
| 98 | ret |= StepUpEnabled; | ||
| 99 | |||
| 100 | return ret; | ||
| 101 | } | ||
| 102 | |||
| 103 | void CSpinBox::SetBase(int base) { | ||
| 104 | this->base = base; | ||
| 105 | |||
| 106 | UpdateText(); | ||
| 107 | } | ||
| 108 | |||
| 109 | void CSpinBox::SetNumDigits(int num_digits) { | ||
| 110 | this->num_digits = num_digits; | ||
| 111 | |||
| 112 | UpdateText(); | ||
| 113 | } | ||
| 114 | |||
| 115 | void CSpinBox::SetPrefix(const QString& prefix) { | ||
| 116 | this->prefix = prefix; | ||
| 117 | |||
| 118 | UpdateText(); | ||
| 119 | } | ||
| 120 | |||
| 121 | void CSpinBox::SetSuffix(const QString& suffix) { | ||
| 122 | this->suffix = suffix; | ||
| 123 | |||
| 124 | UpdateText(); | ||
| 125 | } | ||
| 126 | |||
| 127 | static QString StringToInputMask(const QString& input) { | ||
| 128 | QString mask = input; | ||
| 129 | |||
| 130 | // ... replace any special characters by their escaped counterparts ... | ||
| 131 | mask.replace("\\", "\\\\"); | ||
| 132 | mask.replace("A", "\\A"); | ||
| 133 | mask.replace("a", "\\a"); | ||
| 134 | mask.replace("N", "\\N"); | ||
| 135 | mask.replace("n", "\\n"); | ||
| 136 | mask.replace("X", "\\X"); | ||
| 137 | mask.replace("x", "\\x"); | ||
| 138 | mask.replace("9", "\\9"); | ||
| 139 | mask.replace("0", "\\0"); | ||
| 140 | mask.replace("D", "\\D"); | ||
| 141 | mask.replace("d", "\\d"); | ||
| 142 | mask.replace("#", "\\#"); | ||
| 143 | mask.replace("H", "\\H"); | ||
| 144 | mask.replace("h", "\\h"); | ||
| 145 | mask.replace("B", "\\B"); | ||
| 146 | mask.replace("b", "\\b"); | ||
| 147 | mask.replace(">", "\\>"); | ||
| 148 | mask.replace("<", "\\<"); | ||
| 149 | mask.replace("!", "\\!"); | ||
| 150 | |||
| 151 | return mask; | ||
| 152 | } | ||
| 153 | |||
| 154 | void CSpinBox::UpdateText() { | ||
| 155 | // If a fixed number of digits is used, we put the line edit in insertion mode by setting an | ||
| 156 | // input mask. | ||
| 157 | QString mask; | ||
| 158 | if (num_digits != 0) { | ||
| 159 | mask += StringToInputMask(prefix); | ||
| 160 | |||
| 161 | // For base 10 and negative range, demand a single sign character | ||
| 162 | if (HasSign()) | ||
| 163 | mask += "X"; // identified as "-" or "+" in the validator | ||
| 164 | |||
| 165 | // Uppercase digits greater than 9. | ||
| 166 | mask += ">"; | ||
| 167 | |||
| 168 | // Match num_digits digits | ||
| 169 | // Digits irrelevant to the chosen number base are filtered in the validator | ||
| 170 | mask += QString("H").repeated(std::max(num_digits, 1)); | ||
| 171 | |||
| 172 | // Switch off case conversion | ||
| 173 | mask += "!"; | ||
| 174 | |||
| 175 | mask += StringToInputMask(suffix); | ||
| 176 | } | ||
| 177 | lineEdit()->setInputMask(mask); | ||
| 178 | |||
| 179 | // Set new text without changing the cursor position. This will cause the cursor to briefly | ||
| 180 | // appear at the end of the line and then to jump back to its original position. That's | ||
| 181 | // a bit ugly, but better than having setText() move the cursor permanently all the time. | ||
| 182 | int cursor_position = lineEdit()->cursorPosition(); | ||
| 183 | lineEdit()->setText(TextFromValue()); | ||
| 184 | lineEdit()->setCursorPosition(cursor_position); | ||
| 185 | } | ||
| 186 | |||
| 187 | QString CSpinBox::TextFromValue() { | ||
| 188 | return prefix + QString(HasSign() ? ((value < 0) ? "-" : "+") : "") + | ||
| 189 | QString("%1").arg(std::abs(value), num_digits, base, QLatin1Char('0')).toUpper() + | ||
| 190 | suffix; | ||
| 191 | } | ||
| 192 | |||
| 193 | qint64 CSpinBox::ValueFromText() { | ||
| 194 | unsigned strpos = prefix.length(); | ||
| 195 | |||
| 196 | QString num_string = text().mid(strpos, text().length() - strpos - suffix.length()); | ||
| 197 | return num_string.toLongLong(nullptr, base); | ||
| 198 | } | ||
| 199 | |||
| 200 | bool CSpinBox::HasSign() const { | ||
| 201 | return base == 10 && min_value < 0; | ||
| 202 | } | ||
| 203 | |||
| 204 | void CSpinBox::OnEditingFinished() { | ||
| 205 | // Only update for valid input | ||
| 206 | QString input = lineEdit()->text(); | ||
| 207 | int pos = 0; | ||
| 208 | if (QValidator::Acceptable == validate(input, pos)) | ||
| 209 | SetValue(ValueFromText()); | ||
| 210 | } | ||
| 211 | |||
| 212 | QValidator::State CSpinBox::validate(QString& input, int& pos) const { | ||
| 213 | if (!prefix.isEmpty() && input.left(prefix.length()) != prefix) | ||
| 214 | return QValidator::Invalid; | ||
| 215 | |||
| 216 | int strpos = prefix.length(); | ||
| 217 | |||
| 218 | // Empty "numbers" allowed as intermediate values | ||
| 219 | if (strpos >= input.length() - HasSign() - suffix.length()) | ||
| 220 | return QValidator::Intermediate; | ||
| 221 | |||
| 222 | DEBUG_ASSERT(base <= 10 || base == 16); | ||
| 223 | QString regexp; | ||
| 224 | |||
| 225 | // Demand sign character for negative ranges | ||
| 226 | if (HasSign()) | ||
| 227 | regexp += "[+\\-]"; | ||
| 228 | |||
| 229 | // Match digits corresponding to the chosen number base. | ||
| 230 | regexp += QString("[0-%1").arg(std::min(base, 9)); | ||
| 231 | if (base == 16) { | ||
| 232 | regexp += "a-fA-F"; | ||
| 233 | } | ||
| 234 | regexp += "]"; | ||
| 235 | |||
| 236 | // Specify number of digits | ||
| 237 | if (num_digits > 0) { | ||
| 238 | regexp += QString("{%1}").arg(num_digits); | ||
| 239 | } else { | ||
| 240 | regexp += "+"; | ||
| 241 | } | ||
| 242 | |||
| 243 | // Match string | ||
| 244 | QRegExp num_regexp(regexp); | ||
| 245 | int num_pos = strpos; | ||
| 246 | QString sub_input = input.mid(strpos, input.length() - strpos - suffix.length()); | ||
| 247 | |||
| 248 | if (!num_regexp.exactMatch(sub_input) && num_regexp.matchedLength() == 0) | ||
| 249 | return QValidator::Invalid; | ||
| 250 | |||
| 251 | sub_input = sub_input.left(num_regexp.matchedLength()); | ||
| 252 | bool ok; | ||
| 253 | qint64 val = sub_input.toLongLong(&ok, base); | ||
| 254 | |||
| 255 | if (!ok) | ||
| 256 | return QValidator::Invalid; | ||
| 257 | |||
| 258 | // Outside boundaries => don't accept | ||
| 259 | if (val < min_value || val > max_value) | ||
| 260 | return QValidator::Invalid; | ||
| 261 | |||
| 262 | // Make sure we are actually at the end of this string... | ||
| 263 | strpos += num_regexp.matchedLength(); | ||
| 264 | |||
| 265 | if (!suffix.isEmpty() && input.mid(strpos) != suffix) { | ||
| 266 | return QValidator::Invalid; | ||
| 267 | } else { | ||
| 268 | strpos += suffix.length(); | ||
| 269 | } | ||
| 270 | |||
| 271 | if (strpos != input.length()) | ||
| 272 | return QValidator::Invalid; | ||
| 273 | |||
| 274 | // At this point we can say for sure that the input is fine. Let's fix it up a bit though | ||
| 275 | input.replace(num_pos, sub_input.length(), sub_input.toUpper()); | ||
| 276 | |||
| 277 | return QValidator::Acceptable; | ||
| 278 | } | ||
diff --git a/src/yuzu/util/spinbox.h b/src/yuzu/util/spinbox.h deleted file mode 100644 index 2fa1db3a4..000000000 --- a/src/yuzu/util/spinbox.h +++ /dev/null | |||
| @@ -1,86 +0,0 @@ | |||
| 1 | // Licensed under GPLv2 or any later version | ||
| 2 | // Refer to the license.txt file included. | ||
| 3 | |||
| 4 | // Copyright 2014 Tony Wasserka | ||
| 5 | // All rights reserved. | ||
| 6 | // | ||
| 7 | // Redistribution and use in source and binary forms, with or without | ||
| 8 | // modification, are permitted provided that the following conditions are met: | ||
| 9 | // | ||
| 10 | // * Redistributions of source code must retain the above copyright | ||
| 11 | // notice, this list of conditions and the following disclaimer. | ||
| 12 | // * Redistributions in binary form must reproduce the above copyright | ||
| 13 | // notice, this list of conditions and the following disclaimer in the | ||
| 14 | // documentation and/or other materials provided with the distribution. | ||
| 15 | // * Neither the name of the owner nor the names of its contributors may | ||
| 16 | // be used to endorse or promote products derived from this software | ||
| 17 | // without specific prior written permission. | ||
| 18 | // | ||
| 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 30 | |||
| 31 | #pragma once | ||
| 32 | |||
| 33 | #include <QAbstractSpinBox> | ||
| 34 | #include <QtGlobal> | ||
| 35 | |||
| 36 | class QVariant; | ||
| 37 | |||
| 38 | /** | ||
| 39 | * A custom spin box widget with enhanced functionality over Qt's QSpinBox | ||
| 40 | */ | ||
| 41 | class CSpinBox : public QAbstractSpinBox { | ||
| 42 | Q_OBJECT | ||
| 43 | |||
| 44 | public: | ||
| 45 | explicit CSpinBox(QWidget* parent = nullptr); | ||
| 46 | |||
| 47 | void stepBy(int steps) override; | ||
| 48 | StepEnabled stepEnabled() const override; | ||
| 49 | |||
| 50 | void SetValue(qint64 val); | ||
| 51 | |||
| 52 | void SetRange(qint64 min, qint64 max); | ||
| 53 | |||
| 54 | void SetBase(int base); | ||
| 55 | |||
| 56 | void SetPrefix(const QString& prefix); | ||
| 57 | void SetSuffix(const QString& suffix); | ||
| 58 | |||
| 59 | void SetNumDigits(int num_digits); | ||
| 60 | |||
| 61 | QValidator::State validate(QString& input, int& pos) const override; | ||
| 62 | |||
| 63 | signals: | ||
| 64 | void ValueChanged(qint64 val); | ||
| 65 | |||
| 66 | private slots: | ||
| 67 | void OnEditingFinished(); | ||
| 68 | |||
| 69 | private: | ||
| 70 | void UpdateText(); | ||
| 71 | |||
| 72 | bool HasSign() const; | ||
| 73 | |||
| 74 | QString TextFromValue(); | ||
| 75 | qint64 ValueFromText(); | ||
| 76 | |||
| 77 | qint64 min_value, max_value; | ||
| 78 | |||
| 79 | qint64 value; | ||
| 80 | |||
| 81 | QString prefix, suffix; | ||
| 82 | |||
| 83 | int base; | ||
| 84 | |||
| 85 | int num_digits; | ||
| 86 | }; | ||
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 @@ | |||
| 8 | #include "yuzu/util/util.h" | 8 | #include "yuzu/util/util.h" |
| 9 | 9 | ||
| 10 | QFont GetMonospaceFont() { | 10 | QFont GetMonospaceFont() { |
| 11 | QFont font("monospace"); | 11 | QFont font(QStringLiteral("monospace")); |
| 12 | // Automatic fallback to a monospace font on on platforms without a font called "monospace" | 12 | // Automatic fallback to a monospace font on on platforms without a font called "monospace" |
| 13 | font.setStyleHint(QFont::Monospace); | 13 | font.setStyleHint(QFont::Monospace); |
| 14 | font.setFixedPitch(true); | 14 | font.setFixedPitch(true); |
| @@ -16,14 +16,16 @@ QFont GetMonospaceFont() { | |||
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | QString ReadableByteSize(qulonglong size) { | 18 | QString ReadableByteSize(qulonglong size) { |
| 19 | static const std::array<const char*, 6> units = {"B", "KiB", "MiB", "GiB", "TiB", "PiB"}; | 19 | static constexpr std::array units{"B", "KiB", "MiB", "GiB", "TiB", "PiB"}; |
| 20 | if (size == 0) | 20 | if (size == 0) { |
| 21 | return "0"; | 21 | return QStringLiteral("0"); |
| 22 | int digit_groups = std::min<int>(static_cast<int>(std::log10(size) / std::log10(1024)), | 22 | } |
| 23 | static_cast<int>(units.size())); | 23 | |
| 24 | return QString("%L1 %2") | 24 | const int digit_groups = std::min(static_cast<int>(std::log10(size) / std::log10(1024)), |
| 25 | static_cast<int>(units.size())); | ||
| 26 | return QStringLiteral("%L1 %2") | ||
| 25 | .arg(size / std::pow(1024, digit_groups), 0, 'f', 1) | 27 | .arg(size / std::pow(1024, digit_groups), 0, 'f', 1) |
| 26 | .arg(units[digit_groups]); | 28 | .arg(QString::fromUtf8(units[digit_groups])); |
| 27 | } | 29 | } |
| 28 | 30 | ||
| 29 | QPixmap CreateCirclePixmapFromColor(const QColor& color) { | 31 | 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 | |||
| 4 | config.cpp | 4 | config.cpp |
| 5 | config.h | 5 | config.h |
| 6 | default_ini.h | 6 | default_ini.h |
| 7 | emu_window/emu_window_sdl2_gl.cpp | ||
| 8 | emu_window/emu_window_sdl2_gl.h | ||
| 7 | emu_window/emu_window_sdl2.cpp | 9 | emu_window/emu_window_sdl2.cpp |
| 8 | emu_window/emu_window_sdl2.h | 10 | emu_window/emu_window_sdl2.h |
| 9 | resource.h | 11 | 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() { | |||
| 26 | Config::~Config() = default; | 26 | Config::~Config() = default; |
| 27 | 27 | ||
| 28 | bool Config::LoadINI(const std::string& default_contents, bool retry) { | 28 | bool Config::LoadINI(const std::string& default_contents, bool retry) { |
| 29 | const char* location = this->sdl2_config_loc.c_str(); | 29 | const std::string& location = this->sdl2_config_loc; |
| 30 | if (sdl2_config->ParseError() < 0) { | 30 | if (sdl2_config->ParseError() < 0) { |
| 31 | if (retry) { | 31 | if (retry) { |
| 32 | LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", location); | 32 | LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", location); |
| 33 | FileUtil::CreateFullPath(location); | 33 | FileUtil::CreateFullPath(location); |
| 34 | FileUtil::WriteStringToFile(true, default_contents, location); | 34 | FileUtil::WriteStringToFile(true, location, default_contents); |
| 35 | sdl2_config = std::make_unique<INIReader>(location); // Reopen file | 35 | sdl2_config = std::make_unique<INIReader>(location); // Reopen file |
| 36 | 36 | ||
| 37 | return LoadINI(default_contents, false); | 37 | 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 68a176032..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 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | ||
| 6 | #include <cstdlib> | ||
| 7 | #include <string> | ||
| 8 | #define SDL_MAIN_HANDLED | ||
| 9 | #include <SDL.h> | 5 | #include <SDL.h> |
| 10 | #include <fmt/format.h> | ||
| 11 | #include <glad/glad.h> | ||
| 12 | #include "common/logging/log.h" | 6 | #include "common/logging/log.h" |
| 13 | #include "common/scm_rev.h" | ||
| 14 | #include "common/string_util.h" | ||
| 15 | #include "core/settings.h" | ||
| 16 | #include "input_common/keyboard.h" | 7 | #include "input_common/keyboard.h" |
| 17 | #include "input_common/main.h" | 8 | #include "input_common/main.h" |
| 18 | #include "input_common/motion_emu.h" | 9 | #include "input_common/motion_emu.h" |
| 19 | #include "input_common/sdl/sdl.h" | 10 | #include "input_common/sdl/sdl.h" |
| 20 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" | 11 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" |
| 21 | 12 | ||
| 22 | class SDLGLContext : public Core::Frontend::GraphicsContext { | 13 | EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { |
| 23 | public: | 14 | if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { |
| 24 | explicit SDLGLContext() { | 15 | LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); |
| 25 | // create a hidden window to make the shared context against | 16 | exit(1); |
| 26 | window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, // x position | ||
| 27 | SDL_WINDOWPOS_UNDEFINED, // y position | ||
| 28 | Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, | ||
| 29 | SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); | ||
| 30 | context = SDL_GL_CreateContext(window); | ||
| 31 | } | ||
| 32 | |||
| 33 | ~SDLGLContext() { | ||
| 34 | SDL_GL_DeleteContext(context); | ||
| 35 | SDL_DestroyWindow(window); | ||
| 36 | } | ||
| 37 | |||
| 38 | void MakeCurrent() override { | ||
| 39 | SDL_GL_MakeCurrent(window, context); | ||
| 40 | } | ||
| 41 | |||
| 42 | void DoneCurrent() override { | ||
| 43 | SDL_GL_MakeCurrent(window, nullptr); | ||
| 44 | } | 17 | } |
| 18 | InputCommon::Init(); | ||
| 19 | SDL_SetMainReady(); | ||
| 20 | } | ||
| 45 | 21 | ||
| 46 | void SwapBuffers() override {} | 22 | EmuWindow_SDL2::~EmuWindow_SDL2() { |
| 47 | 23 | InputCommon::Shutdown(); | |
| 48 | private: | 24 | SDL_Quit(); |
| 49 | SDL_Window* window; | 25 | } |
| 50 | SDL_GLContext context; | ||
| 51 | }; | ||
| 52 | 26 | ||
| 53 | void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { | 27 | void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { |
| 54 | TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); | 28 | TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); |
| @@ -139,108 +113,6 @@ void EmuWindow_SDL2::Fullscreen() { | |||
| 139 | SDL_MaximizeWindow(render_window); | 113 | SDL_MaximizeWindow(render_window); |
| 140 | } | 114 | } |
| 141 | 115 | ||
| 142 | bool EmuWindow_SDL2::SupportsRequiredGLExtensions() { | ||
| 143 | std::vector<std::string> unsupported_ext; | ||
| 144 | |||
| 145 | if (!GLAD_GL_ARB_direct_state_access) | ||
| 146 | unsupported_ext.push_back("ARB_direct_state_access"); | ||
| 147 | if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) | ||
| 148 | unsupported_ext.push_back("ARB_vertex_type_10f_11f_11f_rev"); | ||
| 149 | if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) | ||
| 150 | unsupported_ext.push_back("ARB_texture_mirror_clamp_to_edge"); | ||
| 151 | if (!GLAD_GL_ARB_multi_bind) | ||
| 152 | unsupported_ext.push_back("ARB_multi_bind"); | ||
| 153 | |||
| 154 | // Extensions required to support some texture formats. | ||
| 155 | if (!GLAD_GL_EXT_texture_compression_s3tc) | ||
| 156 | unsupported_ext.push_back("EXT_texture_compression_s3tc"); | ||
| 157 | if (!GLAD_GL_ARB_texture_compression_rgtc) | ||
| 158 | unsupported_ext.push_back("ARB_texture_compression_rgtc"); | ||
| 159 | if (!GLAD_GL_ARB_depth_buffer_float) | ||
| 160 | unsupported_ext.push_back("ARB_depth_buffer_float"); | ||
| 161 | |||
| 162 | for (const std::string& ext : unsupported_ext) | ||
| 163 | LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext); | ||
| 164 | |||
| 165 | return unsupported_ext.empty(); | ||
| 166 | } | ||
| 167 | |||
| 168 | EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { | ||
| 169 | // Initialize the window | ||
| 170 | if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { | ||
| 171 | LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); | ||
| 172 | exit(1); | ||
| 173 | } | ||
| 174 | |||
| 175 | InputCommon::Init(); | ||
| 176 | |||
| 177 | SDL_SetMainReady(); | ||
| 178 | |||
| 179 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); | ||
| 180 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); | ||
| 181 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); | ||
| 182 | SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); | ||
| 183 | SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); | ||
| 184 | SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); | ||
| 185 | SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); | ||
| 186 | SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); | ||
| 187 | SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); | ||
| 188 | |||
| 189 | std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname, | ||
| 190 | Common::g_scm_branch, Common::g_scm_desc); | ||
| 191 | render_window = | ||
| 192 | SDL_CreateWindow(window_title.c_str(), | ||
| 193 | SDL_WINDOWPOS_UNDEFINED, // x position | ||
| 194 | SDL_WINDOWPOS_UNDEFINED, // y position | ||
| 195 | Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, | ||
| 196 | SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); | ||
| 197 | |||
| 198 | if (render_window == nullptr) { | ||
| 199 | LOG_CRITICAL(Frontend, "Failed to create SDL2 window! {}", SDL_GetError()); | ||
| 200 | exit(1); | ||
| 201 | } | ||
| 202 | |||
| 203 | if (fullscreen) { | ||
| 204 | Fullscreen(); | ||
| 205 | } | ||
| 206 | gl_context = SDL_GL_CreateContext(render_window); | ||
| 207 | |||
| 208 | if (gl_context == nullptr) { | ||
| 209 | LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context! {}", SDL_GetError()); | ||
| 210 | exit(1); | ||
| 211 | } | ||
| 212 | |||
| 213 | if (!gladLoadGLLoader(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) { | ||
| 214 | LOG_CRITICAL(Frontend, "Failed to initialize GL functions! {}", SDL_GetError()); | ||
| 215 | exit(1); | ||
| 216 | } | ||
| 217 | |||
| 218 | if (!SupportsRequiredGLExtensions()) { | ||
| 219 | LOG_CRITICAL(Frontend, "GPU does not support all required OpenGL extensions! Exiting..."); | ||
| 220 | exit(1); | ||
| 221 | } | ||
| 222 | |||
| 223 | OnResize(); | ||
| 224 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); | ||
| 225 | SDL_PumpEvents(); | ||
| 226 | SDL_GL_SetSwapInterval(false); | ||
| 227 | LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, | ||
| 228 | Common::g_scm_desc); | ||
| 229 | Settings::LogSettings(); | ||
| 230 | |||
| 231 | DoneCurrent(); | ||
| 232 | } | ||
| 233 | |||
| 234 | EmuWindow_SDL2::~EmuWindow_SDL2() { | ||
| 235 | InputCommon::Shutdown(); | ||
| 236 | SDL_GL_DeleteContext(gl_context); | ||
| 237 | SDL_Quit(); | ||
| 238 | } | ||
| 239 | |||
| 240 | void EmuWindow_SDL2::SwapBuffers() { | ||
| 241 | SDL_GL_SwapWindow(render_window); | ||
| 242 | } | ||
| 243 | |||
| 244 | void EmuWindow_SDL2::PollEvents() { | 116 | void EmuWindow_SDL2::PollEvents() { |
| 245 | SDL_Event event; | 117 | SDL_Event event; |
| 246 | 118 | ||
| @@ -253,7 +125,11 @@ void EmuWindow_SDL2::PollEvents() { | |||
| 253 | case SDL_WINDOWEVENT_RESIZED: | 125 | case SDL_WINDOWEVENT_RESIZED: |
| 254 | case SDL_WINDOWEVENT_MAXIMIZED: | 126 | case SDL_WINDOWEVENT_MAXIMIZED: |
| 255 | case SDL_WINDOWEVENT_RESTORED: | 127 | case SDL_WINDOWEVENT_RESTORED: |
| 128 | OnResize(); | ||
| 129 | break; | ||
| 256 | case SDL_WINDOWEVENT_MINIMIZED: | 130 | case SDL_WINDOWEVENT_MINIMIZED: |
| 131 | case SDL_WINDOWEVENT_EXPOSED: | ||
| 132 | is_shown = event.window.event == SDL_WINDOWEVENT_EXPOSED; | ||
| 257 | OnResize(); | 133 | OnResize(); |
| 258 | break; | 134 | break; |
| 259 | case SDL_WINDOWEVENT_CLOSE: | 135 | case SDL_WINDOWEVENT_CLOSE: |
| @@ -296,20 +172,6 @@ void EmuWindow_SDL2::PollEvents() { | |||
| 296 | } | 172 | } |
| 297 | } | 173 | } |
| 298 | 174 | ||
| 299 | void EmuWindow_SDL2::MakeCurrent() { | 175 | void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) { |
| 300 | SDL_GL_MakeCurrent(render_window, gl_context); | ||
| 301 | } | ||
| 302 | |||
| 303 | void EmuWindow_SDL2::DoneCurrent() { | ||
| 304 | SDL_GL_MakeCurrent(render_window, nullptr); | ||
| 305 | } | ||
| 306 | |||
| 307 | void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest( | ||
| 308 | const std::pair<unsigned, unsigned>& minimal_size) { | ||
| 309 | |||
| 310 | SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second); | 176 | SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second); |
| 311 | } | 177 | } |
| 312 | |||
| 313 | std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2::CreateSharedContext() const { | ||
| 314 | return std::make_unique<SDLGLContext>(); | ||
| 315 | } | ||
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: | |||
| 15 | explicit EmuWindow_SDL2(bool fullscreen); | 15 | explicit EmuWindow_SDL2(bool fullscreen); |
| 16 | ~EmuWindow_SDL2(); | 16 | ~EmuWindow_SDL2(); |
| 17 | 17 | ||
| 18 | /// Swap buffers to display the next frame | ||
| 19 | void SwapBuffers() override; | ||
| 20 | |||
| 21 | /// Polls window events | 18 | /// Polls window events |
| 22 | void PollEvents() override; | 19 | void PollEvents() override; |
| 23 | 20 | ||
| 24 | /// Makes the graphics context current for the caller thread | ||
| 25 | void MakeCurrent() override; | ||
| 26 | |||
| 27 | /// Releases the GL context from the caller thread | ||
| 28 | void DoneCurrent() override; | ||
| 29 | |||
| 30 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; | ||
| 31 | |||
| 32 | /// Whether the window is still open, and a close request hasn't yet been sent | 21 | /// Whether the window is still open, and a close request hasn't yet been sent |
| 33 | bool IsOpen() const; | 22 | bool IsOpen() const; |
| 34 | 23 | ||
| 35 | private: | 24 | protected: |
| 36 | /// Called by PollEvents when a key is pressed or released. | 25 | /// Called by PollEvents when a key is pressed or released. |
| 37 | void OnKeyEvent(int key, u8 state); | 26 | void OnKeyEvent(int key, u8 state); |
| 38 | 27 | ||
| @@ -60,20 +49,15 @@ private: | |||
| 60 | /// Called when user passes the fullscreen parameter flag | 49 | /// Called when user passes the fullscreen parameter flag |
| 61 | void Fullscreen(); | 50 | void Fullscreen(); |
| 62 | 51 | ||
| 63 | /// Whether the GPU and driver supports the OpenGL extension required | ||
| 64 | bool SupportsRequiredGLExtensions(); | ||
| 65 | |||
| 66 | /// Called when a configuration change affects the minimal size of the window | 52 | /// Called when a configuration change affects the minimal size of the window |
| 67 | void OnMinimalClientAreaChangeRequest( | 53 | void OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) override; |
| 68 | const std::pair<unsigned, unsigned>& minimal_size) override; | ||
| 69 | 54 | ||
| 70 | /// Is the window still open? | 55 | /// Is the window still open? |
| 71 | bool is_open = true; | 56 | bool is_open = true; |
| 72 | 57 | ||
| 58 | /// Is the window being shown? | ||
| 59 | bool is_shown = true; | ||
| 60 | |||
| 73 | /// Internal SDL2 render window | 61 | /// Internal SDL2 render window |
| 74 | SDL_Window* render_window; | 62 | SDL_Window* render_window; |
| 75 | |||
| 76 | using SDL_GLContext = void*; | ||
| 77 | /// The OpenGL context associated with the window | ||
| 78 | SDL_GLContext gl_context; | ||
| 79 | }; | 63 | }; |
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 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <cstdlib> | ||
| 7 | #include <string> | ||
| 8 | #define SDL_MAIN_HANDLED | ||
| 9 | #include <SDL.h> | ||
| 10 | #include <fmt/format.h> | ||
| 11 | #include <glad/glad.h> | ||
| 12 | #include "common/logging/log.h" | ||
| 13 | #include "common/scm_rev.h" | ||
| 14 | #include "common/string_util.h" | ||
| 15 | #include "core/settings.h" | ||
| 16 | #include "input_common/keyboard.h" | ||
| 17 | #include "input_common/main.h" | ||
| 18 | #include "input_common/motion_emu.h" | ||
| 19 | #include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" | ||
| 20 | |||
| 21 | class SDLGLContext : public Core::Frontend::GraphicsContext { | ||
| 22 | public: | ||
| 23 | explicit SDLGLContext() { | ||
| 24 | // create a hidden window to make the shared context against | ||
| 25 | window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, // x position | ||
| 26 | SDL_WINDOWPOS_UNDEFINED, // y position | ||
| 27 | Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, | ||
| 28 | SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); | ||
| 29 | context = SDL_GL_CreateContext(window); | ||
| 30 | } | ||
| 31 | |||
| 32 | ~SDLGLContext() { | ||
| 33 | SDL_GL_DeleteContext(context); | ||
| 34 | SDL_DestroyWindow(window); | ||
| 35 | } | ||
| 36 | |||
| 37 | void MakeCurrent() override { | ||
| 38 | SDL_GL_MakeCurrent(window, context); | ||
| 39 | } | ||
| 40 | |||
| 41 | void DoneCurrent() override { | ||
| 42 | SDL_GL_MakeCurrent(window, nullptr); | ||
| 43 | } | ||
| 44 | |||
| 45 | void SwapBuffers() override {} | ||
| 46 | |||
| 47 | private: | ||
| 48 | SDL_Window* window; | ||
| 49 | SDL_GLContext context; | ||
| 50 | }; | ||
| 51 | |||
| 52 | bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() { | ||
| 53 | std::vector<std::string> unsupported_ext; | ||
| 54 | |||
| 55 | if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) | ||
| 56 | unsupported_ext.push_back("ARB_vertex_type_10f_11f_11f_rev"); | ||
| 57 | if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) | ||
| 58 | unsupported_ext.push_back("ARB_texture_mirror_clamp_to_edge"); | ||
| 59 | if (!GLAD_GL_ARB_multi_bind) | ||
| 60 | unsupported_ext.push_back("ARB_multi_bind"); | ||
| 61 | |||
| 62 | // Extensions required to support some texture formats. | ||
| 63 | if (!GLAD_GL_EXT_texture_compression_s3tc) | ||
| 64 | unsupported_ext.push_back("EXT_texture_compression_s3tc"); | ||
| 65 | if (!GLAD_GL_ARB_texture_compression_rgtc) | ||
| 66 | unsupported_ext.push_back("ARB_texture_compression_rgtc"); | ||
| 67 | if (!GLAD_GL_ARB_depth_buffer_float) | ||
| 68 | unsupported_ext.push_back("ARB_depth_buffer_float"); | ||
| 69 | |||
| 70 | for (const std::string& ext : unsupported_ext) | ||
| 71 | LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext); | ||
| 72 | |||
| 73 | return unsupported_ext.empty(); | ||
| 74 | } | ||
| 75 | |||
| 76 | EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(bool fullscreen) : EmuWindow_SDL2(fullscreen) { | ||
| 77 | const SDL_GLprofile profile = Settings::values.use_compatibility_profile | ||
| 78 | ? SDL_GL_CONTEXT_PROFILE_COMPATIBILITY | ||
| 79 | : SDL_GL_CONTEXT_PROFILE_CORE; | ||
| 80 | |||
| 81 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); | ||
| 82 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); | ||
| 83 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profile); | ||
| 84 | SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); | ||
| 85 | SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); | ||
| 86 | SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); | ||
| 87 | SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); | ||
| 88 | SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); | ||
| 89 | SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); | ||
| 90 | |||
| 91 | std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname, | ||
| 92 | Common::g_scm_branch, Common::g_scm_desc); | ||
| 93 | render_window = | ||
| 94 | SDL_CreateWindow(window_title.c_str(), | ||
| 95 | SDL_WINDOWPOS_UNDEFINED, // x position | ||
| 96 | SDL_WINDOWPOS_UNDEFINED, // y position | ||
| 97 | Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, | ||
| 98 | SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); | ||
| 99 | |||
| 100 | if (render_window == nullptr) { | ||
| 101 | LOG_CRITICAL(Frontend, "Failed to create SDL2 window! {}", SDL_GetError()); | ||
| 102 | exit(1); | ||
| 103 | } | ||
| 104 | |||
| 105 | if (fullscreen) { | ||
| 106 | Fullscreen(); | ||
| 107 | } | ||
| 108 | gl_context = SDL_GL_CreateContext(render_window); | ||
| 109 | |||
| 110 | if (gl_context == nullptr) { | ||
| 111 | LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context! {}", SDL_GetError()); | ||
| 112 | exit(1); | ||
| 113 | } | ||
| 114 | |||
| 115 | if (!gladLoadGLLoader(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) { | ||
| 116 | LOG_CRITICAL(Frontend, "Failed to initialize GL functions! {}", SDL_GetError()); | ||
| 117 | exit(1); | ||
| 118 | } | ||
| 119 | |||
| 120 | if (!SupportsRequiredGLExtensions()) { | ||
| 121 | LOG_CRITICAL(Frontend, "GPU does not support all required OpenGL extensions! Exiting..."); | ||
| 122 | exit(1); | ||
| 123 | } | ||
| 124 | |||
| 125 | OnResize(); | ||
| 126 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); | ||
| 127 | SDL_PumpEvents(); | ||
| 128 | SDL_GL_SetSwapInterval(false); | ||
| 129 | LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, | ||
| 130 | Common::g_scm_desc); | ||
| 131 | Settings::LogSettings(); | ||
| 132 | |||
| 133 | DoneCurrent(); | ||
| 134 | } | ||
| 135 | |||
| 136 | EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() { | ||
| 137 | SDL_GL_DeleteContext(gl_context); | ||
| 138 | } | ||
| 139 | |||
| 140 | void EmuWindow_SDL2_GL::SwapBuffers() { | ||
| 141 | SDL_GL_SwapWindow(render_window); | ||
| 142 | } | ||
| 143 | |||
| 144 | void EmuWindow_SDL2_GL::MakeCurrent() { | ||
| 145 | SDL_GL_MakeCurrent(render_window, gl_context); | ||
| 146 | } | ||
| 147 | |||
| 148 | void EmuWindow_SDL2_GL::DoneCurrent() { | ||
| 149 | SDL_GL_MakeCurrent(render_window, nullptr); | ||
| 150 | } | ||
| 151 | |||
| 152 | std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const { | ||
| 153 | return std::make_unique<SDLGLContext>(); | ||
| 154 | } | ||
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 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include "core/frontend/emu_window.h" | ||
| 9 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" | ||
| 10 | |||
| 11 | class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 { | ||
| 12 | public: | ||
| 13 | explicit EmuWindow_SDL2_GL(bool fullscreen); | ||
| 14 | ~EmuWindow_SDL2_GL(); | ||
| 15 | |||
| 16 | /// Swap buffers to display the next frame | ||
| 17 | void SwapBuffers() override; | ||
| 18 | |||
| 19 | /// Makes the graphics context current for the caller thread | ||
| 20 | void MakeCurrent() override; | ||
| 21 | |||
| 22 | /// Releases the GL context from the caller thread | ||
| 23 | void DoneCurrent() override; | ||
| 24 | |||
| 25 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; | ||
| 26 | |||
| 27 | private: | ||
| 28 | /// Whether the GPU and driver supports the OpenGL extension required | ||
| 29 | bool SupportsRequiredGLExtensions(); | ||
| 30 | |||
| 31 | using SDL_GLContext = void*; | ||
| 32 | /// The OpenGL context associated with the window | ||
| 33 | SDL_GLContext gl_context; | ||
| 34 | }; | ||
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index a1d7879b1..5d9442646 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -31,6 +31,7 @@ | |||
| 31 | #include "video_core/renderer_base.h" | 31 | #include "video_core/renderer_base.h" |
| 32 | #include "yuzu_cmd/config.h" | 32 | #include "yuzu_cmd/config.h" |
| 33 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" | 33 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" |
| 34 | #include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" | ||
| 34 | 35 | ||
| 35 | #include "core/file_sys/registered_cache.h" | 36 | #include "core/file_sys/registered_cache.h" |
| 36 | 37 | ||
| @@ -173,7 +174,7 @@ int main(int argc, char** argv) { | |||
| 173 | Settings::values.use_gdbstub = use_gdbstub; | 174 | Settings::values.use_gdbstub = use_gdbstub; |
| 174 | Settings::Apply(); | 175 | Settings::Apply(); |
| 175 | 176 | ||
| 176 | std::unique_ptr<EmuWindow_SDL2> emu_window{std::make_unique<EmuWindow_SDL2>(fullscreen)}; | 177 | std::unique_ptr<EmuWindow_SDL2> emu_window{std::make_unique<EmuWindow_SDL2_GL>(fullscreen)}; |
| 177 | 178 | ||
| 178 | if (!Settings::values.use_multi_core) { | 179 | if (!Settings::values.use_multi_core) { |
| 179 | // Single core mode must acquire OpenGL context for entire emulation session | 180 | // Single core mode must acquire OpenGL context for entire emulation session |
| @@ -222,6 +223,7 @@ int main(int argc, char** argv) { | |||
| 222 | 223 | ||
| 223 | system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDL"); | 224 | system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDL"); |
| 224 | 225 | ||
| 226 | emu_window->MakeCurrent(); | ||
| 225 | system.Renderer().Rasterizer().LoadDiskResources(); | 227 | system.Renderer().Rasterizer().LoadDiskResources(); |
| 226 | 228 | ||
| 227 | while (emu_window->IsOpen()) { | 229 | while (emu_window->IsOpen()) { |