Compare commits
175 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9853412cea | |||
| 90c9d703ba | |||
| bb248a2710 | |||
| f97e206348 | |||
| 91300bdfb2 | |||
| 88cd5e888e | |||
| 6640f631e2 | |||
| d61199721d | |||
| ef3c0f54d0 | |||
| c03fb00ac1 | |||
| 5d645c6dd9 | |||
| 9218e347cd | |||
| 5a4564bd8e | |||
| e86d2e2e5b | |||
| 68c9c9222d | |||
| 3c0280cf66 | |||
| bb06b98d81 | |||
| 16bf791939 | |||
| b3d7180164 | |||
| cf9cc41478 | |||
| f5d416e071 | |||
| 6f2a8fbb13 | |||
| 1a2d90ab09 | |||
| 59f110ef31 | |||
| d4f8fe24d9 | |||
| c70404eab5 | |||
| e7ab0e9127 | |||
| 11e9bee91d | |||
| 943f6da1ac | |||
| 2b1fcc8a14 | |||
| e3b2539986 | |||
| 8cd3d9be26 | |||
| b6dcb1ae4d | |||
| 228e58d0a5 | |||
| 87b4c1ac5e | |||
| 3e7d37301a | |||
| d0e200a894 | |||
| 819d229e76 | |||
| 195b54602f | |||
| f4c15db9e8 | |||
| de23847184 | |||
| 69215b5a55 | |||
| c03b8c4c19 | |||
| 75e7b45d69 | |||
| f78ef617b6 | |||
| f49a04ba39 | |||
| 938d6dca30 | |||
| 9ffc60b5b3 | |||
| dbcff5d574 | |||
| 9a17b20896 | |||
| bc32474901 | |||
| ed2fedac13 | |||
| 7a82d6f394 | |||
| 486c3e6085 | |||
| 922d8c6cb4 | |||
| fd34732e26 | |||
| 317f1263fb | |||
| 58a0c13e34 | |||
| 6fb29764d6 | |||
| 784d2b6c3d | |||
| 0adb54abc1 | |||
| 73ee85e9ae | |||
| 911fafb967 | |||
| 91ec251c4a | |||
| d49efbfb4a | |||
| 13dda1d8ed | |||
| 2318c394a8 | |||
| d9c4d64ed5 | |||
| 428d8098a7 | |||
| 17255cd835 | |||
| 3039211c20 | |||
| c9c4208c4a | |||
| aa83639b78 | |||
| 4d2da5a40a | |||
| b94b08fa6f | |||
| d81d4a0f68 | |||
| 7e650088dd | |||
| 05235ccaa9 | |||
| 5f01ec338e | |||
| d00ca5c6c8 | |||
| d184224e8f | |||
| f8b1e53369 | |||
| 2aebbe9bf9 | |||
| fadf66993c | |||
| 9e98100c94 | |||
| 6fd247c84a | |||
| 18cdbdafa2 | |||
| 594328f494 | |||
| 9175bffbdb | |||
| ac3775e6ae | |||
| b54bd3f018 | |||
| 3bd5f01240 | |||
| a6ed792ac4 | |||
| 3ff0c70c72 | |||
| 4452195d41 | |||
| 8e9a1e4249 | |||
| dfddb12255 | |||
| f9ad88f9d7 | |||
| edf8c0a545 | |||
| 209a0dfa35 | |||
| 27033de2e5 | |||
| 30c984dc97 | |||
| 256e5c9583 | |||
| b42ca9888d | |||
| ffd9a1f3ef | |||
| 2437ca04d7 | |||
| aa61478d8c | |||
| e310d943b8 | |||
| 212b148923 | |||
| 81e7e63080 | |||
| e09ee0ff23 | |||
| bc6972caf9 | |||
| ce04ab38bb | |||
| 3356ea5bc2 | |||
| 0a7f09a99b | |||
| 634b78a4c6 | |||
| 7fdc644c44 | |||
| 683c4e523f | |||
| 5e4c227608 | |||
| f417be9d3b | |||
| 22324e3ef1 | |||
| d77d1a0207 | |||
| cad4f2ed29 | |||
| a059b9eed4 | |||
| 242273788a | |||
| 5993133d5e | |||
| d5cce86431 | |||
| 88c263ee8e | |||
| a47aaa7f1b | |||
| 8d6342384b | |||
| 21ea8b2fcb | |||
| 52340c3294 | |||
| a652e58c54 | |||
| fb85d5670d | |||
| 6f1720a5b7 | |||
| 865025f612 | |||
| 1975d32f2d | |||
| 4cf64f8e09 | |||
| 69265e4504 | |||
| 5f877d9458 | |||
| e6c60b419c | |||
| a6fb6ccc83 | |||
| fc975e9021 | |||
| b01cce716e | |||
| 9b6d993e52 | |||
| ec1c69258a | |||
| 0f83c8dffa | |||
| 5db1b54b58 | |||
| 48ce5880a0 | |||
| c212fc9b2c | |||
| d6d809db87 | |||
| c5129a3a58 | |||
| 9ef45f00bf | |||
| c823cf6594 | |||
| d955944869 | |||
| bf1829a717 | |||
| 4ef3329f81 | |||
| 3a08c3207b | |||
| d4df803b2b | |||
| 5321cdd276 | |||
| 28bffb1ffa | |||
| fe700e1856 | |||
| c6f9e651b2 | |||
| 71aa9d0877 | |||
| b7d412c99b | |||
| bd81a03d9d | |||
| 06b363c9b5 | |||
| 002ecbea19 | |||
| 7632a7d6d2 | |||
| 2bcb8a20b4 | |||
| 03746be097 | |||
| de93507a5a | |||
| 19632d2421 | |||
| d672c6e759 | |||
| 69a2003a8e |
+3
-3
@@ -132,7 +132,7 @@ find_package(Threads REQUIRED)
|
||||
if (ENABLE_SDL2)
|
||||
if (YUZU_USE_BUNDLED_SDL2)
|
||||
# Detect toolchain and platform
|
||||
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1920) AND ARCHITECTURE_x86_64)
|
||||
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1930) AND ARCHITECTURE_x86_64)
|
||||
set(SDL2_VER "SDL2-2.0.8")
|
||||
else()
|
||||
message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable YUZU_USE_BUNDLED_SDL2 and provide your own.")
|
||||
@@ -165,7 +165,7 @@ if (YUZU_USE_BUNDLED_UNICORN)
|
||||
if (MSVC)
|
||||
message(STATUS "unicorn not found, falling back to bundled")
|
||||
# Detect toolchain and platform
|
||||
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1920) AND ARCHITECTURE_x86_64)
|
||||
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1930) AND ARCHITECTURE_x86_64)
|
||||
set(UNICORN_VER "unicorn-yuzu")
|
||||
else()
|
||||
message(FATAL_ERROR "No bundled Unicorn binaries for your toolchain. Disable YUZU_USE_BUNDLED_UNICORN and provide your own.")
|
||||
@@ -233,7 +233,7 @@ endif()
|
||||
|
||||
if (ENABLE_QT)
|
||||
if (YUZU_USE_BUNDLED_QT)
|
||||
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1920) AND ARCHITECTURE_x86_64)
|
||||
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1930) AND ARCHITECTURE_x86_64)
|
||||
set(QT_VER qt-5.12.0-msvc2017_64)
|
||||
else()
|
||||
message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable YUZU_USE_BUNDLED_QT and provide your own.")
|
||||
|
||||
+10
-2
@@ -90,12 +90,20 @@
|
||||
* int arg2) KHRONOS_APIATTRIBUTES;
|
||||
*/
|
||||
|
||||
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
|
||||
# define KHRONOS_STATIC 1
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APICALL
|
||||
*-------------------------------------------------------------------------
|
||||
* This precedes the return type of the function in the function prototype.
|
||||
*/
|
||||
#if defined(_WIN32) && !defined(__SCITECH_SNAP__)
|
||||
#if defined(KHRONOS_STATIC)
|
||||
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
|
||||
* header compatible with static linking. */
|
||||
# define KHRONOS_APICALL
|
||||
#elif defined(_WIN32)
|
||||
# define KHRONOS_APICALL __declspec(dllimport)
|
||||
#elif defined (__SYMBIAN32__)
|
||||
# define KHRONOS_APICALL IMPORT_C
|
||||
@@ -111,7 +119,7 @@
|
||||
* This follows the return type of the function and precedes the function
|
||||
* name in the function prototype.
|
||||
*/
|
||||
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
|
||||
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(KHRONOS_STATIC)
|
||||
/* Win32 but not WinCE */
|
||||
# define KHRONOS_APIENTRY __stdcall
|
||||
#else
|
||||
|
||||
Vendored
+1976
-412
File diff suppressed because one or more lines are too long
Vendored
+997
-128
File diff suppressed because one or more lines are too long
+13
-36
@@ -78,16 +78,17 @@ namespace FileUtil {
|
||||
// Remove any ending forward slashes from directory paths
|
||||
// Modifies argument.
|
||||
static void StripTailDirSlashes(std::string& fname) {
|
||||
if (fname.length() > 1) {
|
||||
std::size_t i = fname.length();
|
||||
while (i > 0 && fname[i - 1] == DIR_SEP_CHR)
|
||||
--i;
|
||||
fname.resize(i);
|
||||
if (fname.length() <= 1) {
|
||||
return;
|
||||
}
|
||||
return;
|
||||
|
||||
std::size_t i = fname.length();
|
||||
while (i > 0 && fname[i - 1] == DIR_SEP_CHR) {
|
||||
--i;
|
||||
}
|
||||
fname.resize(i);
|
||||
}
|
||||
|
||||
// Returns true if file filename exists
|
||||
bool Exists(const std::string& filename) {
|
||||
struct stat file_info;
|
||||
|
||||
@@ -107,7 +108,6 @@ bool Exists(const std::string& filename) {
|
||||
return (result == 0);
|
||||
}
|
||||
|
||||
// Returns true if filename is a directory
|
||||
bool IsDirectory(const std::string& filename) {
|
||||
struct stat file_info;
|
||||
|
||||
@@ -132,8 +132,6 @@ bool IsDirectory(const std::string& filename) {
|
||||
return S_ISDIR(file_info.st_mode);
|
||||
}
|
||||
|
||||
// Deletes a given filename, return true on success
|
||||
// Doesn't supports deleting a directory
|
||||
bool Delete(const std::string& filename) {
|
||||
LOG_TRACE(Common_Filesystem, "file {}", filename);
|
||||
|
||||
@@ -165,7 +163,6 @@ bool Delete(const std::string& filename) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns true if successful, or path already exists.
|
||||
bool CreateDir(const std::string& path) {
|
||||
LOG_TRACE(Common_Filesystem, "directory {}", path);
|
||||
#ifdef _WIN32
|
||||
@@ -194,7 +191,6 @@ bool CreateDir(const std::string& path) {
|
||||
#endif
|
||||
}
|
||||
|
||||
// Creates the full path of fullPath returns true on success
|
||||
bool CreateFullPath(const std::string& fullPath) {
|
||||
int panicCounter = 100;
|
||||
LOG_TRACE(Common_Filesystem, "path {}", fullPath);
|
||||
@@ -230,7 +226,6 @@ bool CreateFullPath(const std::string& fullPath) {
|
||||
}
|
||||
}
|
||||
|
||||
// Deletes a directory filename, returns true on success
|
||||
bool DeleteDir(const std::string& filename) {
|
||||
LOG_TRACE(Common_Filesystem, "directory {}", filename);
|
||||
|
||||
@@ -252,7 +247,6 @@ bool DeleteDir(const std::string& filename) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// renames file srcFilename to destFilename, returns true on success
|
||||
bool Rename(const std::string& srcFilename, const std::string& destFilename) {
|
||||
LOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename);
|
||||
#ifdef _WIN32
|
||||
@@ -268,7 +262,6 @@ bool Rename(const std::string& srcFilename, const std::string& destFilename) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// copies file srcFilename to destFilename, returns true on success
|
||||
bool Copy(const std::string& srcFilename, const std::string& destFilename) {
|
||||
LOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename);
|
||||
#ifdef _WIN32
|
||||
@@ -324,7 +317,6 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) {
|
||||
#endif
|
||||
}
|
||||
|
||||
// Returns the size of filename (64bit)
|
||||
u64 GetSize(const std::string& filename) {
|
||||
if (!Exists(filename)) {
|
||||
LOG_ERROR(Common_Filesystem, "failed {}: No such file", filename);
|
||||
@@ -351,7 +343,6 @@ u64 GetSize(const std::string& filename) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Overloaded GetSize, accepts file descriptor
|
||||
u64 GetSize(const int fd) {
|
||||
struct stat buf;
|
||||
if (fstat(fd, &buf) != 0) {
|
||||
@@ -361,7 +352,6 @@ u64 GetSize(const int fd) {
|
||||
return buf.st_size;
|
||||
}
|
||||
|
||||
// Overloaded GetSize, accepts FILE*
|
||||
u64 GetSize(FILE* f) {
|
||||
// can't use off_t here because it can be 32-bit
|
||||
u64 pos = ftello(f);
|
||||
@@ -377,7 +367,6 @@ u64 GetSize(FILE* f) {
|
||||
return size;
|
||||
}
|
||||
|
||||
// creates an empty file filename, returns true on success
|
||||
bool CreateEmptyFile(const std::string& filename) {
|
||||
LOG_TRACE(Common_Filesystem, "{}", filename);
|
||||
|
||||
@@ -502,7 +491,6 @@ bool DeleteDirRecursively(const std::string& directory, unsigned int recursion)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create directory and copy contents (does not overwrite existing files)
|
||||
void CopyDir(const std::string& source_path, const std::string& dest_path) {
|
||||
#ifndef _WIN32
|
||||
if (source_path == dest_path)
|
||||
@@ -539,8 +527,7 @@ void CopyDir(const std::string& source_path, const std::string& dest_path) {
|
||||
#endif
|
||||
}
|
||||
|
||||
// Returns the current directory
|
||||
std::string GetCurrentDir() {
|
||||
std::optional<std::string> GetCurrentDir() {
|
||||
// Get the current working directory (getcwd uses malloc)
|
||||
#ifdef _WIN32
|
||||
wchar_t* dir;
|
||||
@@ -550,7 +537,7 @@ std::string GetCurrentDir() {
|
||||
if (!(dir = getcwd(nullptr, 0))) {
|
||||
#endif
|
||||
LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: {}", GetLastErrorMsg());
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
#ifdef _WIN32
|
||||
std::string strDir = Common::UTF16ToUTF8(dir);
|
||||
@@ -561,7 +548,6 @@ std::string GetCurrentDir() {
|
||||
return strDir;
|
||||
}
|
||||
|
||||
// Sets the current directory to the given directory
|
||||
bool SetCurrentDir(const std::string& directory) {
|
||||
#ifdef _WIN32
|
||||
return _wchdir(Common::UTF8ToUTF16W(directory).c_str()) == 0;
|
||||
@@ -673,8 +659,6 @@ std::string GetSysDirectory() {
|
||||
return sysDir;
|
||||
}
|
||||
|
||||
// Returns a string with a yuzu data dir or file in the user's home
|
||||
// directory. To be used in "multi-user" mode (that is, installed).
|
||||
const std::string& GetUserPath(UserPath path, const std::string& new_path) {
|
||||
static std::unordered_map<UserPath, std::string> paths;
|
||||
auto& user_path = paths[UserPath::UserDir];
|
||||
@@ -762,11 +746,11 @@ std::string GetNANDRegistrationDir(bool system) {
|
||||
return GetUserPath(UserPath::NANDDir) + "user/Contents/registered/";
|
||||
}
|
||||
|
||||
std::size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) {
|
||||
return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size());
|
||||
std::size_t WriteStringToFile(bool text_file, const std::string& filename, std::string_view str) {
|
||||
return IOFile(filename, text_file ? "w" : "wb").WriteString(str);
|
||||
}
|
||||
|
||||
std::size_t ReadFileToString(bool text_file, const char* filename, std::string& str) {
|
||||
std::size_t ReadFileToString(bool text_file, const std::string& filename, std::string& str) {
|
||||
IOFile file(filename, text_file ? "r" : "rb");
|
||||
|
||||
if (!file.IsOpen())
|
||||
@@ -776,13 +760,6 @@ std::size_t ReadFileToString(bool text_file, const char* filename, std::string&
|
||||
return file.ReadArray(&str[0], str.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits the filename into 8.3 format
|
||||
* Loosely implemented following https://en.wikipedia.org/wiki/8.3_filename
|
||||
* @param filename The normal filename to use
|
||||
* @param short_name A 9-char array in which the short name will be written
|
||||
* @param extension A 4-char array in which the extension will be written
|
||||
*/
|
||||
void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name,
|
||||
std::array<char, 4>& extension) {
|
||||
const std::string forbidden_characters = ".\"/\\[]:;=, ";
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
@@ -118,7 +119,7 @@ u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
|
||||
bool DeleteDirRecursively(const std::string& directory, unsigned int recursion = 256);
|
||||
|
||||
// Returns the current directory
|
||||
std::string GetCurrentDir();
|
||||
std::optional<std::string> GetCurrentDir();
|
||||
|
||||
// Create directory and copy contents (does not overwrite existing files)
|
||||
void CopyDir(const std::string& source_path, const std::string& dest_path);
|
||||
@@ -146,9 +147,9 @@ const std::string& GetExeDirectory();
|
||||
std::string AppDataRoamingDirectory();
|
||||
#endif
|
||||
|
||||
std::size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename);
|
||||
std::size_t WriteStringToFile(bool text_file, const std::string& filename, std::string_view str);
|
||||
|
||||
std::size_t ReadFileToString(bool text_file, const char* filename, std::string& str);
|
||||
std::size_t ReadFileToString(bool text_file, const std::string& filename, std::string& str);
|
||||
|
||||
/**
|
||||
* Splits the filename into 8.3 format
|
||||
@@ -257,8 +258,8 @@ public:
|
||||
return WriteArray(&object, 1);
|
||||
}
|
||||
|
||||
std::size_t WriteString(const std::string& str) {
|
||||
return WriteArray(str.c_str(), str.length());
|
||||
std::size_t WriteString(std::string_view str) {
|
||||
return WriteArray(str.data(), str.length());
|
||||
}
|
||||
|
||||
bool IsOpen() const {
|
||||
@@ -286,8 +287,8 @@ private:
|
||||
template <typename T>
|
||||
void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) {
|
||||
#ifdef _MSC_VER
|
||||
fstream.open(Common::UTF8ToUTF16W(filename).c_str(), openmode);
|
||||
fstream.open(Common::UTF8ToUTF16W(filename), openmode);
|
||||
#else
|
||||
fstream.open(filename.c_str(), openmode);
|
||||
fstream.open(filename, openmode);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -438,7 +438,7 @@ inline float RequestParser::Pop() {
|
||||
template <>
|
||||
inline double RequestParser::Pop() {
|
||||
const u64 value = Pop<u64>();
|
||||
float real;
|
||||
double real;
|
||||
std::memcpy(&real, &value, sizeof(real));
|
||||
return real;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ void SessionRequestHandler::ClientDisconnected(const SharedPtr<ServerSession>& s
|
||||
}
|
||||
|
||||
SharedPtr<WritableEvent> HLERequestContext::SleepClientThread(
|
||||
SharedPtr<Thread> thread, const std::string& reason, u64 timeout, WakeupCallback&& callback,
|
||||
const std::string& reason, u64 timeout, WakeupCallback&& callback,
|
||||
SharedPtr<WritableEvent> writable_event) {
|
||||
// Put the client thread to sleep until the wait event is signaled or the timeout expires.
|
||||
thread->SetWakeupCallback([context = *this, callback](
|
||||
@@ -58,7 +58,7 @@ SharedPtr<WritableEvent> HLERequestContext::SleepClientThread(
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
if (!writable_event) {
|
||||
// Create event if not provided
|
||||
const auto pair = WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
|
||||
const auto pair = WritableEvent::CreateEventPair(kernel, ResetType::Automatic,
|
||||
"HLE Pause Event: " + reason);
|
||||
writable_event = pair.writable;
|
||||
}
|
||||
@@ -76,8 +76,9 @@ SharedPtr<WritableEvent> HLERequestContext::SleepClientThread(
|
||||
return writable_event;
|
||||
}
|
||||
|
||||
HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_session)
|
||||
: server_session(std::move(server_session)) {
|
||||
HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_session,
|
||||
SharedPtr<Thread> thread)
|
||||
: server_session(std::move(server_session)), thread(std::move(thread)) {
|
||||
cmd_buf[0] = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ protected:
|
||||
*/
|
||||
class HLERequestContext {
|
||||
public:
|
||||
explicit HLERequestContext(SharedPtr<ServerSession> session);
|
||||
explicit HLERequestContext(SharedPtr<ServerSession> session, SharedPtr<Thread> thread);
|
||||
~HLERequestContext();
|
||||
|
||||
/// Returns a pointer to the IPC command buffer for this request.
|
||||
@@ -119,7 +119,6 @@ public:
|
||||
/**
|
||||
* Puts the specified guest thread to sleep until the returned event is signaled or until the
|
||||
* specified timeout expires.
|
||||
* @param thread Thread to be put to sleep.
|
||||
* @param reason Reason for pausing the thread, to be used for debugging purposes.
|
||||
* @param timeout Timeout in nanoseconds after which the thread will be awoken and the callback
|
||||
* invoked with a Timeout reason.
|
||||
@@ -130,8 +129,8 @@ public:
|
||||
* created.
|
||||
* @returns Event that when signaled will resume the thread and call the callback function.
|
||||
*/
|
||||
SharedPtr<WritableEvent> SleepClientThread(SharedPtr<Thread> thread, const std::string& reason,
|
||||
u64 timeout, WakeupCallback&& callback,
|
||||
SharedPtr<WritableEvent> SleepClientThread(const std::string& reason, u64 timeout,
|
||||
WakeupCallback&& callback,
|
||||
SharedPtr<WritableEvent> writable_event = nullptr);
|
||||
|
||||
/// Populates this context with data from the requesting process/thread.
|
||||
@@ -268,6 +267,7 @@ private:
|
||||
|
||||
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
|
||||
SharedPtr<Kernel::ServerSession> server_session;
|
||||
SharedPtr<Thread> thread;
|
||||
// TODO(yuriks): Check common usage of this and optimize size accordingly
|
||||
boost::container::small_vector<SharedPtr<Object>, 8> move_objects;
|
||||
boost::container::small_vector<SharedPtr<Object>, 8> copy_objects;
|
||||
|
||||
@@ -33,8 +33,8 @@ enum class HandleType : u32 {
|
||||
};
|
||||
|
||||
enum class ResetType {
|
||||
OneShot, ///< Reset automatically on object acquisition
|
||||
Sticky, ///< Never reset automatically
|
||||
Automatic, ///< Reset automatically on object acquisition
|
||||
Manual, ///< Never reset automatically
|
||||
};
|
||||
|
||||
class Object : NonCopyable {
|
||||
|
||||
@@ -21,8 +21,9 @@ bool ReadableEvent::ShouldWait(const Thread* thread) const {
|
||||
void ReadableEvent::Acquire(Thread* thread) {
|
||||
ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
|
||||
|
||||
if (reset_type == ResetType::OneShot)
|
||||
if (reset_type == ResetType::Automatic) {
|
||||
signaled = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ReadableEvent::Signal() {
|
||||
|
||||
@@ -130,7 +130,7 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
|
||||
// The ServerSession received a sync request, this means that there's new data available
|
||||
// from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or
|
||||
// similar.
|
||||
Kernel::HLERequestContext context(this);
|
||||
Kernel::HLERequestContext context(this, thread);
|
||||
u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress());
|
||||
context.PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
|
||||
|
||||
|
||||
+54
-47
@@ -1255,8 +1255,8 @@ static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_hand
|
||||
return vm_manager.MapCodeMemory(dst_address, src_address, size);
|
||||
}
|
||||
|
||||
ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
|
||||
u64 src_address, u64 size) {
|
||||
static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle,
|
||||
u64 dst_address, u64 src_address, u64 size) {
|
||||
LOG_DEBUG(Kernel_SVC,
|
||||
"called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, "
|
||||
"size=0x{:016X}",
|
||||
@@ -1342,7 +1342,7 @@ static void ExitProcess(Core::System& system) {
|
||||
/// Creates a new thread
|
||||
static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg,
|
||||
VAddr stack_top, u32 priority, s32 processor_id) {
|
||||
LOG_TRACE(Kernel_SVC,
|
||||
LOG_DEBUG(Kernel_SVC,
|
||||
"called entrypoint=0x{:08X}, arg=0x{:08X}, stacktop=0x{:08X}, "
|
||||
"threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}",
|
||||
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
|
||||
|
||||
/// Starts the thread for the provided handle
|
||||
static ResultCode StartThread(Core::System& system, Handle thread_handle) {
|
||||
LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
|
||||
LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
|
||||
|
||||
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
|
||||
const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
|
||||
@@ -1425,7 +1425,7 @@ static ResultCode StartThread(Core::System& system, Handle thread_handle) {
|
||||
|
||||
/// Called when a thread exits
|
||||
static void ExitThread(Core::System& system) {
|
||||
LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
|
||||
LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
|
||||
|
||||
auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
|
||||
current_thread->Stop();
|
||||
@@ -1435,7 +1435,7 @@ static void ExitThread(Core::System& system) {
|
||||
|
||||
/// Sleep the current thread
|
||||
static void SleepThread(Core::System& system, s64 nanoseconds) {
|
||||
LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds);
|
||||
LOG_DEBUG(Kernel_SVC, "called nanoseconds={}", nanoseconds);
|
||||
|
||||
enum class SleepType : s64 {
|
||||
YieldWithoutLoadBalancing = 0,
|
||||
@@ -1880,11 +1880,51 @@ static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle,
|
||||
}
|
||||
|
||||
static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, u32 core,
|
||||
u64 mask) {
|
||||
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:016X}, core=0x{:X}", thread_handle,
|
||||
mask, core);
|
||||
u64 affinity_mask) {
|
||||
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, core=0x{:X}, affinity_mask=0x{:016X}",
|
||||
thread_handle, core, affinity_mask);
|
||||
|
||||
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
|
||||
const auto* const current_process = system.Kernel().CurrentProcess();
|
||||
|
||||
if (core == static_cast<u32>(THREADPROCESSORID_IDEAL)) {
|
||||
const u8 ideal_cpu_core = current_process->GetIdealCore();
|
||||
|
||||
ASSERT(ideal_cpu_core != static_cast<u8>(THREADPROCESSORID_IDEAL));
|
||||
|
||||
// Set the target CPU to the ideal core specified by the process.
|
||||
core = ideal_cpu_core;
|
||||
affinity_mask = 1ULL << core;
|
||||
} else {
|
||||
const u64 core_mask = current_process->GetCoreMask();
|
||||
|
||||
if ((core_mask | affinity_mask) != core_mask) {
|
||||
LOG_ERROR(
|
||||
Kernel_SVC,
|
||||
"Invalid processor ID specified (core_mask=0x{:08X}, affinity_mask=0x{:016X})",
|
||||
core_mask, affinity_mask);
|
||||
return ERR_INVALID_PROCESSOR_ID;
|
||||
}
|
||||
|
||||
if (affinity_mask == 0) {
|
||||
LOG_ERROR(Kernel_SVC, "Specfified affinity mask is zero.");
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
|
||||
if (core < Core::NUM_CPU_CORES) {
|
||||
if ((affinity_mask & (1ULL << core)) == 0) {
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"Core is not enabled for the current mask, core={}, mask={:016X}", core,
|
||||
affinity_mask);
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
} else if (core != static_cast<u32>(THREADPROCESSORID_DONT_CARE) &&
|
||||
core != static_cast<u32>(THREADPROCESSORID_DONT_UPDATE)) {
|
||||
LOG_ERROR(Kernel_SVC, "Invalid processor ID specified (core={}).", core);
|
||||
return ERR_INVALID_PROCESSOR_ID;
|
||||
}
|
||||
}
|
||||
|
||||
const auto& handle_table = current_process->GetHandleTable();
|
||||
const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
|
||||
if (!thread) {
|
||||
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
|
||||
@@ -1892,40 +1932,7 @@ static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle,
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (core == static_cast<u32>(THREADPROCESSORID_IDEAL)) {
|
||||
const u8 ideal_cpu_core = thread->GetOwnerProcess()->GetIdealCore();
|
||||
|
||||
ASSERT(ideal_cpu_core != static_cast<u8>(THREADPROCESSORID_IDEAL));
|
||||
|
||||
// Set the target CPU to the ideal core specified by the process.
|
||||
core = ideal_cpu_core;
|
||||
mask = 1ULL << core;
|
||||
}
|
||||
|
||||
if (mask == 0) {
|
||||
LOG_ERROR(Kernel_SVC, "Mask is 0");
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
|
||||
/// This value is used to only change the affinity mask without changing the current ideal core.
|
||||
static constexpr u32 OnlyChangeMask = static_cast<u32>(-3);
|
||||
|
||||
if (core == OnlyChangeMask) {
|
||||
core = thread->GetIdealCore();
|
||||
} else if (core >= Core::NUM_CPU_CORES && core != static_cast<u32>(-1)) {
|
||||
LOG_ERROR(Kernel_SVC, "Invalid core specified, got {}", core);
|
||||
return ERR_INVALID_PROCESSOR_ID;
|
||||
}
|
||||
|
||||
// Error out if the input core isn't enabled in the input mask.
|
||||
if (core < Core::NUM_CPU_CORES && (mask & (1ull << core)) == 0) {
|
||||
LOG_ERROR(Kernel_SVC, "Core is not enabled for the current mask, core={}, mask={:016X}",
|
||||
core, mask);
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
|
||||
thread->ChangeCore(core, mask);
|
||||
|
||||
thread->ChangeCore(core, affinity_mask);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1980,7 +1987,7 @@ static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle
|
||||
|
||||
auto& kernel = system.Kernel();
|
||||
const auto [readable_event, writable_event] =
|
||||
WritableEvent::CreateEventPair(kernel, ResetType::Sticky, "CreateEvent");
|
||||
WritableEvent::CreateEventPair(kernel, ResetType::Manual, "CreateEvent");
|
||||
|
||||
HandleTable& handle_table = kernel.CurrentProcess()->GetHandleTable();
|
||||
|
||||
@@ -2183,8 +2190,8 @@ static ResultCode GetProcessList(Core::System& system, u32* out_num_processes,
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids,
|
||||
u32 out_thread_ids_size, Handle debug_handle) {
|
||||
static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids,
|
||||
u32 out_thread_ids_size, Handle debug_handle) {
|
||||
// TODO: Handle this case when debug events are supported.
|
||||
UNIMPLEMENTED_IF(debug_handle != InvalidHandle);
|
||||
|
||||
|
||||
@@ -30,12 +30,21 @@ enum ThreadPriority : u32 {
|
||||
};
|
||||
|
||||
enum ThreadProcessorId : s32 {
|
||||
THREADPROCESSORID_IDEAL = -2, ///< Run thread on the ideal core specified by the process.
|
||||
THREADPROCESSORID_0 = 0, ///< Run thread on core 0
|
||||
THREADPROCESSORID_1 = 1, ///< Run thread on core 1
|
||||
THREADPROCESSORID_2 = 2, ///< Run thread on core 2
|
||||
THREADPROCESSORID_3 = 3, ///< Run thread on core 3
|
||||
THREADPROCESSORID_MAX = 4, ///< Processor ID must be less than this
|
||||
/// Indicates that no particular processor core is preferred.
|
||||
THREADPROCESSORID_DONT_CARE = -1,
|
||||
|
||||
/// Run thread on the ideal core specified by the process.
|
||||
THREADPROCESSORID_IDEAL = -2,
|
||||
|
||||
/// Indicates that the preferred processor ID shouldn't be updated in
|
||||
/// a core mask setting operation.
|
||||
THREADPROCESSORID_DONT_UPDATE = -3,
|
||||
|
||||
THREADPROCESSORID_0 = 0, ///< Run thread on core 0
|
||||
THREADPROCESSORID_1 = 1, ///< Run thread on core 1
|
||||
THREADPROCESSORID_2 = 2, ///< Run thread on core 2
|
||||
THREADPROCESSORID_3 = 3, ///< Run thread on core 3
|
||||
THREADPROCESSORID_MAX = 4, ///< Processor ID must be less than this
|
||||
|
||||
/// Allowed CPU mask
|
||||
THREADPROCESSORID_DEFAULT_MASK = (1 << THREADPROCESSORID_0) | (1 << THREADPROCESSORID_1) |
|
||||
|
||||
@@ -276,7 +276,7 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
|
||||
launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
|
||||
"ISelfController:LaunchableEvent");
|
||||
}
|
||||
|
||||
@@ -442,10 +442,10 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c
|
||||
|
||||
AppletMessageQueue::AppletMessageQueue() {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
on_new_message = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
|
||||
on_new_message = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
|
||||
"AMMessageQueue:OnMessageRecieved");
|
||||
on_operation_mode_changed = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::OneShot, "AMMessageQueue:OperationModeChanged");
|
||||
kernel, Kernel::ResetType::Automatic, "AMMessageQueue:OperationModeChanged");
|
||||
}
|
||||
|
||||
AppletMessageQueue::~AppletMessageQueue() = default;
|
||||
@@ -835,6 +835,7 @@ void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_SIZE_OUT_OF_BOUNDS);
|
||||
return;
|
||||
}
|
||||
|
||||
std::memcpy(backing.buffer.data() + offset, data.data(), data.size());
|
||||
@@ -857,6 +858,7 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_SIZE_OUT_OF_BOUNDS);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.WriteBuffer(backing.buffer.data() + offset, size);
|
||||
|
||||
@@ -26,11 +26,11 @@ namespace Service::AM::Applets {
|
||||
AppletDataBroker::AppletDataBroker() {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
state_changed_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:StateChangedEvent");
|
||||
kernel, Kernel::ResetType::Manual, "ILibraryAppletAccessor:StateChangedEvent");
|
||||
pop_out_data_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:PopDataOutEvent");
|
||||
kernel, Kernel::ResetType::Manual, "ILibraryAppletAccessor:PopDataOutEvent");
|
||||
pop_interactive_out_data_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:PopInteractiveDataOutEvent");
|
||||
kernel, Kernel::ResetType::Manual, "ILibraryAppletAccessor:PopInteractiveDataOutEvent");
|
||||
}
|
||||
|
||||
AppletDataBroker::~AppletDataBroker() = default;
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/partition_filesystem.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
@@ -18,7 +17,6 @@
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/aoc/aoc_u.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
@@ -68,14 +66,22 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
aoc_change_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
|
||||
aoc_change_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
|
||||
"GetAddOnContentListChanged:Event");
|
||||
}
|
||||
|
||||
AOC_U::~AOC_U() = default;
|
||||
|
||||
void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AOC, "called");
|
||||
struct Parameters {
|
||||
u64 process_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 8);
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto params = rp.PopRaw<Parameters>();
|
||||
|
||||
LOG_DEBUG(Service_AOC, "called. process_id={}", params.process_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -94,23 +100,32 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
u32 offset;
|
||||
u32 count;
|
||||
u64 process_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 16);
|
||||
|
||||
const auto offset = rp.PopRaw<u32>();
|
||||
auto count = rp.PopRaw<u32>();
|
||||
LOG_DEBUG(Service_AOC, "called with offset={}, count={}", offset, count);
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto [offset, count, process_id] = rp.PopRaw<Parameters>();
|
||||
|
||||
LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count,
|
||||
process_id);
|
||||
|
||||
const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID();
|
||||
|
||||
std::vector<u32> out;
|
||||
for (size_t i = 0; i < add_on_content.size(); ++i) {
|
||||
if ((add_on_content[i] & DLC_BASE_TITLE_ID_MASK) == current)
|
||||
out.push_back(static_cast<u32>(add_on_content[i] & 0x7FF));
|
||||
}
|
||||
|
||||
const auto& disabled = Settings::values.disabled_addons[current];
|
||||
if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end())
|
||||
out = {};
|
||||
if (std::find(disabled.begin(), disabled.end(), "DLC") == disabled.end()) {
|
||||
for (u64 content_id : add_on_content) {
|
||||
if ((content_id & DLC_BASE_TITLE_ID_MASK) != current) {
|
||||
continue;
|
||||
}
|
||||
|
||||
out.push_back(static_cast<u32>(content_id & 0x7FF));
|
||||
}
|
||||
}
|
||||
|
||||
if (out.size() < offset) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -119,22 +134,31 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
count = static_cast<u32>(std::min<size_t>(out.size() - offset, count));
|
||||
const auto out_count = static_cast<u32>(std::min<size_t>(out.size() - offset, count));
|
||||
std::rotate(out.begin(), out.begin() + offset, out.end());
|
||||
out.resize(count);
|
||||
out.resize(out_count);
|
||||
|
||||
ctx.WriteBuffer(out);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(count);
|
||||
rb.Push(out_count);
|
||||
}
|
||||
|
||||
void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AOC, "called");
|
||||
struct Parameters {
|
||||
u64 process_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 8);
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto params = rp.PopRaw<Parameters>();
|
||||
|
||||
LOG_DEBUG(Service_AOC, "called. process_id={}", params.process_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
const auto title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
|
||||
FileSys::PatchManager pm{title_id};
|
||||
|
||||
@@ -148,10 +172,17 @@ void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void AOC_U::PrepareAddOnContent(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
s32 addon_index;
|
||||
u64 process_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 16);
|
||||
|
||||
const auto aoc_id = rp.PopRaw<u32>();
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called with aoc_id={:08X}", aoc_id);
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto [addon_index, process_id] = rp.PopRaw<Parameters>();
|
||||
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called with addon_index={}, process_id={}", addon_index,
|
||||
process_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
@@ -67,7 +67,7 @@ public:
|
||||
// This is the event handle used to check if the audio buffer was released
|
||||
auto& system = Core::System::GetInstance();
|
||||
buffer_event = Kernel::WritableEvent::CreateEventPair(
|
||||
system.Kernel(), Kernel::ResetType::Sticky, "IAudioOutBufferReleased");
|
||||
system.Kernel(), Kernel::ResetType::Manual, "IAudioOutBufferReleased");
|
||||
|
||||
stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate,
|
||||
audio_params.channel_count, std::move(unique_name),
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "audio_core/audio_renderer.h"
|
||||
#include "common/alignment.h"
|
||||
#include "common/bit_util.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
@@ -46,7 +47,7 @@ public:
|
||||
|
||||
auto& system = Core::System::GetInstance();
|
||||
system_event = Kernel::WritableEvent::CreateEventPair(
|
||||
system.Kernel(), Kernel::ResetType::Sticky, "IAudioRenderer:SystemEvent");
|
||||
system.Kernel(), Kernel::ResetType::Manual, "IAudioRenderer:SystemEvent");
|
||||
renderer = std::make_unique<AudioCore::AudioRenderer>(system.CoreTiming(), audren_params,
|
||||
system_event.writable);
|
||||
}
|
||||
@@ -178,7 +179,7 @@ public:
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
|
||||
buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
|
||||
"IAudioOutBufferReleasedEvent");
|
||||
}
|
||||
|
||||
@@ -262,64 +263,304 @@ void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
|
||||
OpenAudioRendererImpl(ctx);
|
||||
}
|
||||
|
||||
static u64 CalculateNumPerformanceEntries(const AudioCore::AudioRendererParameter& params) {
|
||||
// +1 represents the final mix.
|
||||
return u64{params.effect_count} + params.submix_count + params.sink_count + params.voice_count +
|
||||
1;
|
||||
}
|
||||
|
||||
void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
u64 buffer_sz = Common::AlignUp(4 * params.mix_buffer_count, 0x40);
|
||||
buffer_sz += params.submix_count * 1024;
|
||||
buffer_sz += 0x940 * (params.submix_count + 1);
|
||||
buffer_sz += 0x3F0 * params.voice_count;
|
||||
buffer_sz += Common::AlignUp(8 * (params.submix_count + 1), 0x10);
|
||||
buffer_sz += Common::AlignUp(8 * params.voice_count, 0x10);
|
||||
buffer_sz += Common::AlignUp(
|
||||
(0x3C0 * (params.sink_count + params.submix_count) + 4 * params.sample_count) *
|
||||
(params.mix_buffer_count + 6),
|
||||
0x40);
|
||||
// Several calculations below align the sizes being calculated
|
||||
// onto a 64 byte boundary.
|
||||
static constexpr u64 buffer_alignment_size = 64;
|
||||
|
||||
if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
|
||||
const u32 count = params.submix_count + 1;
|
||||
u64 node_count = Common::AlignUp(count, 0x40);
|
||||
const u64 node_state_buffer_sz =
|
||||
4 * (node_count * node_count) + 0xC * node_count + 2 * (node_count / 8);
|
||||
u64 edge_matrix_buffer_sz = 0;
|
||||
node_count = Common::AlignUp(count * count, 0x40);
|
||||
if (node_count >> 31 != 0) {
|
||||
edge_matrix_buffer_sz = (node_count | 7) / 8;
|
||||
} else {
|
||||
edge_matrix_buffer_sz = node_count / 8;
|
||||
// Some calculations that calculate portions of the buffer
|
||||
// that will contain information, on the other hand, align
|
||||
// the result of some of their calcularions on a 16 byte boundary.
|
||||
static constexpr u64 info_field_alignment_size = 16;
|
||||
|
||||
// Maximum detail entries that may exist at one time for performance
|
||||
// frame statistics.
|
||||
static constexpr u64 max_perf_detail_entries = 100;
|
||||
|
||||
// Size of the data structure representing the bulk of the voice-related state.
|
||||
static constexpr u64 voice_state_size = 0x100;
|
||||
|
||||
// Size of the upsampler manager data structure
|
||||
constexpr u64 upsampler_manager_size = 0x48;
|
||||
|
||||
// Calculates the part of the size that relates to mix buffers.
|
||||
const auto calculate_mix_buffer_sizes = [](const AudioCore::AudioRendererParameter& params) {
|
||||
// As of 8.0.0 this is the maximum on voice channels.
|
||||
constexpr u64 max_voice_channels = 6;
|
||||
|
||||
// The service expects the sample_count member of the parameters to either be
|
||||
// a value of 160 or 240, so the maximum sample count is assumed in order
|
||||
// to adequately handle all values at runtime.
|
||||
constexpr u64 default_max_sample_count = 240;
|
||||
|
||||
const u64 total_mix_buffers = params.mix_buffer_count + max_voice_channels;
|
||||
|
||||
u64 size = 0;
|
||||
size += total_mix_buffers * (sizeof(s32) * params.sample_count);
|
||||
size += total_mix_buffers * (sizeof(s32) * default_max_sample_count);
|
||||
size += u64{params.submix_count} + params.sink_count;
|
||||
size = Common::AlignUp(size, buffer_alignment_size);
|
||||
size += Common::AlignUp(params.unknown_30, buffer_alignment_size);
|
||||
size += Common::AlignUp(sizeof(s32) * params.mix_buffer_count, buffer_alignment_size);
|
||||
return size;
|
||||
};
|
||||
|
||||
// Calculates the portion of the size related to the mix data (and the sorting thereof).
|
||||
const auto calculate_mix_info_size = [this](const AudioCore::AudioRendererParameter& params) {
|
||||
// The size of the mixing info data structure.
|
||||
constexpr u64 mix_info_size = 0x940;
|
||||
|
||||
// Consists of total submixes with the final mix included.
|
||||
const u64 total_mix_count = u64{params.submix_count} + 1;
|
||||
|
||||
// The total number of effects that may be available to the audio renderer at any time.
|
||||
constexpr u64 max_effects = 256;
|
||||
|
||||
// Calculates the part of the size related to the audio node state.
|
||||
// This will only be used if the audio revision supports the splitter.
|
||||
const auto calculate_node_state_size = [](std::size_t num_nodes) {
|
||||
// Internally within a nodestate, it appears to use a data structure
|
||||
// similar to a std::bitset<64> twice.
|
||||
constexpr u64 bit_size = Common::BitSize<u64>();
|
||||
constexpr u64 num_bitsets = 2;
|
||||
|
||||
// Node state instances have three states internally for performing
|
||||
// depth-first searches of nodes. Initialized, Found, and Done Sorting.
|
||||
constexpr u64 num_states = 3;
|
||||
|
||||
u64 size = 0;
|
||||
size += (num_nodes * num_nodes) * sizeof(s32);
|
||||
size += num_states * (num_nodes * sizeof(s32));
|
||||
size += num_bitsets * (Common::AlignUp(num_nodes, bit_size) / Common::BitSize<u8>());
|
||||
return size;
|
||||
};
|
||||
|
||||
// Calculates the part of the size related to the adjacency (aka edge) matrix.
|
||||
const auto calculate_edge_matrix_size = [](std::size_t num_nodes) {
|
||||
return (num_nodes * num_nodes) * sizeof(s32);
|
||||
};
|
||||
|
||||
u64 size = 0;
|
||||
size += Common::AlignUp(sizeof(void*) * total_mix_count, info_field_alignment_size);
|
||||
size += Common::AlignUp(mix_info_size * total_mix_count, info_field_alignment_size);
|
||||
size += Common::AlignUp(sizeof(s32) * max_effects * params.submix_count,
|
||||
info_field_alignment_size);
|
||||
|
||||
if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
|
||||
size += Common::AlignUp(calculate_node_state_size(total_mix_count) +
|
||||
calculate_edge_matrix_size(total_mix_count),
|
||||
info_field_alignment_size);
|
||||
}
|
||||
buffer_sz += Common::AlignUp(node_state_buffer_sz + edge_matrix_buffer_sz, 0x10);
|
||||
}
|
||||
|
||||
buffer_sz += 0x20 * (params.effect_count + 4 * params.voice_count) + 0x50;
|
||||
if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
|
||||
buffer_sz += 0xE0 * params.num_splitter_send_channels;
|
||||
buffer_sz += 0x20 * params.splitter_count;
|
||||
buffer_sz += Common::AlignUp(4 * params.num_splitter_send_channels, 0x10);
|
||||
}
|
||||
buffer_sz = Common::AlignUp(buffer_sz, 0x40) + 0x170 * params.sink_count;
|
||||
u64 output_sz = buffer_sz + 0x280 * params.sink_count + 0x4B0 * params.effect_count +
|
||||
((params.voice_count * 256) | 0x40);
|
||||
return size;
|
||||
};
|
||||
|
||||
if (params.performance_frame_count >= 1) {
|
||||
output_sz = Common::AlignUp(((16 * params.sink_count + 16 * params.effect_count +
|
||||
16 * params.voice_count + 16) +
|
||||
0x658) *
|
||||
(params.performance_frame_count + 1) +
|
||||
0xc0,
|
||||
0x40) +
|
||||
output_sz;
|
||||
}
|
||||
output_sz = Common::AlignUp(output_sz + 0x1807e, 0x1000);
|
||||
// Calculates the part of the size related to voice channel info.
|
||||
const auto calculate_voice_info_size = [](const AudioCore::AudioRendererParameter& params) {
|
||||
constexpr u64 voice_info_size = 0x220;
|
||||
constexpr u64 voice_resource_size = 0xD0;
|
||||
|
||||
u64 size = 0;
|
||||
size += Common::AlignUp(sizeof(void*) * params.voice_count, info_field_alignment_size);
|
||||
size += Common::AlignUp(voice_info_size * params.voice_count, info_field_alignment_size);
|
||||
size +=
|
||||
Common::AlignUp(voice_resource_size * params.voice_count, info_field_alignment_size);
|
||||
size += Common::AlignUp(voice_state_size * params.voice_count, info_field_alignment_size);
|
||||
return size;
|
||||
};
|
||||
|
||||
// Calculates the part of the size related to memory pools.
|
||||
const auto calculate_memory_pools_size = [](const AudioCore::AudioRendererParameter& params) {
|
||||
const u64 num_memory_pools = sizeof(s32) * (u64{params.effect_count} + params.voice_count);
|
||||
const u64 memory_pool_info_size = 0x20;
|
||||
return Common::AlignUp(num_memory_pools * memory_pool_info_size, info_field_alignment_size);
|
||||
};
|
||||
|
||||
// Calculates the part of the size related to the splitter context.
|
||||
const auto calculate_splitter_context_size =
|
||||
[this](const AudioCore::AudioRendererParameter& params) -> u64 {
|
||||
if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
constexpr u64 splitter_info_size = 0x20;
|
||||
constexpr u64 splitter_destination_data_size = 0xE0;
|
||||
|
||||
u64 size = 0;
|
||||
size += params.num_splitter_send_channels;
|
||||
size +=
|
||||
Common::AlignUp(splitter_info_size * params.splitter_count, info_field_alignment_size);
|
||||
size += Common::AlignUp(splitter_destination_data_size * params.num_splitter_send_channels,
|
||||
info_field_alignment_size);
|
||||
|
||||
return size;
|
||||
};
|
||||
|
||||
// Calculates the part of the size related to the upsampler info.
|
||||
const auto calculate_upsampler_info_size = [](const AudioCore::AudioRendererParameter& params) {
|
||||
constexpr u64 upsampler_info_size = 0x280;
|
||||
// Yes, using the buffer size over info alignment size is intentional here.
|
||||
return Common::AlignUp(upsampler_info_size * (u64{params.submix_count} + params.sink_count),
|
||||
buffer_alignment_size);
|
||||
};
|
||||
|
||||
// Calculates the part of the size related to effect info.
|
||||
const auto calculate_effect_info_size = [](const AudioCore::AudioRendererParameter& params) {
|
||||
constexpr u64 effect_info_size = 0x2B0;
|
||||
return Common::AlignUp(effect_info_size * params.effect_count, info_field_alignment_size);
|
||||
};
|
||||
|
||||
// Calculates the part of the size related to audio sink info.
|
||||
const auto calculate_sink_info_size = [](const AudioCore::AudioRendererParameter& params) {
|
||||
const u64 sink_info_size = 0x170;
|
||||
return Common::AlignUp(sink_info_size * params.sink_count, info_field_alignment_size);
|
||||
};
|
||||
|
||||
// Calculates the part of the size related to voice state info.
|
||||
const auto calculate_voice_state_size = [](const AudioCore::AudioRendererParameter& params) {
|
||||
const u64 voice_state_size = 0x100;
|
||||
const u64 additional_size = buffer_alignment_size - 1;
|
||||
return Common::AlignUp(voice_state_size * params.voice_count + additional_size,
|
||||
info_field_alignment_size);
|
||||
};
|
||||
|
||||
// Calculates the part of the size related to performance statistics.
|
||||
const auto calculate_perf_size = [this](const AudioCore::AudioRendererParameter& params) {
|
||||
// Extra size value appended to the end of the calculation.
|
||||
constexpr u64 appended = 128;
|
||||
|
||||
// Whether or not we assume the newer version of performance metrics data structures.
|
||||
const bool is_v2 =
|
||||
IsFeatureSupported(AudioFeatures::PerformanceMetricsVersion2, params.revision);
|
||||
|
||||
// Data structure sizes
|
||||
constexpr u64 perf_statistics_size = 0x0C;
|
||||
const u64 header_size = is_v2 ? 0x30 : 0x18;
|
||||
const u64 entry_size = is_v2 ? 0x18 : 0x10;
|
||||
const u64 detail_size = is_v2 ? 0x18 : 0x10;
|
||||
|
||||
const u64 entry_count = CalculateNumPerformanceEntries(params);
|
||||
const u64 size_per_frame =
|
||||
header_size + (entry_size * entry_count) + (detail_size * max_perf_detail_entries);
|
||||
|
||||
u64 size = 0;
|
||||
size += Common::AlignUp(size_per_frame * params.performance_frame_count + 1,
|
||||
buffer_alignment_size);
|
||||
size += Common::AlignUp(perf_statistics_size, buffer_alignment_size);
|
||||
size += appended;
|
||||
return size;
|
||||
};
|
||||
|
||||
// Calculates the part of the size that relates to the audio command buffer.
|
||||
const auto calculate_command_buffer_size =
|
||||
[this](const AudioCore::AudioRendererParameter& params) {
|
||||
constexpr u64 alignment = (buffer_alignment_size - 1) * 2;
|
||||
|
||||
if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) {
|
||||
constexpr u64 command_buffer_size = 0x18000;
|
||||
|
||||
return command_buffer_size + alignment;
|
||||
}
|
||||
|
||||
// When the variadic command buffer is supported, this means
|
||||
// the command generator for the audio renderer can issue commands
|
||||
// that are (as one would expect), variable in size. So what we need to do
|
||||
// is determine the maximum possible size for a few command data structures
|
||||
// then multiply them by the amount of present commands indicated by the given
|
||||
// respective audio parameters.
|
||||
|
||||
constexpr u64 max_biquad_filters = 2;
|
||||
constexpr u64 max_mix_buffers = 24;
|
||||
|
||||
constexpr u64 biquad_filter_command_size = 0x2C;
|
||||
|
||||
constexpr u64 depop_mix_command_size = 0x24;
|
||||
constexpr u64 depop_setup_command_size = 0x50;
|
||||
|
||||
constexpr u64 effect_command_max_size = 0x540;
|
||||
|
||||
constexpr u64 mix_command_size = 0x1C;
|
||||
constexpr u64 mix_ramp_command_size = 0x24;
|
||||
constexpr u64 mix_ramp_grouped_command_size = 0x13C;
|
||||
|
||||
constexpr u64 perf_command_size = 0x28;
|
||||
|
||||
constexpr u64 sink_command_size = 0x130;
|
||||
|
||||
constexpr u64 submix_command_max_size =
|
||||
depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers;
|
||||
|
||||
constexpr u64 volume_command_size = 0x1C;
|
||||
constexpr u64 volume_ramp_command_size = 0x20;
|
||||
|
||||
constexpr u64 voice_biquad_filter_command_size =
|
||||
biquad_filter_command_size * max_biquad_filters;
|
||||
constexpr u64 voice_data_command_size = 0x9C;
|
||||
const u64 voice_command_max_size =
|
||||
(params.splitter_count * depop_setup_command_size) +
|
||||
(voice_data_command_size + voice_biquad_filter_command_size +
|
||||
volume_ramp_command_size + mix_ramp_grouped_command_size);
|
||||
|
||||
// Now calculate the individual elements that comprise the size and add them together.
|
||||
const u64 effect_commands_size = params.effect_count * effect_command_max_size;
|
||||
|
||||
const u64 final_mix_commands_size =
|
||||
depop_mix_command_size + volume_command_size * max_mix_buffers;
|
||||
|
||||
const u64 perf_commands_size =
|
||||
perf_command_size *
|
||||
(CalculateNumPerformanceEntries(params) + max_perf_detail_entries);
|
||||
|
||||
const u64 sink_commands_size = params.sink_count * sink_command_size;
|
||||
|
||||
const u64 splitter_commands_size =
|
||||
params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size;
|
||||
|
||||
const u64 submix_commands_size = params.submix_count * submix_command_max_size;
|
||||
|
||||
const u64 voice_commands_size = params.voice_count * voice_command_max_size;
|
||||
|
||||
return effect_commands_size + final_mix_commands_size + perf_commands_size +
|
||||
sink_commands_size + splitter_commands_size + submix_commands_size +
|
||||
voice_commands_size + alignment;
|
||||
};
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
|
||||
|
||||
u64 size = 0;
|
||||
size += calculate_mix_buffer_sizes(params);
|
||||
size += calculate_mix_info_size(params);
|
||||
size += calculate_voice_info_size(params);
|
||||
size += upsampler_manager_size;
|
||||
size += calculate_memory_pools_size(params);
|
||||
size += calculate_splitter_context_size(params);
|
||||
|
||||
size = Common::AlignUp(size, buffer_alignment_size);
|
||||
|
||||
size += calculate_upsampler_info_size(params);
|
||||
size += calculate_effect_info_size(params);
|
||||
size += calculate_sink_info_size(params);
|
||||
size += calculate_voice_state_size(params);
|
||||
size += calculate_perf_size(params);
|
||||
size += calculate_command_buffer_size(params);
|
||||
|
||||
// finally, 4KB page align the size, and we're done.
|
||||
size = Common::AlignUp(size, 4096);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u64>(output_sz);
|
||||
rb.Push<u64>(size);
|
||||
|
||||
LOG_DEBUG(Service_Audio, "buffer_size=0x{:X}", output_sz);
|
||||
LOG_DEBUG(Service_Audio, "buffer_size=0x{:X}", size);
|
||||
}
|
||||
|
||||
void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) {
|
||||
@@ -357,10 +598,15 @@ void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
bool AudRenU::IsFeatureSupported(AudioFeatures feature, u32_le revision) const {
|
||||
u32_be version_num = (revision - Common::MakeMagic('R', 'E', 'V', '0')); // Byte swap
|
||||
// Byte swap
|
||||
const u32_be version_num = revision - Common::MakeMagic('R', 'E', 'V', '0');
|
||||
|
||||
switch (feature) {
|
||||
case AudioFeatures::Splitter:
|
||||
return version_num >= 2u;
|
||||
return version_num >= 2U;
|
||||
case AudioFeatures::PerformanceMetricsVersion2:
|
||||
case AudioFeatures::VariadicCommandBuffer:
|
||||
return version_num >= 5U;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ private:
|
||||
|
||||
enum class AudioFeatures : u32 {
|
||||
Splitter,
|
||||
PerformanceMetricsVersion2,
|
||||
VariadicCommandBuffer,
|
||||
};
|
||||
|
||||
bool IsFeatureSupported(AudioFeatures feature, u32_le revision) const;
|
||||
|
||||
@@ -34,8 +34,8 @@ public:
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
register_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
|
||||
"BT:RegisterEvent");
|
||||
register_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Automatic, "BT:RegisterEvent");
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -57,13 +57,13 @@ public:
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
scan_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
|
||||
scan_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
|
||||
"IBtmUserCore:ScanEvent");
|
||||
connection_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::OneShot, "IBtmUserCore:ConnectionEvent");
|
||||
kernel, Kernel::ResetType::Automatic, "IBtmUserCore:ConnectionEvent");
|
||||
service_discovery = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::OneShot, "IBtmUserCore:Discovery");
|
||||
config_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
|
||||
kernel, Kernel::ResetType::Automatic, "IBtmUserCore:Discovery");
|
||||
config_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
|
||||
"IBtmUserCore:ConfigEvent");
|
||||
}
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
|
||||
void Controller_NPad::OnInit() {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
styleset_changed_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged");
|
||||
kernel, Kernel::ResetType::Automatic, "npad:NpadStyleSetChanged");
|
||||
|
||||
if (!IsControllerActivated()) {
|
||||
return;
|
||||
|
||||
@@ -26,7 +26,7 @@ constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152);
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
|
||||
: ServiceFramework(name), module(std::move(module)) {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
nfc_tag_load = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
|
||||
nfc_tag_load = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
|
||||
"IUser:NFCTagDetected");
|
||||
}
|
||||
|
||||
@@ -67,9 +67,9 @@ public:
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
deactivate_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::OneShot, "IUser:DeactivateEvent");
|
||||
kernel, Kernel::ResetType::Automatic, "IUser:DeactivateEvent");
|
||||
availability_change_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::OneShot, "IUser:AvailabilityChangeEvent");
|
||||
kernel, Kernel::ResetType::Automatic, "IUser:AvailabilityChangeEvent");
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -62,9 +62,9 @@ public:
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
event1 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
|
||||
event1 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
|
||||
"IRequest:Event1");
|
||||
event2 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
|
||||
event2 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
|
||||
"IRequest:Event2");
|
||||
}
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@ public:
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
finished_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::OneShot,
|
||||
kernel, Kernel::ResetType::Automatic,
|
||||
"IEnsureNetworkClockAvailabilityService:FinishEvent");
|
||||
}
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
query_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
|
||||
query_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
|
||||
"NVDRV::query_event");
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Service::NVFlinger {
|
||||
|
||||
BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
buffer_wait_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
|
||||
buffer_wait_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
|
||||
"BufferQueue NativeHandle");
|
||||
}
|
||||
|
||||
|
||||
@@ -2,16 +2,15 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/client_session.h"
|
||||
#include "core/hle/service/set/set.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Service::Set {
|
||||
|
||||
namespace {
|
||||
constexpr std::array<LanguageCode, 17> available_language_codes = {{
|
||||
LanguageCode::JA,
|
||||
LanguageCode::EN_US,
|
||||
@@ -32,41 +31,35 @@ constexpr std::array<LanguageCode, 17> available_language_codes = {{
|
||||
LanguageCode::ZH_HANT,
|
||||
}};
|
||||
|
||||
constexpr std::size_t pre4_0_0_max_entries = 0xF;
|
||||
constexpr std::size_t post4_0_0_max_entries = 0x40;
|
||||
constexpr std::size_t pre4_0_0_max_entries = 15;
|
||||
constexpr std::size_t post4_0_0_max_entries = 17;
|
||||
|
||||
constexpr ResultCode ERR_INVALID_LANGUAGE{ErrorModule::Settings, 625};
|
||||
|
||||
void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, std::size_t num_language_codes) {
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(static_cast<u32>(num_language_codes));
|
||||
}
|
||||
|
||||
void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t max_size) {
|
||||
const std::size_t requested_amount = ctx.GetWriteBufferSize() / sizeof(LanguageCode);
|
||||
const std::size_t copy_amount = std::min(requested_amount, max_size);
|
||||
const std::size_t copy_size = copy_amount * sizeof(LanguageCode);
|
||||
|
||||
ctx.WriteBuffer(available_language_codes.data(), copy_size);
|
||||
PushResponseLanguageCode(ctx, copy_amount);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
LanguageCode GetLanguageCodeFromIndex(std::size_t index) {
|
||||
return available_language_codes.at(index);
|
||||
}
|
||||
|
||||
template <std::size_t size>
|
||||
static std::array<LanguageCode, size> MakeLanguageCodeSubset() {
|
||||
std::array<LanguageCode, size> arr;
|
||||
std::copy_n(available_language_codes.begin(), size, arr.begin());
|
||||
return arr;
|
||||
}
|
||||
|
||||
static void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, std::size_t max_size) {
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
if (available_language_codes.size() > max_size) {
|
||||
rb.Push(static_cast<u32>(max_size));
|
||||
} else {
|
||||
rb.Push(static_cast<u32>(available_language_codes.size()));
|
||||
}
|
||||
}
|
||||
|
||||
void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_SET, "called");
|
||||
|
||||
if (available_language_codes.size() > pre4_0_0_max_entries) {
|
||||
ctx.WriteBuffer(MakeLanguageCodeSubset<pre4_0_0_max_entries>());
|
||||
} else {
|
||||
ctx.WriteBuffer(available_language_codes);
|
||||
}
|
||||
PushResponseLanguageCode(ctx, pre4_0_0_max_entries);
|
||||
GetAvailableLanguageCodesImpl(ctx, pre4_0_0_max_entries);
|
||||
}
|
||||
|
||||
void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) {
|
||||
@@ -87,12 +80,7 @@ void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) {
|
||||
void SET::GetAvailableLanguageCodes2(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_SET, "called");
|
||||
|
||||
if (available_language_codes.size() > post4_0_0_max_entries) {
|
||||
ctx.WriteBuffer(MakeLanguageCodeSubset<post4_0_0_max_entries>());
|
||||
} else {
|
||||
ctx.WriteBuffer(available_language_codes);
|
||||
}
|
||||
PushResponseLanguageCode(ctx, post4_0_0_max_entries);
|
||||
GetAvailableLanguageCodesImpl(ctx, post4_0_0_max_entries);
|
||||
}
|
||||
|
||||
void SET::GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx) {
|
||||
@@ -102,9 +90,9 @@ void SET::GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void SET::GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx) {
|
||||
PushResponseLanguageCode(ctx, post4_0_0_max_entries);
|
||||
|
||||
LOG_DEBUG(Service_SET, "called");
|
||||
|
||||
PushResponseLanguageCode(ctx, post4_0_0_max_entries);
|
||||
}
|
||||
|
||||
void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Service::VI {
|
||||
|
||||
Display::Display(u64 id, std::string name) : id{id}, name{std::move(name)} {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
|
||||
vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
|
||||
fmt::format("Display VSync Event {}", id));
|
||||
}
|
||||
|
||||
|
||||
@@ -556,7 +556,7 @@ private:
|
||||
} else {
|
||||
// Wait the current thread until a buffer becomes available
|
||||
ctx.SleepClientThread(
|
||||
Kernel::GetCurrentThread(), "IHOSBinderDriver::DequeueBuffer", -1,
|
||||
"IHOSBinderDriver::DequeueBuffer", -1,
|
||||
[=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
|
||||
Kernel::ThreadWakeupReason reason) {
|
||||
// Repeat TransactParcel DequeueBuffer when a buffer is available
|
||||
|
||||
@@ -143,9 +143,9 @@ std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& g
|
||||
std::lock_guard lock{joystick_map_mutex};
|
||||
const auto it = joystick_map.find(guid);
|
||||
if (it != joystick_map.end()) {
|
||||
while (it->second.size() <= port) {
|
||||
auto joystick = std::make_shared<SDLJoystick>(guid, it->second.size(), nullptr,
|
||||
[](SDL_Joystick*) {});
|
||||
while (it->second.size() <= static_cast<std::size_t>(port)) {
|
||||
auto joystick = std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()),
|
||||
nullptr, [](SDL_Joystick*) {});
|
||||
it->second.emplace_back(std::move(joystick));
|
||||
}
|
||||
return it->second[port];
|
||||
|
||||
@@ -40,6 +40,13 @@ bool DmaPusher::Step() {
|
||||
}
|
||||
|
||||
const CommandList& command_list{dma_pushbuffer.front()};
|
||||
ASSERT_OR_EXECUTE(!command_list.empty(), {
|
||||
// Somehow the command_list is empty, in order to avoid a crash
|
||||
// We ignore it and assume its size is 0.
|
||||
dma_pushbuffer.pop();
|
||||
dma_pushbuffer_subindex = 0;
|
||||
return true;
|
||||
});
|
||||
const CommandListHeader command_list_header{command_list[dma_pushbuffer_subindex++]};
|
||||
GPUVAddr dma_get = command_list_header.addr;
|
||||
GPUVAddr dma_put = dma_get + command_list_header.size * sizeof(u32);
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "video_core/engines/engine_upload.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
@@ -10,7 +12,9 @@
|
||||
namespace Tegra::Engines::Upload {
|
||||
|
||||
State::State(MemoryManager& memory_manager, Registers& regs)
|
||||
: memory_manager(memory_manager), regs(regs) {}
|
||||
: regs{regs}, memory_manager{memory_manager} {}
|
||||
|
||||
State::~State() = default;
|
||||
|
||||
void State::ProcessExec(const bool is_linear) {
|
||||
write_offset = 0;
|
||||
|
||||
@@ -4,10 +4,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Tegra {
|
||||
@@ -57,10 +55,10 @@ struct Registers {
|
||||
class State {
|
||||
public:
|
||||
State(MemoryManager& memory_manager, Registers& regs);
|
||||
~State() = default;
|
||||
~State();
|
||||
|
||||
void ProcessExec(const bool is_linear);
|
||||
void ProcessData(const u32 data, const bool is_last_call);
|
||||
void ProcessExec(bool is_linear);
|
||||
void ProcessData(u32 data, bool is_last_call);
|
||||
|
||||
private:
|
||||
u32 write_offset = 0;
|
||||
|
||||
@@ -34,9 +34,9 @@ void Maxwell3D::InitializeRegisterDefaults() {
|
||||
|
||||
// Depth range near/far is not always set, but is expected to be the default 0.0f, 1.0f. This is
|
||||
// needed for ARMS.
|
||||
for (std::size_t viewport{}; viewport < Regs::NumViewports; ++viewport) {
|
||||
regs.viewports[viewport].depth_range_near = 0.0f;
|
||||
regs.viewports[viewport].depth_range_far = 1.0f;
|
||||
for (auto& viewport : regs.viewports) {
|
||||
viewport.depth_range_near = 0.0f;
|
||||
viewport.depth_range_far = 1.0f;
|
||||
}
|
||||
|
||||
// Doom and Bomberman seems to use the uninitialized registers and just enable blend
|
||||
@@ -47,13 +47,13 @@ void Maxwell3D::InitializeRegisterDefaults() {
|
||||
regs.blend.equation_a = Regs::Blend::Equation::Add;
|
||||
regs.blend.factor_source_a = Regs::Blend::Factor::One;
|
||||
regs.blend.factor_dest_a = Regs::Blend::Factor::Zero;
|
||||
for (std::size_t blend_index = 0; blend_index < Regs::NumRenderTargets; blend_index++) {
|
||||
regs.independent_blend[blend_index].equation_rgb = Regs::Blend::Equation::Add;
|
||||
regs.independent_blend[blend_index].factor_source_rgb = Regs::Blend::Factor::One;
|
||||
regs.independent_blend[blend_index].factor_dest_rgb = Regs::Blend::Factor::Zero;
|
||||
regs.independent_blend[blend_index].equation_a = Regs::Blend::Equation::Add;
|
||||
regs.independent_blend[blend_index].factor_source_a = Regs::Blend::Factor::One;
|
||||
regs.independent_blend[blend_index].factor_dest_a = Regs::Blend::Factor::Zero;
|
||||
for (auto& blend : regs.independent_blend) {
|
||||
blend.equation_rgb = Regs::Blend::Equation::Add;
|
||||
blend.factor_source_rgb = Regs::Blend::Factor::One;
|
||||
blend.factor_dest_rgb = Regs::Blend::Factor::Zero;
|
||||
blend.equation_a = Regs::Blend::Equation::Add;
|
||||
blend.factor_source_a = Regs::Blend::Factor::One;
|
||||
blend.factor_dest_a = Regs::Blend::Factor::Zero;
|
||||
}
|
||||
regs.stencil_front_op_fail = Regs::StencilOp::Keep;
|
||||
regs.stencil_front_op_zfail = Regs::StencilOp::Keep;
|
||||
@@ -75,11 +75,11 @@ void Maxwell3D::InitializeRegisterDefaults() {
|
||||
|
||||
// TODO(bunnei): Some games do not initialize the color masks (e.g. Sonic Mania). Assuming a
|
||||
// default of enabled fixes rendering here.
|
||||
for (std::size_t color_mask = 0; color_mask < Regs::NumRenderTargets; color_mask++) {
|
||||
regs.color_mask[color_mask].R.Assign(1);
|
||||
regs.color_mask[color_mask].G.Assign(1);
|
||||
regs.color_mask[color_mask].B.Assign(1);
|
||||
regs.color_mask[color_mask].A.Assign(1);
|
||||
for (auto& color_mask : regs.color_mask) {
|
||||
color_mask.R.Assign(1);
|
||||
color_mask.G.Assign(1);
|
||||
color_mask.B.Assign(1);
|
||||
color_mask.A.Assign(1);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
||||
// Vertex buffer
|
||||
if (method >= MAXWELL3D_REG_INDEX(vertex_array) &&
|
||||
method < MAXWELL3D_REG_INDEX(vertex_array) + 4 * 32) {
|
||||
method < MAXWELL3D_REG_INDEX(vertex_array) + 4 * Regs::NumVertexArrays) {
|
||||
dirty_flags.vertex_array.set((method - MAXWELL3D_REG_INDEX(vertex_array)) >> 2);
|
||||
} else if (method >= MAXWELL3D_REG_INDEX(vertex_array_limit) &&
|
||||
method < MAXWELL3D_REG_INDEX(vertex_array_limit) + 2 * 32) {
|
||||
method < MAXWELL3D_REG_INDEX(vertex_array_limit) + 2 * Regs::NumVertexArrays) {
|
||||
dirty_flags.vertex_array.set((method - MAXWELL3D_REG_INDEX(vertex_array_limit)) >> 1);
|
||||
} else if (method >= MAXWELL3D_REG_INDEX(instanced_arrays) &&
|
||||
method < MAXWELL3D_REG_INDEX(instanced_arrays) + 32) {
|
||||
method < MAXWELL3D_REG_INDEX(instanced_arrays) + Regs::NumVertexArrays) {
|
||||
dirty_flags.vertex_array.set(method - MAXWELL3D_REG_INDEX(instanced_arrays));
|
||||
}
|
||||
}
|
||||
@@ -442,7 +442,7 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
|
||||
const auto a_type = tic_entry.a_type.Value();
|
||||
|
||||
// TODO(Subv): Different data types for separate components are not supported
|
||||
ASSERT(r_type == g_type && r_type == b_type && r_type == a_type);
|
||||
DEBUG_ASSERT(r_type == g_type && r_type == b_type && r_type == a_type);
|
||||
|
||||
return tic_entry;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
@@ -58,6 +59,7 @@ public:
|
||||
static constexpr std::size_t NumCBData = 16;
|
||||
static constexpr std::size_t NumVertexArrays = 32;
|
||||
static constexpr std::size_t NumVertexAttributes = 32;
|
||||
static constexpr std::size_t NumVaryings = 31;
|
||||
static constexpr std::size_t NumTextureSamplers = 32;
|
||||
static constexpr std::size_t NumClipDistances = 8;
|
||||
static constexpr std::size_t MaxShaderProgram = 6;
|
||||
@@ -1107,6 +1109,7 @@ public:
|
||||
} regs{};
|
||||
|
||||
static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), "Maxwell3D Regs has wrong size");
|
||||
static_assert(std::is_trivially_copyable_v<Regs>, "Maxwell3D Regs must be trivially copyable");
|
||||
|
||||
struct State {
|
||||
struct ConstBufferInfo {
|
||||
|
||||
@@ -98,6 +98,10 @@ union Attribute {
|
||||
BitField<22, 2, u64> element;
|
||||
BitField<24, 6, Index> index;
|
||||
BitField<47, 3, AttributeSize> size;
|
||||
|
||||
bool IsPhysical() const {
|
||||
return element == 0 && static_cast<u64>(index.Value()) == 0;
|
||||
}
|
||||
} fmt20;
|
||||
|
||||
union {
|
||||
@@ -499,6 +503,11 @@ enum class SystemVariable : u64 {
|
||||
CircularQueueEntryAddressHigh = 0x63,
|
||||
};
|
||||
|
||||
enum class PhysicalAttributeDirection : u64 {
|
||||
Input = 0,
|
||||
Output = 1,
|
||||
};
|
||||
|
||||
union Instruction {
|
||||
Instruction& operator=(const Instruction& instr) {
|
||||
value = instr.value;
|
||||
@@ -520,6 +529,11 @@ union Instruction {
|
||||
BitField<39, 8, Register> gpr39;
|
||||
BitField<48, 16, u64> opcode;
|
||||
|
||||
union {
|
||||
BitField<8, 8, Register> gpr;
|
||||
BitField<20, 24, s64> offset;
|
||||
} gmem;
|
||||
|
||||
union {
|
||||
BitField<20, 16, u64> imm20_16;
|
||||
BitField<20, 19, u64> imm20_19;
|
||||
@@ -587,6 +601,7 @@ union Instruction {
|
||||
} alu;
|
||||
|
||||
union {
|
||||
BitField<38, 1, u64> idx;
|
||||
BitField<51, 1, u64> saturate;
|
||||
BitField<52, 2, IpaSampleMode> sample_mode;
|
||||
BitField<54, 2, IpaInterpMode> interp_mode;
|
||||
@@ -802,15 +817,24 @@ union Instruction {
|
||||
union {
|
||||
BitField<48, 3, UniformType> type;
|
||||
BitField<46, 2, u64> cache_mode;
|
||||
BitField<20, 24, s64> immediate_offset;
|
||||
} ldg;
|
||||
|
||||
union {
|
||||
BitField<48, 3, UniformType> type;
|
||||
BitField<46, 2, u64> cache_mode;
|
||||
BitField<20, 24, s64> immediate_offset;
|
||||
} stg;
|
||||
|
||||
union {
|
||||
BitField<32, 1, PhysicalAttributeDirection> direction;
|
||||
BitField<47, 3, AttributeSize> size;
|
||||
BitField<20, 11, u64> address;
|
||||
} al2p;
|
||||
|
||||
union {
|
||||
BitField<53, 3, UniformType> type;
|
||||
BitField<52, 1, u64> extended;
|
||||
} generic;
|
||||
|
||||
union {
|
||||
BitField<0, 3, u64> pred0;
|
||||
BitField<3, 3, u64> pred3;
|
||||
@@ -1371,11 +1395,14 @@ public:
|
||||
LD_L,
|
||||
LD_S,
|
||||
LD_C,
|
||||
LD, // Load from generic memory
|
||||
LDG, // Load from global memory
|
||||
ST_A,
|
||||
ST_L,
|
||||
ST_S,
|
||||
LDG, // Load from global memory
|
||||
STG, // Store in global memory
|
||||
ST, // Store in generic memory
|
||||
STG, // Store in global memory
|
||||
AL2P, // Transforms attribute memory into physical memory
|
||||
TEX,
|
||||
TEX_B, // Texture Load Bindless
|
||||
TXQ, // Texture Query
|
||||
@@ -1641,11 +1668,14 @@ private:
|
||||
INST("1110111101001---", Id::LD_S, Type::Memory, "LD_S"),
|
||||
INST("1110111101000---", Id::LD_L, Type::Memory, "LD_L"),
|
||||
INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"),
|
||||
INST("100-------------", Id::LD, Type::Memory, "LD"),
|
||||
INST("1110111011010---", Id::LDG, Type::Memory, "LDG"),
|
||||
INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"),
|
||||
INST("1110111101011---", Id::ST_S, Type::Memory, "ST_S"),
|
||||
INST("1110111101010---", Id::ST_L, Type::Memory, "ST_L"),
|
||||
INST("1110111011010---", Id::LDG, Type::Memory, "LDG"),
|
||||
INST("101-------------", Id::ST, Type::Memory, "ST"),
|
||||
INST("1110111011011---", Id::STG, Type::Memory, "STG"),
|
||||
INST("1110111110100---", Id::AL2P, Type::Memory, "AL2P"),
|
||||
INST("110000----111---", Id::TEX, Type::Texture, "TEX"),
|
||||
INST("1101111010111---", Id::TEX_B, Type::Texture, "TEX_B"),
|
||||
INST("1101111101001---", Id::TXQ, Type::Texture, "TXQ"),
|
||||
|
||||
@@ -118,7 +118,7 @@ void SynchState::WaitForSynchronization(u64 fence) {
|
||||
// Wait for the GPU to be idle (all commands to be executed)
|
||||
{
|
||||
MICROPROFILE_SCOPE(GPU_wait);
|
||||
std::unique_lock<std::mutex> lock{synchronization_mutex};
|
||||
std::unique_lock lock{synchronization_mutex};
|
||||
synchronization_condition.wait(lock, [this, fence] { return signaled_fence >= fence; });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,12 +81,6 @@ struct CommandDataContainer {
|
||||
CommandDataContainer(CommandData&& data, u64 next_fence)
|
||||
: data{std::move(data)}, fence{next_fence} {}
|
||||
|
||||
CommandDataContainer& operator=(const CommandDataContainer& t) {
|
||||
data = std::move(t.data);
|
||||
fence = t.fence;
|
||||
return *this;
|
||||
}
|
||||
|
||||
CommandData data;
|
||||
u64 fence{};
|
||||
};
|
||||
@@ -109,7 +103,7 @@ struct SynchState final {
|
||||
|
||||
void TrySynchronize() {
|
||||
if (IsSynchronized()) {
|
||||
std::lock_guard<std::mutex> lock{synchronization_mutex};
|
||||
std::lock_guard lock{synchronization_mutex};
|
||||
synchronization_condition.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,10 +118,12 @@ bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) {
|
||||
static_cast<u32>(opcode.operation.Value()));
|
||||
}
|
||||
|
||||
// An instruction with the Exit flag will not actually
|
||||
// cause an exit if it's executed inside a delay slot.
|
||||
// TODO(Blinkhawk): Reversed to always exit. The behavior explained above requires further
|
||||
// testing on the MME code.
|
||||
if (opcode.is_exit) {
|
||||
// Exit has a delay slot, execute the next instruction
|
||||
// Note: Executing an exit during a branch delay slot will cause the instruction at the
|
||||
// branch target to be executed before exiting.
|
||||
Step(offset, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -144,8 +144,9 @@ protected:
|
||||
|
||||
object->SetIsRegistered(false);
|
||||
rasterizer.UpdatePagesCachedCount(object->GetCpuAddr(), object->GetSizeInBytes(), -1);
|
||||
const CacheAddr addr = object->GetCacheAddr();
|
||||
interval_cache.subtract({GetInterval(object), ObjectSet{object}});
|
||||
map_cache.erase(object->GetCacheAddr());
|
||||
map_cache.erase(addr);
|
||||
}
|
||||
|
||||
/// Returns a ticks counter used for tracking when cached objects were last modified
|
||||
|
||||
@@ -21,9 +21,18 @@ T GetInteger(GLenum pname) {
|
||||
|
||||
Device::Device() {
|
||||
uniform_buffer_alignment = GetInteger<std::size_t>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT);
|
||||
max_vertex_attributes = GetInteger<u32>(GL_MAX_VERTEX_ATTRIBS);
|
||||
max_varyings = GetInteger<u32>(GL_MAX_VARYING_VECTORS);
|
||||
has_variable_aoffi = TestVariableAoffi();
|
||||
}
|
||||
|
||||
Device::Device(std::nullptr_t) {
|
||||
uniform_buffer_alignment = 0;
|
||||
max_vertex_attributes = 16;
|
||||
max_varyings = 15;
|
||||
has_variable_aoffi = true;
|
||||
}
|
||||
|
||||
bool Device::TestVariableAoffi() {
|
||||
const GLchar* AOFFI_TEST = R"(#version 430 core
|
||||
uniform sampler2D tex;
|
||||
|
||||
@@ -5,17 +5,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class Device {
|
||||
public:
|
||||
Device();
|
||||
explicit Device();
|
||||
explicit Device(std::nullptr_t);
|
||||
|
||||
std::size_t GetUniformBufferAlignment() const {
|
||||
return uniform_buffer_alignment;
|
||||
}
|
||||
|
||||
u32 GetMaxVertexAttributes() const {
|
||||
return max_vertex_attributes;
|
||||
}
|
||||
|
||||
u32 GetMaxVaryings() const {
|
||||
return max_varyings;
|
||||
}
|
||||
|
||||
bool HasVariableAoffi() const {
|
||||
return has_variable_aoffi;
|
||||
}
|
||||
@@ -24,6 +34,8 @@ private:
|
||||
static bool TestVariableAoffi();
|
||||
|
||||
std::size_t uniform_buffer_alignment{};
|
||||
u32 max_vertex_attributes{};
|
||||
u32 max_varyings{};
|
||||
bool has_variable_aoffi{};
|
||||
};
|
||||
|
||||
|
||||
@@ -98,9 +98,11 @@ struct FramebufferCacheKey {
|
||||
}
|
||||
};
|
||||
|
||||
RasterizerOpenGL::RasterizerOpenGL(Core::System& system, ScreenInfo& info)
|
||||
: res_cache{*this}, shader_cache{*this, system, device}, global_cache{*this}, system{system},
|
||||
screen_info{info}, buffer_cache(*this, STREAM_BUFFER_SIZE) {
|
||||
RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,
|
||||
ScreenInfo& info)
|
||||
: res_cache{*this}, shader_cache{*this, system, emu_window, device},
|
||||
global_cache{*this}, system{system}, screen_info{info},
|
||||
buffer_cache(*this, STREAM_BUFFER_SIZE) {
|
||||
OpenGLState::ApplyDefaultState();
|
||||
|
||||
shader_program_manager = std::make_unique<GLShader::ProgramManager>();
|
||||
@@ -261,8 +263,8 @@ DrawParameters RasterizerOpenGL::SetupDraw() {
|
||||
// MakeQuadArray always generates u32 indexes
|
||||
params.index_format = GL_UNSIGNED_INT;
|
||||
params.count = (regs.vertex_buffer.count / 4) * 6;
|
||||
params.index_buffer_offset =
|
||||
primitive_assembler.MakeQuadArray(regs.vertex_buffer.first, params.count);
|
||||
params.index_buffer_offset = primitive_assembler.MakeQuadArray(
|
||||
regs.vertex_buffer.first, regs.vertex_buffer.count);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
@@ -1135,7 +1137,9 @@ void RasterizerOpenGL::SyncTransformFeedback() {
|
||||
|
||||
void RasterizerOpenGL::SyncPointState() {
|
||||
const auto& regs = system.GPU().Maxwell3D().regs;
|
||||
state.point.size = regs.point_size;
|
||||
// Limit the point size to 1 since nouveau sometimes sets a point size of 0 (and that's invalid
|
||||
// in OpenGL).
|
||||
state.point.size = std::max(1.0f, regs.point_size);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncPolygonOffset() {
|
||||
|
||||
@@ -48,7 +48,8 @@ struct FramebufferCacheKey;
|
||||
|
||||
class RasterizerOpenGL : public VideoCore::RasterizerInterface {
|
||||
public:
|
||||
explicit RasterizerOpenGL(Core::System& system, ScreenInfo& info);
|
||||
explicit RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,
|
||||
ScreenInfo& info);
|
||||
~RasterizerOpenGL() override;
|
||||
|
||||
void DrawArrays() override;
|
||||
|
||||
@@ -2,10 +2,14 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include "common/assert.h"
|
||||
#include "common/hash.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
||||
@@ -344,8 +348,8 @@ ShaderDiskCacheUsage CachedShader::GetUsage(GLenum primitive_mode,
|
||||
}
|
||||
|
||||
ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system,
|
||||
const Device& device)
|
||||
: RasterizerCache{rasterizer}, device{device}, disk_cache{system} {}
|
||||
Core::Frontend::EmuWindow& emu_window, const Device& device)
|
||||
: RasterizerCache{rasterizer}, emu_window{emu_window}, device{device}, disk_cache{system} {}
|
||||
|
||||
void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
|
||||
const VideoCore::DiskResourceLoadCallback& callback) {
|
||||
@@ -353,62 +357,107 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
|
||||
if (!transferable) {
|
||||
return;
|
||||
}
|
||||
const auto [raws, usages] = *transferable;
|
||||
const auto [raws, shader_usages] = *transferable;
|
||||
|
||||
auto [decompiled, dumps] = disk_cache.LoadPrecompiled();
|
||||
|
||||
const auto supported_formats{GetSupportedFormats()};
|
||||
const auto unspecialized{
|
||||
const auto unspecialized_shaders{
|
||||
GenerateUnspecializedShaders(stop_loading, callback, raws, decompiled)};
|
||||
if (stop_loading)
|
||||
if (stop_loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Track if precompiled cache was altered during loading to know if we have to serialize the
|
||||
// virtual precompiled cache file back to the hard drive
|
||||
bool precompiled_cache_altered = false;
|
||||
|
||||
// Build shaders
|
||||
if (callback)
|
||||
callback(VideoCore::LoadCallbackStage::Build, 0, usages.size());
|
||||
for (std::size_t i = 0; i < usages.size(); ++i) {
|
||||
if (stop_loading)
|
||||
return;
|
||||
// Inform the frontend about shader build initialization
|
||||
if (callback) {
|
||||
callback(VideoCore::LoadCallbackStage::Build, 0, shader_usages.size());
|
||||
}
|
||||
|
||||
const auto& usage{usages[i]};
|
||||
LOG_INFO(Render_OpenGL, "Building shader {:016x} ({} of {})", usage.unique_identifier,
|
||||
i + 1, usages.size());
|
||||
std::mutex mutex;
|
||||
std::size_t built_shaders = 0; // It doesn't have be atomic since it's used behind a mutex
|
||||
std::atomic_bool compilation_failed = false;
|
||||
|
||||
const auto& unspec{unspecialized.at(usage.unique_identifier)};
|
||||
const auto dump_it = dumps.find(usage);
|
||||
const auto Worker = [&](Core::Frontend::GraphicsContext* context, std::size_t begin,
|
||||
std::size_t end, const std::vector<ShaderDiskCacheUsage>& shader_usages,
|
||||
const ShaderDumpsMap& dumps) {
|
||||
context->MakeCurrent();
|
||||
SCOPE_EXIT({ return context->DoneCurrent(); });
|
||||
|
||||
CachedProgram shader;
|
||||
if (dump_it != dumps.end()) {
|
||||
// If the shader is dumped, attempt to load it with
|
||||
shader = GeneratePrecompiledProgram(dump_it->second, supported_formats);
|
||||
if (!shader) {
|
||||
// Invalidate the precompiled cache if a shader dumped shader was rejected
|
||||
disk_cache.InvalidatePrecompiled();
|
||||
precompiled_cache_altered = true;
|
||||
dumps.clear();
|
||||
for (std::size_t i = begin; i < end; ++i) {
|
||||
if (stop_loading || compilation_failed) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!shader) {
|
||||
shader = SpecializeShader(unspec.code, unspec.entries, unspec.program_type,
|
||||
usage.bindings, usage.primitive, true);
|
||||
}
|
||||
precompiled_programs.insert({usage, std::move(shader)});
|
||||
const auto& usage{shader_usages[i]};
|
||||
LOG_INFO(Render_OpenGL, "Building shader {:016x} (index {} of {})",
|
||||
usage.unique_identifier, i, shader_usages.size());
|
||||
|
||||
if (callback)
|
||||
callback(VideoCore::LoadCallbackStage::Build, i + 1, usages.size());
|
||||
const auto& unspecialized{unspecialized_shaders.at(usage.unique_identifier)};
|
||||
const auto dump{dumps.find(usage)};
|
||||
|
||||
CachedProgram shader;
|
||||
if (dump != dumps.end()) {
|
||||
// If the shader is dumped, attempt to load it with
|
||||
shader = GeneratePrecompiledProgram(dump->second, supported_formats);
|
||||
if (!shader) {
|
||||
compilation_failed = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!shader) {
|
||||
shader = SpecializeShader(unspecialized.code, unspecialized.entries,
|
||||
unspecialized.program_type, usage.bindings,
|
||||
usage.primitive, true);
|
||||
}
|
||||
|
||||
std::scoped_lock lock(mutex);
|
||||
if (callback) {
|
||||
callback(VideoCore::LoadCallbackStage::Build, ++built_shaders,
|
||||
shader_usages.size());
|
||||
}
|
||||
|
||||
precompiled_programs.emplace(usage, std::move(shader));
|
||||
}
|
||||
};
|
||||
|
||||
const auto num_workers{static_cast<std::size_t>(std::thread::hardware_concurrency() + 1)};
|
||||
const std::size_t bucket_size{shader_usages.size() / num_workers};
|
||||
std::vector<std::unique_ptr<Core::Frontend::GraphicsContext>> contexts(num_workers);
|
||||
std::vector<std::thread> threads(num_workers);
|
||||
for (std::size_t i = 0; i < num_workers; ++i) {
|
||||
const bool is_last_worker = i + 1 == num_workers;
|
||||
const std::size_t start{bucket_size * i};
|
||||
const std::size_t end{is_last_worker ? shader_usages.size() : start + bucket_size};
|
||||
|
||||
// On some platforms the shared context has to be created from the GUI thread
|
||||
contexts[i] = emu_window.CreateSharedContext();
|
||||
threads[i] = std::thread(Worker, contexts[i].get(), start, end, shader_usages, dumps);
|
||||
}
|
||||
for (auto& thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
|
||||
if (compilation_failed) {
|
||||
// Invalidate the precompiled cache if a shader dumped shader was rejected
|
||||
disk_cache.InvalidatePrecompiled();
|
||||
dumps.clear();
|
||||
precompiled_cache_altered = true;
|
||||
return;
|
||||
}
|
||||
if (stop_loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(Rodrigo): Do state tracking for transferable shaders and do a dummy draw before
|
||||
// precompiling them
|
||||
|
||||
for (std::size_t i = 0; i < usages.size(); ++i) {
|
||||
const auto& usage{usages[i]};
|
||||
for (std::size_t i = 0; i < shader_usages.size(); ++i) {
|
||||
const auto& usage{shader_usages[i]};
|
||||
if (dumps.find(usage) == dumps.end()) {
|
||||
const auto& program = precompiled_programs.at(usage);
|
||||
const auto& program{precompiled_programs.at(usage)};
|
||||
disk_cache.SaveDump(usage, program->handle);
|
||||
precompiled_cache_altered = true;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,11 @@
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
} // namespace Core
|
||||
}
|
||||
|
||||
namespace Core::Frontend {
|
||||
class EmuWindow;
|
||||
}
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
@@ -111,7 +115,7 @@ private:
|
||||
class ShaderCacheOpenGL final : public RasterizerCache<Shader> {
|
||||
public:
|
||||
explicit ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system,
|
||||
const Device& device);
|
||||
Core::Frontend::EmuWindow& emu_window, const Device& device);
|
||||
|
||||
/// Loads disk cache for the current game
|
||||
void LoadDiskCache(const std::atomic_bool& stop_loading,
|
||||
@@ -133,13 +137,13 @@ private:
|
||||
CachedProgram GeneratePrecompiledProgram(const ShaderDiskCacheDump& dump,
|
||||
const std::set<GLenum>& supported_formats);
|
||||
|
||||
Core::Frontend::EmuWindow& emu_window;
|
||||
const Device& device;
|
||||
|
||||
std::array<Shader, Maxwell::MaxShaderProgram> last_shaders;
|
||||
|
||||
ShaderDiskCacheOpenGL disk_cache;
|
||||
|
||||
PrecompiledShaders precompiled_shaders;
|
||||
PrecompiledPrograms precompiled_programs;
|
||||
std::array<Shader, Maxwell::MaxShaderProgram> last_shaders;
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -104,8 +104,9 @@ bool ShaderDiskCacheRaw::Save(FileUtil::IOFile& file) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
ShaderDiskCacheOpenGL::ShaderDiskCacheOpenGL(Core::System& system)
|
||||
: system{system}, precompiled_cache_virtual_file_offset{0} {}
|
||||
ShaderDiskCacheOpenGL::ShaderDiskCacheOpenGL(Core::System& system) : system{system} {}
|
||||
|
||||
ShaderDiskCacheOpenGL::~ShaderDiskCacheOpenGL() = default;
|
||||
|
||||
std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>>
|
||||
ShaderDiskCacheOpenGL::LoadTransferable() {
|
||||
@@ -182,8 +183,7 @@ ShaderDiskCacheOpenGL::LoadTransferable() {
|
||||
return {{raws, usages}};
|
||||
}
|
||||
|
||||
std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>,
|
||||
std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>
|
||||
std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, ShaderDumpsMap>
|
||||
ShaderDiskCacheOpenGL::LoadPrecompiled() {
|
||||
if (!IsUsable())
|
||||
return {};
|
||||
@@ -207,8 +207,7 @@ ShaderDiskCacheOpenGL::LoadPrecompiled() {
|
||||
return *result;
|
||||
}
|
||||
|
||||
std::optional<std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>,
|
||||
std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>>
|
||||
std::optional<std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, ShaderDumpsMap>>
|
||||
ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) {
|
||||
// Read compressed file from disk and decompress to virtual precompiled cache file
|
||||
std::vector<u8> compressed(file.GetSize());
|
||||
@@ -229,7 +228,7 @@ ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) {
|
||||
}
|
||||
|
||||
std::unordered_map<u64, ShaderDiskCacheDecompiled> decompiled;
|
||||
std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump> dumps;
|
||||
ShaderDumpsMap dumps;
|
||||
while (precompiled_cache_virtual_file_offset < precompiled_cache_virtual_file.GetSize()) {
|
||||
PrecompiledEntryKind kind{};
|
||||
if (!LoadObjectFromPrecompiled(kind)) {
|
||||
@@ -243,7 +242,7 @@ ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto entry = LoadDecompiledEntry();
|
||||
auto entry = LoadDecompiledEntry();
|
||||
if (!entry) {
|
||||
return {};
|
||||
}
|
||||
@@ -287,13 +286,13 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<u8> code(code_size);
|
||||
std::string code(code_size, '\0');
|
||||
if (!LoadArrayFromPrecompiled(code.data(), code.size())) {
|
||||
return {};
|
||||
}
|
||||
|
||||
ShaderDiskCacheDecompiled entry;
|
||||
entry.code = std::string(reinterpret_cast<const char*>(code.data()), code_size);
|
||||
entry.code = std::move(code);
|
||||
|
||||
u32 const_buffers_count{};
|
||||
if (!LoadObjectFromPrecompiled(const_buffers_count)) {
|
||||
@@ -303,12 +302,12 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn
|
||||
for (u32 i = 0; i < const_buffers_count; ++i) {
|
||||
u32 max_offset{};
|
||||
u32 index{};
|
||||
u8 is_indirect{};
|
||||
bool is_indirect{};
|
||||
if (!LoadObjectFromPrecompiled(max_offset) || !LoadObjectFromPrecompiled(index) ||
|
||||
!LoadObjectFromPrecompiled(is_indirect)) {
|
||||
return {};
|
||||
}
|
||||
entry.entries.const_buffers.emplace_back(max_offset, is_indirect != 0, index);
|
||||
entry.entries.const_buffers.emplace_back(max_offset, is_indirect, index);
|
||||
}
|
||||
|
||||
u32 samplers_count{};
|
||||
@@ -320,18 +319,17 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn
|
||||
u64 offset{};
|
||||
u64 index{};
|
||||
u32 type{};
|
||||
u8 is_array{};
|
||||
u8 is_shadow{};
|
||||
u8 is_bindless{};
|
||||
bool is_array{};
|
||||
bool is_shadow{};
|
||||
bool is_bindless{};
|
||||
if (!LoadObjectFromPrecompiled(offset) || !LoadObjectFromPrecompiled(index) ||
|
||||
!LoadObjectFromPrecompiled(type) || !LoadObjectFromPrecompiled(is_array) ||
|
||||
!LoadObjectFromPrecompiled(is_shadow) || !LoadObjectFromPrecompiled(is_bindless)) {
|
||||
return {};
|
||||
}
|
||||
entry.entries.samplers.emplace_back(static_cast<std::size_t>(offset),
|
||||
static_cast<std::size_t>(index),
|
||||
static_cast<Tegra::Shader::TextureType>(type),
|
||||
is_array != 0, is_shadow != 0, is_bindless != 0);
|
||||
entry.entries.samplers.emplace_back(
|
||||
static_cast<std::size_t>(offset), static_cast<std::size_t>(index),
|
||||
static_cast<Tegra::Shader::TextureType>(type), is_array, is_shadow, is_bindless);
|
||||
}
|
||||
|
||||
u32 global_memory_count{};
|
||||
@@ -342,21 +340,20 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn
|
||||
for (u32 i = 0; i < global_memory_count; ++i) {
|
||||
u32 cbuf_index{};
|
||||
u32 cbuf_offset{};
|
||||
u8 is_read{};
|
||||
u8 is_written{};
|
||||
bool is_read{};
|
||||
bool is_written{};
|
||||
if (!LoadObjectFromPrecompiled(cbuf_index) || !LoadObjectFromPrecompiled(cbuf_offset) ||
|
||||
!LoadObjectFromPrecompiled(is_read) || !LoadObjectFromPrecompiled(is_written)) {
|
||||
return {};
|
||||
}
|
||||
entry.entries.global_memory_entries.emplace_back(cbuf_index, cbuf_offset, is_read != 0,
|
||||
is_written != 0);
|
||||
entry.entries.global_memory_entries.emplace_back(cbuf_index, cbuf_offset, is_read,
|
||||
is_written);
|
||||
}
|
||||
|
||||
for (auto& clip_distance : entry.entries.clip_distances) {
|
||||
u8 clip_distance_raw{};
|
||||
if (!LoadObjectFromPrecompiled(clip_distance_raw))
|
||||
if (!LoadObjectFromPrecompiled(clip_distance)) {
|
||||
return {};
|
||||
clip_distance = clip_distance_raw != 0;
|
||||
}
|
||||
}
|
||||
|
||||
u64 shader_length{};
|
||||
@@ -384,7 +381,7 @@ bool ShaderDiskCacheOpenGL::SaveDecompiledFile(u64 unique_identifier, const std:
|
||||
for (const auto& cbuf : entries.const_buffers) {
|
||||
if (!SaveObjectToPrecompiled(static_cast<u32>(cbuf.GetMaxOffset())) ||
|
||||
!SaveObjectToPrecompiled(static_cast<u32>(cbuf.GetIndex())) ||
|
||||
!SaveObjectToPrecompiled(static_cast<u8>(cbuf.IsIndirect() ? 1 : 0))) {
|
||||
!SaveObjectToPrecompiled(cbuf.IsIndirect())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -396,9 +393,9 @@ bool ShaderDiskCacheOpenGL::SaveDecompiledFile(u64 unique_identifier, const std:
|
||||
if (!SaveObjectToPrecompiled(static_cast<u64>(sampler.GetOffset())) ||
|
||||
!SaveObjectToPrecompiled(static_cast<u64>(sampler.GetIndex())) ||
|
||||
!SaveObjectToPrecompiled(static_cast<u32>(sampler.GetType())) ||
|
||||
!SaveObjectToPrecompiled(static_cast<u8>(sampler.IsArray() ? 1 : 0)) ||
|
||||
!SaveObjectToPrecompiled(static_cast<u8>(sampler.IsShadow() ? 1 : 0)) ||
|
||||
!SaveObjectToPrecompiled(static_cast<u8>(sampler.IsBindless() ? 1 : 0))) {
|
||||
!SaveObjectToPrecompiled(sampler.IsArray()) ||
|
||||
!SaveObjectToPrecompiled(sampler.IsShadow()) ||
|
||||
!SaveObjectToPrecompiled(sampler.IsBindless())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -409,14 +406,13 @@ bool ShaderDiskCacheOpenGL::SaveDecompiledFile(u64 unique_identifier, const std:
|
||||
for (const auto& gmem : entries.global_memory_entries) {
|
||||
if (!SaveObjectToPrecompiled(static_cast<u32>(gmem.GetCbufIndex())) ||
|
||||
!SaveObjectToPrecompiled(static_cast<u32>(gmem.GetCbufOffset())) ||
|
||||
!SaveObjectToPrecompiled(static_cast<u8>(gmem.IsRead() ? 1 : 0)) ||
|
||||
!SaveObjectToPrecompiled(static_cast<u8>(gmem.IsWritten() ? 1 : 0))) {
|
||||
!SaveObjectToPrecompiled(gmem.IsRead()) || !SaveObjectToPrecompiled(gmem.IsWritten())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const bool clip_distance : entries.clip_distances) {
|
||||
if (!SaveObjectToPrecompiled(static_cast<u8>(clip_distance ? 1 : 0))) {
|
||||
if (!SaveObjectToPrecompiled(clip_distance)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,11 @@ namespace OpenGL {
|
||||
using ProgramCode = std::vector<u64>;
|
||||
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
||||
|
||||
struct ShaderDiskCacheUsage;
|
||||
struct ShaderDiskCacheDump;
|
||||
|
||||
using ShaderDumpsMap = std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>;
|
||||
|
||||
/// Allocated bindings used by an OpenGL shader program
|
||||
struct BaseBindings {
|
||||
u32 cbuf{};
|
||||
@@ -70,14 +75,14 @@ namespace std {
|
||||
|
||||
template <>
|
||||
struct hash<OpenGL::BaseBindings> {
|
||||
std::size_t operator()(const OpenGL::BaseBindings& bindings) const {
|
||||
std::size_t operator()(const OpenGL::BaseBindings& bindings) const noexcept {
|
||||
return bindings.cbuf | bindings.gmem << 8 | bindings.sampler << 16;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<OpenGL::ShaderDiskCacheUsage> {
|
||||
std::size_t operator()(const OpenGL::ShaderDiskCacheUsage& usage) const {
|
||||
std::size_t operator()(const OpenGL::ShaderDiskCacheUsage& usage) const noexcept {
|
||||
return static_cast<std::size_t>(usage.unique_identifier) ^
|
||||
std::hash<OpenGL::BaseBindings>()(usage.bindings) ^ usage.primitive << 16;
|
||||
}
|
||||
@@ -162,6 +167,7 @@ struct ShaderDiskCacheDump {
|
||||
class ShaderDiskCacheOpenGL {
|
||||
public:
|
||||
explicit ShaderDiskCacheOpenGL(Core::System& system);
|
||||
~ShaderDiskCacheOpenGL();
|
||||
|
||||
/// Loads transferable cache. If file has a old version or on failure, it deletes the file.
|
||||
std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>>
|
||||
@@ -259,23 +265,38 @@ private:
|
||||
return SaveArrayToPrecompiled(&object, 1);
|
||||
}
|
||||
|
||||
bool SaveObjectToPrecompiled(bool object) {
|
||||
const auto value = static_cast<u8>(object);
|
||||
return SaveArrayToPrecompiled(&value, 1);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool LoadObjectFromPrecompiled(T& object) {
|
||||
return LoadArrayFromPrecompiled(&object, 1);
|
||||
}
|
||||
|
||||
// Copre system
|
||||
bool LoadObjectFromPrecompiled(bool& object) {
|
||||
u8 value;
|
||||
const bool read_ok = LoadArrayFromPrecompiled(&value, 1);
|
||||
if (!read_ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
object = value != 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Core system
|
||||
Core::System& system;
|
||||
// Stored transferable shaders
|
||||
std::map<u64, std::unordered_set<ShaderDiskCacheUsage>> transferable;
|
||||
// Stores whole precompiled cache which will be read from or saved to the precompiled chache
|
||||
// file
|
||||
// Stores whole precompiled cache which will be read from/saved to the precompiled cache file
|
||||
FileSys::VectorVfsFile precompiled_cache_virtual_file;
|
||||
// Stores the current offset of the precompiled cache file for IO purposes
|
||||
std::size_t precompiled_cache_virtual_file_offset;
|
||||
std::size_t precompiled_cache_virtual_file_offset = 0;
|
||||
|
||||
// The cache has been loaded at boot
|
||||
bool tried_to_load{};
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -33,14 +33,14 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform vs_config {
|
||||
};
|
||||
|
||||
)";
|
||||
ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET);
|
||||
const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET);
|
||||
ProgramResult program =
|
||||
Decompile(device, program_ir, Maxwell3D::Regs::ShaderStage::Vertex, "vertex");
|
||||
|
||||
out += program.first;
|
||||
|
||||
if (setup.IsDualProgram()) {
|
||||
ShaderIR program_ir_b(setup.program.code_b, PROGRAM_OFFSET);
|
||||
const ShaderIR program_ir_b(setup.program.code_b, PROGRAM_OFFSET);
|
||||
ProgramResult program_b =
|
||||
Decompile(device, program_ir_b, Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b");
|
||||
|
||||
@@ -76,7 +76,7 @@ void main() {
|
||||
}
|
||||
})";
|
||||
|
||||
return {out, program.second};
|
||||
return {std::move(out), std::move(program.second)};
|
||||
}
|
||||
|
||||
ProgramResult GenerateGeometryShader(const Device& device, const ShaderSetup& setup) {
|
||||
@@ -97,7 +97,7 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform gs_config {
|
||||
};
|
||||
|
||||
)";
|
||||
ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET);
|
||||
const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET);
|
||||
ProgramResult program =
|
||||
Decompile(device, program_ir, Maxwell3D::Regs::ShaderStage::Geometry, "geometry");
|
||||
out += program.first;
|
||||
@@ -107,7 +107,7 @@ void main() {
|
||||
execute_geometry();
|
||||
};)";
|
||||
|
||||
return {out, program.second};
|
||||
return {std::move(out), std::move(program.second)};
|
||||
}
|
||||
|
||||
ProgramResult GenerateFragmentShader(const Device& device, const ShaderSetup& setup) {
|
||||
@@ -160,7 +160,7 @@ bool AlphaFunc(in float value) {
|
||||
}
|
||||
|
||||
)";
|
||||
ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET);
|
||||
const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET);
|
||||
ProgramResult program =
|
||||
Decompile(device, program_ir, Maxwell3D::Regs::ShaderStage::Fragment, "fragment");
|
||||
|
||||
@@ -172,7 +172,7 @@ void main() {
|
||||
}
|
||||
|
||||
)";
|
||||
return {out, program.second};
|
||||
return {std::move(out), std::move(program.second)};
|
||||
}
|
||||
|
||||
} // namespace OpenGL::GLShader
|
||||
|
||||
@@ -126,6 +126,8 @@ inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) {
|
||||
return GL_TRIANGLES;
|
||||
case Maxwell::PrimitiveTopology::TriangleStrip:
|
||||
return GL_TRIANGLE_STRIP;
|
||||
case Maxwell::PrimitiveTopology::TriangleFan:
|
||||
return GL_TRIANGLE_FAN;
|
||||
default:
|
||||
LOG_CRITICAL(Render_OpenGL, "Unimplemented topology={}", static_cast<u32>(topology));
|
||||
UNREACHABLE();
|
||||
|
||||
@@ -97,8 +97,8 @@ static std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(const float width, cons
|
||||
return matrix;
|
||||
}
|
||||
|
||||
RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& window, Core::System& system)
|
||||
: VideoCore::RendererBase{window}, system{system} {}
|
||||
RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system)
|
||||
: VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system} {}
|
||||
|
||||
RendererOpenGL::~RendererOpenGL() = default;
|
||||
|
||||
@@ -265,7 +265,7 @@ void RendererOpenGL::CreateRasterizer() {
|
||||
}
|
||||
// Initialize sRGB Usage
|
||||
OpenGLState::ClearsRGBUsed();
|
||||
rasterizer = std::make_unique<RasterizerOpenGL>(system, screen_info);
|
||||
rasterizer = std::make_unique<RasterizerOpenGL>(system, emu_window, screen_info);
|
||||
}
|
||||
|
||||
void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
|
||||
|
||||
@@ -45,7 +45,7 @@ struct ScreenInfo {
|
||||
|
||||
class RendererOpenGL : public VideoCore::RendererBase {
|
||||
public:
|
||||
explicit RendererOpenGL(Core::Frontend::EmuWindow& window, Core::System& system);
|
||||
explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system);
|
||||
~RendererOpenGL() override;
|
||||
|
||||
/// Swap buffers (render frame)
|
||||
@@ -77,6 +77,7 @@ private:
|
||||
void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a,
|
||||
const TextureInfo& texture);
|
||||
|
||||
Core::Frontend::EmuWindow& emu_window;
|
||||
Core::System& system;
|
||||
|
||||
OpenGLState state;
|
||||
|
||||
@@ -38,27 +38,27 @@ void BindBuffersRangePushBuffer::Bind() const {
|
||||
sizes.data());
|
||||
}
|
||||
|
||||
void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string extra_info) {
|
||||
void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string_view extra_info) {
|
||||
if (!GLAD_GL_KHR_debug) {
|
||||
return; // We don't need to throw an error as this is just for debugging
|
||||
// We don't need to throw an error as this is just for debugging
|
||||
return;
|
||||
}
|
||||
const std::string nice_addr = fmt::format("0x{:016x}", addr);
|
||||
std::string object_label;
|
||||
|
||||
std::string object_label;
|
||||
if (extra_info.empty()) {
|
||||
switch (identifier) {
|
||||
case GL_TEXTURE:
|
||||
object_label = "Texture@" + nice_addr;
|
||||
object_label = fmt::format("Texture@0x{:016X}", addr);
|
||||
break;
|
||||
case GL_PROGRAM:
|
||||
object_label = "Shader@" + nice_addr;
|
||||
object_label = fmt::format("Shader@0x{:016X}", addr);
|
||||
break;
|
||||
default:
|
||||
object_label = fmt::format("Object(0x{:x})@{}", identifier, nice_addr);
|
||||
object_label = fmt::format("Object(0x{:X})@0x{:016X}", identifier, addr);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
object_label = extra_info + '@' + nice_addr;
|
||||
object_label = fmt::format("{}@0x{:016X}", extra_info, addr);
|
||||
}
|
||||
glObjectLabel(identifier, handle, -1, static_cast<const GLchar*>(object_label.c_str()));
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <glad/glad.h>
|
||||
#include "common/common_types.h"
|
||||
@@ -30,6 +30,6 @@ private:
|
||||
std::vector<GLsizeiptr> sizes;
|
||||
};
|
||||
|
||||
void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string extra_info = "");
|
||||
void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string_view extra_info = {});
|
||||
|
||||
} // namespace OpenGL
|
||||
@@ -194,8 +194,8 @@ public:
|
||||
for (const auto& sampler : ir.GetSamplers()) {
|
||||
entries.samplers.emplace_back(sampler);
|
||||
}
|
||||
for (const auto& attr : ir.GetInputAttributes()) {
|
||||
entries.attributes.insert(GetGenericAttributeLocation(attr.first));
|
||||
for (const auto& attribute : ir.GetInputAttributes()) {
|
||||
entries.attributes.insert(GetGenericAttributeLocation(attribute));
|
||||
}
|
||||
entries.clip_distances = ir.GetClipDistances();
|
||||
entries.shader_length = ir.GetLength();
|
||||
@@ -321,8 +321,7 @@ private:
|
||||
}
|
||||
|
||||
void DeclareInputAttributes() {
|
||||
for (const auto element : ir.GetInputAttributes()) {
|
||||
const Attribute::Index index = element.first;
|
||||
for (const auto index : ir.GetInputAttributes()) {
|
||||
if (!IsGenericAttribute(index)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
@@ -152,4 +153,4 @@ u32 ShaderIR::DecodeArithmetic(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
@@ -47,4 +48,4 @@ u32 ShaderIR::DecodeArithmeticHalfImmediate(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -49,4 +49,4 @@ u32 ShaderIR::DecodeArithmeticImmediate(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -93,4 +93,4 @@ void ShaderIR::WriteLogicOperation(NodeBlock& bb, Register dest, LogicOperation
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -46,4 +46,4 @@ u32 ShaderIR::DecodeBfe(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -38,4 +38,4 @@ u32 ShaderIR::DecodeBfi(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -56,4 +56,4 @@ u32 ShaderIR::DecodeFfma(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -55,4 +55,4 @@ u32 ShaderIR::DecodeFloatSet(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -53,4 +53,4 @@ u32 ShaderIR::DecodeFloatSetPredicate(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
@@ -64,4 +65,4 @@ u32 ShaderIR::DecodeHalfSet(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -59,4 +59,4 @@ u32 ShaderIR::DecodeHalfSetPredicate(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
@@ -47,4 +46,4 @@ u32 ShaderIR::DecodeIntegerSet(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -50,4 +50,4 @@ u32 ShaderIR::DecodeIntegerSetPredicate(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -47,17 +47,20 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
"Indirect attribute loads are not supported");
|
||||
UNIMPLEMENTED_IF_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) != 0,
|
||||
"Unaligned attribute loads are not supported");
|
||||
UNIMPLEMENTED_IF_MSG(instr.attribute.fmt20.IsPhysical() &&
|
||||
instr.attribute.fmt20.size != Tegra::Shader::AttributeSize::Word,
|
||||
"Non-32 bits PHYS reads are not implemented");
|
||||
|
||||
Tegra::Shader::IpaMode input_mode{Tegra::Shader::IpaInterpMode::Pass,
|
||||
Tegra::Shader::IpaSampleMode::Default};
|
||||
const Node buffer{GetRegister(instr.gpr39)};
|
||||
|
||||
u64 next_element = instr.attribute.fmt20.element;
|
||||
auto next_index = static_cast<u64>(instr.attribute.fmt20.index.Value());
|
||||
|
||||
const auto LoadNextElement = [&](u32 reg_offset) {
|
||||
const Node buffer = GetRegister(instr.gpr39);
|
||||
const Node attribute = GetInputAttribute(static_cast<Attribute::Index>(next_index),
|
||||
next_element, input_mode, buffer);
|
||||
const Node attribute{instr.attribute.fmt20.IsPhysical()
|
||||
? GetPhysicalInputAttribute(instr.gpr8, buffer)
|
||||
: GetInputAttribute(static_cast<Attribute::Index>(next_index),
|
||||
next_element, buffer)};
|
||||
|
||||
SetRegister(bb, instr.gpr0.Value() + reg_offset, attribute);
|
||||
|
||||
@@ -143,12 +146,25 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::LD:
|
||||
case OpCode::Id::LDG: {
|
||||
const auto [real_address_base, base_address, descriptor] =
|
||||
TrackAndGetGlobalMemory(bb, GetRegister(instr.gpr8),
|
||||
static_cast<u32>(instr.ldg.immediate_offset.Value()), false);
|
||||
const auto type = [instr, &opcode]() -> Tegra::Shader::UniformType {
|
||||
switch (opcode->get().GetId()) {
|
||||
case OpCode::Id::LD:
|
||||
UNIMPLEMENTED_IF_MSG(!instr.generic.extended, "Unextended LD is not implemented");
|
||||
return instr.generic.type;
|
||||
case OpCode::Id::LDG:
|
||||
return instr.ldg.type;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
}
|
||||
}();
|
||||
|
||||
const u32 count = GetUniformTypeElementsCount(instr.ldg.type);
|
||||
const auto [real_address_base, base_address, descriptor] =
|
||||
TrackAndGetGlobalMemory(bb, instr, false);
|
||||
|
||||
const u32 count = GetUniformTypeElementsCount(type);
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
const Node it_offset = Immediate(i * 4);
|
||||
const Node real_address =
|
||||
@@ -162,28 +178,6 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::STG: {
|
||||
const auto [real_address_base, base_address, descriptor] =
|
||||
TrackAndGetGlobalMemory(bb, GetRegister(instr.gpr8),
|
||||
static_cast<u32>(instr.stg.immediate_offset.Value()), true);
|
||||
|
||||
// Encode in temporary registers like this: real_base_address, {registers_to_be_written...}
|
||||
SetTemporal(bb, 0, real_address_base);
|
||||
|
||||
const u32 count = GetUniformTypeElementsCount(instr.stg.type);
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
SetTemporal(bb, i + 1, GetRegister(instr.gpr0.Value() + i));
|
||||
}
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
const Node it_offset = Immediate(i * 4);
|
||||
const Node real_address =
|
||||
Operation(OperationCode::UAdd, NO_PRECISE, real_address_base, it_offset);
|
||||
const Node gmem = StoreNode(GmemNode(real_address, base_address, descriptor));
|
||||
|
||||
bb.push_back(Operation(OperationCode::Assign, gmem, GetTemporal(i + 1)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::ST_A: {
|
||||
UNIMPLEMENTED_IF_MSG(instr.gpr8.Value() != Register::ZeroIndex,
|
||||
"Indirect attribute loads are not supported");
|
||||
@@ -239,6 +233,56 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::ST:
|
||||
case OpCode::Id::STG: {
|
||||
const auto type = [instr, &opcode]() -> Tegra::Shader::UniformType {
|
||||
switch (opcode->get().GetId()) {
|
||||
case OpCode::Id::ST:
|
||||
UNIMPLEMENTED_IF_MSG(!instr.generic.extended, "Unextended ST is not implemented");
|
||||
return instr.generic.type;
|
||||
case OpCode::Id::STG:
|
||||
return instr.stg.type;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
}
|
||||
}();
|
||||
|
||||
const auto [real_address_base, base_address, descriptor] =
|
||||
TrackAndGetGlobalMemory(bb, instr, true);
|
||||
|
||||
// Encode in temporary registers like this: real_base_address, {registers_to_be_written...}
|
||||
SetTemporal(bb, 0, real_address_base);
|
||||
|
||||
const u32 count = GetUniformTypeElementsCount(type);
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
SetTemporal(bb, i + 1, GetRegister(instr.gpr0.Value() + i));
|
||||
}
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
const Node it_offset = Immediate(i * 4);
|
||||
const Node real_address =
|
||||
Operation(OperationCode::UAdd, NO_PRECISE, real_address_base, it_offset);
|
||||
const Node gmem = StoreNode(GmemNode(real_address, base_address, descriptor));
|
||||
|
||||
bb.push_back(Operation(OperationCode::Assign, gmem, GetTemporal(i + 1)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::AL2P: {
|
||||
// Ignore al2p.direction since we don't care about it.
|
||||
|
||||
// Calculate emulation fake physical address.
|
||||
const Node fixed_address{Immediate(static_cast<u32>(instr.al2p.address))};
|
||||
const Node reg{GetRegister(instr.gpr8)};
|
||||
const Node fake_address{Operation(OperationCode::IAdd, NO_PRECISE, reg, fixed_address)};
|
||||
|
||||
// Set the fake address to target register.
|
||||
SetRegister(bb, instr.gpr0, fake_address);
|
||||
|
||||
// Signal the shader IR to declare all possible attributes and varyings
|
||||
uses_physical_attributes = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unhandled memory instruction: {}", opcode->get().GetName());
|
||||
}
|
||||
@@ -247,9 +291,11 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
}
|
||||
|
||||
std::tuple<Node, Node, GlobalMemoryBase> ShaderIR::TrackAndGetGlobalMemory(NodeBlock& bb,
|
||||
Node addr_register,
|
||||
u32 immediate_offset,
|
||||
Instruction instr,
|
||||
bool is_write) {
|
||||
const auto addr_register{GetRegister(instr.gmem.gpr)};
|
||||
const auto immediate_offset{static_cast<u32>(instr.gmem.offset)};
|
||||
|
||||
const Node base_address{
|
||||
TrackCbuf(addr_register, global_code, static_cast<s64>(global_code.size()))};
|
||||
const auto cbuf = std::get_if<CbufNode>(base_address);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
@@ -130,15 +131,18 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::IPA: {
|
||||
const auto& attribute = instr.attribute.fmt28;
|
||||
const bool is_physical = instr.ipa.idx && instr.gpr8.Value() != 0xff;
|
||||
|
||||
const auto attribute = instr.attribute.fmt28;
|
||||
const Tegra::Shader::IpaMode input_mode{instr.ipa.interp_mode.Value(),
|
||||
instr.ipa.sample_mode.Value()};
|
||||
|
||||
const Node attr = GetInputAttribute(attribute.index, attribute.element, input_mode);
|
||||
Node value = attr;
|
||||
Node value = is_physical ? GetPhysicalInputAttribute(instr.gpr8)
|
||||
: GetInputAttribute(attribute.index, attribute.element);
|
||||
const Tegra::Shader::Attribute::Index index = attribute.index.Value();
|
||||
if (index >= Tegra::Shader::Attribute::Index::Attribute_0 &&
|
||||
index <= Tegra::Shader::Attribute::Index::Attribute_31) {
|
||||
const bool is_generic = index >= Tegra::Shader::Attribute::Index::Attribute_0 &&
|
||||
index <= Tegra::Shader::Attribute::Index::Attribute_31;
|
||||
if (is_generic || is_physical) {
|
||||
// TODO(Blinkhawk): There are cases where a perspective attribute use PASS.
|
||||
// In theory by setting them as perspective, OpenGL does the perspective correction.
|
||||
// A way must figured to reverse the last step of it.
|
||||
|
||||
@@ -64,4 +64,4 @@ u32 ShaderIR::DecodePredicateSetPredicate(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -43,4 +43,4 @@ u32 ShaderIR::DecodePredicateSetRegister(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -48,4 +48,4 @@ u32 ShaderIR::DecodeRegisterSetPredicate(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -52,4 +52,4 @@ u32 ShaderIR::DecodeShift(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -108,4 +108,4 @@ Node ShaderIR::GetVideoOperand(Node op, bool is_chunk, bool is_signed,
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -21,6 +21,13 @@ using Tegra::Shader::PredCondition;
|
||||
using Tegra::Shader::PredOperation;
|
||||
using Tegra::Shader::Register;
|
||||
|
||||
ShaderIR::ShaderIR(const ProgramCode& program_code, u32 main_offset)
|
||||
: program_code{program_code}, main_offset{main_offset} {
|
||||
Decode();
|
||||
}
|
||||
|
||||
ShaderIR::~ShaderIR() = default;
|
||||
|
||||
Node ShaderIR::StoreNode(NodeData&& node_data) {
|
||||
auto store = std::make_unique<NodeData>(node_data);
|
||||
const Node node = store.get();
|
||||
@@ -32,8 +39,8 @@ Node ShaderIR::Conditional(Node condition, std::vector<Node>&& code) {
|
||||
return StoreNode(ConditionalNode(condition, std::move(code)));
|
||||
}
|
||||
|
||||
Node ShaderIR::Comment(const std::string& text) {
|
||||
return StoreNode(CommentNode(text));
|
||||
Node ShaderIR::Comment(std::string text) {
|
||||
return StoreNode(CommentNode(std::move(text)));
|
||||
}
|
||||
|
||||
Node ShaderIR::Immediate(u32 value) {
|
||||
@@ -89,13 +96,14 @@ Node ShaderIR::GetPredicate(bool immediate) {
|
||||
return GetPredicate(static_cast<u64>(immediate ? Pred::UnusedIndex : Pred::NeverExecute));
|
||||
}
|
||||
|
||||
Node ShaderIR::GetInputAttribute(Attribute::Index index, u64 element,
|
||||
const Tegra::Shader::IpaMode& input_mode, Node buffer) {
|
||||
const auto [entry, is_new] =
|
||||
used_input_attributes.emplace(std::make_pair(index, std::set<Tegra::Shader::IpaMode>{}));
|
||||
entry->second.insert(input_mode);
|
||||
Node ShaderIR::GetInputAttribute(Attribute::Index index, u64 element, Node buffer) {
|
||||
used_input_attributes.emplace(index);
|
||||
return StoreNode(AbufNode(index, static_cast<u32>(element), buffer));
|
||||
}
|
||||
|
||||
return StoreNode(AbufNode(index, static_cast<u32>(element), input_mode, buffer));
|
||||
Node ShaderIR::GetPhysicalInputAttribute(Tegra::Shader::Register physical_address, Node buffer) {
|
||||
uses_physical_attributes = true;
|
||||
return StoreNode(AbufNode(GetRegister(physical_address), buffer));
|
||||
}
|
||||
|
||||
Node ShaderIR::GetOutputAttribute(Attribute::Index index, u64 element, Node buffer) {
|
||||
|
||||
@@ -328,40 +328,31 @@ struct MetaTexture {
|
||||
u32 element{};
|
||||
};
|
||||
|
||||
inline constexpr MetaArithmetic PRECISE = {true};
|
||||
inline constexpr MetaArithmetic NO_PRECISE = {false};
|
||||
constexpr MetaArithmetic PRECISE = {true};
|
||||
constexpr MetaArithmetic NO_PRECISE = {false};
|
||||
|
||||
using Meta = std::variant<MetaArithmetic, MetaTexture, Tegra::Shader::HalfType>;
|
||||
|
||||
/// Holds any kind of operation that can be done in the IR
|
||||
class OperationNode final {
|
||||
public:
|
||||
template <typename... T>
|
||||
explicit constexpr OperationNode(OperationCode code) : code{code}, meta{} {}
|
||||
explicit OperationNode(OperationCode code) : code{code} {}
|
||||
|
||||
explicit OperationNode(OperationCode code, Meta&& meta) : code{code}, meta{std::move(meta)} {}
|
||||
|
||||
template <typename... T>
|
||||
explicit constexpr OperationNode(OperationCode code, Meta&& meta)
|
||||
: code{code}, meta{std::move(meta)} {}
|
||||
|
||||
template <typename... T>
|
||||
explicit constexpr OperationNode(OperationCode code, const T*... operands)
|
||||
explicit OperationNode(OperationCode code, const T*... operands)
|
||||
: OperationNode(code, {}, operands...) {}
|
||||
|
||||
template <typename... T>
|
||||
explicit constexpr OperationNode(OperationCode code, Meta&& meta, const T*... operands_)
|
||||
: code{code}, meta{std::move(meta)} {
|
||||
|
||||
auto operands_list = {operands_...};
|
||||
for (auto& operand : operands_list) {
|
||||
operands.push_back(operand);
|
||||
}
|
||||
}
|
||||
explicit OperationNode(OperationCode code, Meta&& meta, const T*... operands_)
|
||||
: code{code}, meta{std::move(meta)}, operands{operands_...} {}
|
||||
|
||||
explicit OperationNode(OperationCode code, Meta&& meta, std::vector<Node>&& operands)
|
||||
: code{code}, meta{meta}, operands{std::move(operands)} {}
|
||||
|
||||
explicit OperationNode(OperationCode code, std::vector<Node>&& operands)
|
||||
: code{code}, meta{}, operands{std::move(operands)} {}
|
||||
: code{code}, operands{std::move(operands)} {}
|
||||
|
||||
OperationCode GetCode() const {
|
||||
return code;
|
||||
@@ -465,17 +456,14 @@ private:
|
||||
/// Attribute buffer memory (known as attributes or varyings in GLSL terms)
|
||||
class AbufNode final {
|
||||
public:
|
||||
explicit constexpr AbufNode(Tegra::Shader::Attribute::Index index, u32 element,
|
||||
const Tegra::Shader::IpaMode& input_mode, Node buffer = {})
|
||||
: input_mode{input_mode}, buffer{buffer}, index{index}, element{element} {}
|
||||
|
||||
// Initialize for standard attributes (index is explicit).
|
||||
explicit constexpr AbufNode(Tegra::Shader::Attribute::Index index, u32 element,
|
||||
Node buffer = {})
|
||||
: input_mode{}, buffer{buffer}, index{index}, element{element} {}
|
||||
: buffer{buffer}, index{index}, element{element} {}
|
||||
|
||||
Tegra::Shader::IpaMode GetInputMode() const {
|
||||
return input_mode;
|
||||
}
|
||||
// Initialize for physical attributes (index is a variable value).
|
||||
explicit constexpr AbufNode(Node physical_address, Node buffer = {})
|
||||
: physical_address{physical_address}, buffer{buffer} {}
|
||||
|
||||
Tegra::Shader::Attribute::Index GetIndex() const {
|
||||
return index;
|
||||
@@ -489,11 +477,19 @@ public:
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool IsPhysicalBuffer() const {
|
||||
return physical_address != nullptr;
|
||||
}
|
||||
|
||||
Node GetPhysicalAddress() const {
|
||||
return physical_address;
|
||||
}
|
||||
|
||||
private:
|
||||
const Tegra::Shader::IpaMode input_mode;
|
||||
const Node buffer;
|
||||
const Tegra::Shader::Attribute::Index index;
|
||||
const u32 element;
|
||||
Node physical_address{};
|
||||
Node buffer{};
|
||||
Tegra::Shader::Attribute::Index index{};
|
||||
u32 element{};
|
||||
};
|
||||
|
||||
/// Constant buffer node, usually mapped to uniform buffers in GLSL
|
||||
@@ -567,11 +563,8 @@ private:
|
||||
|
||||
class ShaderIR final {
|
||||
public:
|
||||
explicit ShaderIR(const ProgramCode& program_code, u32 main_offset)
|
||||
: program_code{program_code}, main_offset{main_offset} {
|
||||
|
||||
Decode();
|
||||
}
|
||||
explicit ShaderIR(const ProgramCode& program_code, u32 main_offset);
|
||||
~ShaderIR();
|
||||
|
||||
const std::map<u32, NodeBlock>& GetBasicBlocks() const {
|
||||
return basic_blocks;
|
||||
@@ -585,8 +578,7 @@ public:
|
||||
return used_predicates;
|
||||
}
|
||||
|
||||
const std::map<Tegra::Shader::Attribute::Index, std::set<Tegra::Shader::IpaMode>>&
|
||||
GetInputAttributes() const {
|
||||
const std::set<Tegra::Shader::Attribute::Index>& GetInputAttributes() const {
|
||||
return used_input_attributes;
|
||||
}
|
||||
|
||||
@@ -615,6 +607,10 @@ public:
|
||||
return static_cast<std::size_t>(coverage_end * sizeof(u64));
|
||||
}
|
||||
|
||||
bool HasPhysicalAttributes() const {
|
||||
return uses_physical_attributes;
|
||||
}
|
||||
|
||||
const Tegra::Shader::Header& GetHeader() const {
|
||||
return header;
|
||||
}
|
||||
@@ -667,7 +663,7 @@ private:
|
||||
/// Creates a conditional node
|
||||
Node Conditional(Node condition, std::vector<Node>&& code);
|
||||
/// Creates a commentary
|
||||
Node Comment(const std::string& text);
|
||||
Node Comment(std::string text);
|
||||
/// Creates an u32 immediate
|
||||
Node Immediate(u32 value);
|
||||
/// Creates a s32 immediate
|
||||
@@ -696,8 +692,9 @@ private:
|
||||
/// Generates a predicate node for an immediate true or false value
|
||||
Node GetPredicate(bool immediate);
|
||||
/// Generates a node representing an input attribute. Keeps track of used attributes.
|
||||
Node GetInputAttribute(Tegra::Shader::Attribute::Index index, u64 element,
|
||||
const Tegra::Shader::IpaMode& input_mode, Node buffer = {});
|
||||
Node GetInputAttribute(Tegra::Shader::Attribute::Index index, u64 element, Node buffer = {});
|
||||
/// Generates a node representing a physical input attribute.
|
||||
Node GetPhysicalInputAttribute(Tegra::Shader::Register physical_address, Node buffer = {});
|
||||
/// Generates a node representing an output attribute. Keeps track of used attributes.
|
||||
Node GetOutputAttribute(Tegra::Shader::Attribute::Index index, u64 element, Node buffer);
|
||||
/// Generates a node representing an internal flag
|
||||
@@ -814,16 +811,15 @@ private:
|
||||
void WriteLop3Instruction(NodeBlock& bb, Tegra::Shader::Register dest, Node op_a, Node op_b,
|
||||
Node op_c, Node imm_lut, bool sets_cc);
|
||||
|
||||
Node TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor);
|
||||
Node TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) const;
|
||||
|
||||
std::optional<u32> TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor);
|
||||
std::optional<u32> TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor) const;
|
||||
|
||||
std::pair<Node, s64> TrackRegister(const GprNode* tracked, const NodeBlock& code, s64 cursor);
|
||||
std::pair<Node, s64> TrackRegister(const GprNode* tracked, const NodeBlock& code,
|
||||
s64 cursor) const;
|
||||
|
||||
std::tuple<Node, Node, GlobalMemoryBase> TrackAndGetGlobalMemory(NodeBlock& bb,
|
||||
Node addr_register,
|
||||
u32 immediate_offset,
|
||||
bool is_write);
|
||||
std::tuple<Node, Node, GlobalMemoryBase> TrackAndGetGlobalMemory(
|
||||
NodeBlock& bb, Tegra::Shader::Instruction instr, bool is_write);
|
||||
|
||||
template <typename... T>
|
||||
Node Operation(OperationCode code, const T*... operands) {
|
||||
@@ -835,12 +831,10 @@ private:
|
||||
return StoreNode(OperationNode(code, std::move(meta), operands...));
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
Node Operation(OperationCode code, std::vector<Node>&& operands) {
|
||||
return StoreNode(OperationNode(code, std::move(operands)));
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
Node Operation(OperationCode code, Meta&& meta, std::vector<Node>&& operands) {
|
||||
return StoreNode(OperationNode(code, std::move(meta), std::move(operands)));
|
||||
}
|
||||
@@ -872,13 +866,13 @@ private:
|
||||
|
||||
std::set<u32> used_registers;
|
||||
std::set<Tegra::Shader::Pred> used_predicates;
|
||||
std::map<Tegra::Shader::Attribute::Index, std::set<Tegra::Shader::IpaMode>>
|
||||
used_input_attributes;
|
||||
std::set<Tegra::Shader::Attribute::Index> used_input_attributes;
|
||||
std::set<Tegra::Shader::Attribute::Index> used_output_attributes;
|
||||
std::map<u32, ConstBuffer> used_cbufs;
|
||||
std::set<Sampler> used_samplers;
|
||||
std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances> used_clip_distances{};
|
||||
std::map<GlobalMemoryBase, GlobalMemoryUsage> used_global_memory;
|
||||
bool uses_physical_attributes{}; // Shader uses AL2P or physical attribute read/writes
|
||||
|
||||
Tegra::Shader::Header header;
|
||||
};
|
||||
|
||||
@@ -17,22 +17,24 @@ std::pair<Node, s64> FindOperation(const NodeBlock& code, s64 cursor,
|
||||
for (; cursor >= 0; --cursor) {
|
||||
const Node node = code.at(cursor);
|
||||
if (const auto operation = std::get_if<OperationNode>(node)) {
|
||||
if (operation->GetCode() == operation_code)
|
||||
if (operation->GetCode() == operation_code) {
|
||||
return {node, cursor};
|
||||
}
|
||||
}
|
||||
if (const auto conditional = std::get_if<ConditionalNode>(node)) {
|
||||
const auto& conditional_code = conditional->GetCode();
|
||||
const auto [found, internal_cursor] = FindOperation(
|
||||
conditional_code, static_cast<s64>(conditional_code.size() - 1), operation_code);
|
||||
if (found)
|
||||
if (found) {
|
||||
return {found, cursor};
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Node ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) {
|
||||
Node ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) const {
|
||||
if (const auto cbuf = std::get_if<CbufNode>(tracked)) {
|
||||
// Cbuf found, but it has to be immediate
|
||||
return std::holds_alternative<ImmediateNode>(*cbuf->GetOffset()) ? tracked : nullptr;
|
||||
@@ -65,7 +67,7 @@ Node ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::optional<u32> ShaderIR::TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor) {
|
||||
std::optional<u32> ShaderIR::TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor) const {
|
||||
// Reduce the cursor in one to avoid infinite loops when the instruction sets the same register
|
||||
// that it uses as operand
|
||||
const auto [found, found_cursor] =
|
||||
@@ -80,7 +82,7 @@ std::optional<u32> ShaderIR::TrackImmediate(Node tracked, const NodeBlock& code,
|
||||
}
|
||||
|
||||
std::pair<Node, s64> ShaderIR::TrackRegister(const GprNode* tracked, const NodeBlock& code,
|
||||
s64 cursor) {
|
||||
s64 cursor) const {
|
||||
for (; cursor >= 0; --cursor) {
|
||||
const auto [found_node, new_cursor] = FindOperation(code, cursor, OperationCode::Assign);
|
||||
if (!found_node) {
|
||||
|
||||
@@ -82,8 +82,6 @@ add_executable(yuzu
|
||||
util/limitable_input_dialog.h
|
||||
util/sequence_dialog/sequence_dialog.cpp
|
||||
util/sequence_dialog/sequence_dialog.h
|
||||
util/spinbox.cpp
|
||||
util/spinbox.h
|
||||
util/util.cpp
|
||||
util/util.h
|
||||
compatdb.cpp
|
||||
@@ -157,6 +155,10 @@ target_compile_definitions(yuzu PRIVATE
|
||||
# Use QStringBuilder for string concatenation to reduce
|
||||
# the overall number of temporary strings created.
|
||||
-DQT_USE_QSTRINGBUILDER
|
||||
|
||||
# Disable implicit conversions from/to C strings
|
||||
-DQT_NO_CAST_FROM_ASCII
|
||||
-DQT_NO_CAST_TO_ASCII
|
||||
)
|
||||
|
||||
if (YUZU_ENABLE_COMPATIBILITY_REPORTING)
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
|
||||
AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AboutDialog) {
|
||||
ui->setupUi(this);
|
||||
ui->labelLogo->setPixmap(QIcon::fromTheme("yuzu").pixmap(200));
|
||||
ui->labelBuildInfo->setText(
|
||||
ui->labelBuildInfo->text().arg(Common::g_build_fullname, Common::g_scm_branch,
|
||||
Common::g_scm_desc, QString(Common::g_build_date).left(10)));
|
||||
ui->labelLogo->setPixmap(QIcon::fromTheme(QStringLiteral("yuzu")).pixmap(200));
|
||||
ui->labelBuildInfo->setText(ui->labelBuildInfo->text().arg(
|
||||
QString::fromUtf8(Common::g_build_fullname), QString::fromUtf8(Common::g_scm_branch),
|
||||
QString::fromUtf8(Common::g_scm_desc), QString::fromUtf8(Common::g_build_date).left(10)));
|
||||
}
|
||||
|
||||
AboutDialog::~AboutDialog() = default;
|
||||
|
||||
@@ -29,11 +29,13 @@ void QtErrorDisplay::ShowError(ResultCode error, std::function<void()> finished)
|
||||
void QtErrorDisplay::ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
|
||||
std::function<void()> finished) const {
|
||||
this->callback = std::move(finished);
|
||||
|
||||
const QDateTime date_time = QDateTime::fromSecsSinceEpoch(time.count());
|
||||
emit MainWindowDisplayError(
|
||||
tr("An error occured on %1 at %2.\nPlease try again or contact the "
|
||||
"developer of the software.\n\nError Code: %3-%4 (0x%5)")
|
||||
.arg(QDateTime::fromSecsSinceEpoch(time.count()).toString("dddd, MMMM d, yyyy"))
|
||||
.arg(QDateTime::fromSecsSinceEpoch(time.count()).toString("h:mm:ss A"))
|
||||
.arg(date_time.toString(QStringLiteral("dddd, MMMM d, yyyy")))
|
||||
.arg(date_time.toString(QStringLiteral("h:mm:ss A")))
|
||||
.arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0'))
|
||||
.arg(error.description, 4, 10, QChar::fromLatin1('0'))
|
||||
.arg(error.raw, 8, 16, QChar::fromLatin1('0')));
|
||||
@@ -54,6 +56,6 @@ void QtErrorDisplay::ShowCustomErrorText(ResultCode error, std::string dialog_te
|
||||
|
||||
void QtErrorDisplay::MainWindowFinishedError() {
|
||||
// Acquire the HLE mutex
|
||||
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
callback();
|
||||
}
|
||||
|
||||
@@ -84,10 +84,10 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent)
|
||||
tree_view->setContextMenuPolicy(Qt::NoContextMenu);
|
||||
|
||||
item_model->insertColumns(0, 1);
|
||||
item_model->setHeaderData(0, Qt::Horizontal, "Users");
|
||||
item_model->setHeaderData(0, Qt::Horizontal, tr("Users"));
|
||||
|
||||
// We must register all custom types with the Qt Automoc system so that we are able to use it
|
||||
// with signals/slots. In this case, QList falls under the umbrells of custom types.
|
||||
// with signals/slots. In this case, QList falls under the umbrella of custom types.
|
||||
qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>");
|
||||
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
@@ -18,23 +18,30 @@ QtSoftwareKeyboardValidator::QtSoftwareKeyboardValidator(
|
||||
: parameters(std::move(parameters)) {}
|
||||
|
||||
QValidator::State QtSoftwareKeyboardValidator::validate(QString& input, int& pos) const {
|
||||
if (input.size() > parameters.max_length)
|
||||
if (input.size() > static_cast<s64>(parameters.max_length)) {
|
||||
return Invalid;
|
||||
if (parameters.disable_space && input.contains(' '))
|
||||
}
|
||||
if (parameters.disable_space && input.contains(QLatin1Char{' '})) {
|
||||
return Invalid;
|
||||
if (parameters.disable_address && input.contains('@'))
|
||||
}
|
||||
if (parameters.disable_address && input.contains(QLatin1Char{'@'})) {
|
||||
return Invalid;
|
||||
if (parameters.disable_percent && input.contains('%'))
|
||||
}
|
||||
if (parameters.disable_percent && input.contains(QLatin1Char{'%'})) {
|
||||
return Invalid;
|
||||
if (parameters.disable_slash && (input.contains('/') || input.contains('\\')))
|
||||
}
|
||||
if (parameters.disable_slash &&
|
||||
(input.contains(QLatin1Char{'/'}) || input.contains(QLatin1Char{'\\'}))) {
|
||||
return Invalid;
|
||||
}
|
||||
if (parameters.disable_number &&
|
||||
std::any_of(input.begin(), input.end(), [](QChar c) { return c.isDigit(); })) {
|
||||
return Invalid;
|
||||
}
|
||||
|
||||
if (parameters.disable_download_code &&
|
||||
std::any_of(input.begin(), input.end(), [](QChar c) { return c == 'O' || c == 'I'; })) {
|
||||
if (parameters.disable_download_code && std::any_of(input.begin(), input.end(), [](QChar c) {
|
||||
return c == QLatin1Char{'O'} || c == QLatin1Char{'I'};
|
||||
})) {
|
||||
return Invalid;
|
||||
}
|
||||
|
||||
@@ -142,7 +149,7 @@ void QtSoftwareKeyboard::SendTextCheckDialog(std::u16string error_message,
|
||||
void QtSoftwareKeyboard::MainWindowFinishedText(std::optional<std::u16string> text) {
|
||||
// Acquire the HLE mutex
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
text_output(text);
|
||||
text_output(std::move(text));
|
||||
}
|
||||
|
||||
void QtSoftwareKeyboard::MainWindowFinishedCheckDialog() {
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
#include <QDialog>
|
||||
#include <QValidator>
|
||||
#include "common/assert.h"
|
||||
#include "core/frontend/applets/software_keyboard.h"
|
||||
|
||||
class GMainWindow;
|
||||
|
||||
+14
-11
@@ -91,25 +91,25 @@ void EmuThread::run() {
|
||||
|
||||
class GGLContext : public Core::Frontend::GraphicsContext {
|
||||
public:
|
||||
explicit GGLContext(QOpenGLContext* shared_context)
|
||||
: context{std::make_unique<QOpenGLContext>(shared_context)} {
|
||||
surface.setFormat(shared_context->format());
|
||||
surface.create();
|
||||
explicit GGLContext(QOpenGLContext* shared_context) : shared_context{shared_context} {
|
||||
context.setFormat(shared_context->format());
|
||||
context.setShareContext(shared_context);
|
||||
context.create();
|
||||
}
|
||||
|
||||
void MakeCurrent() override {
|
||||
context->makeCurrent(&surface);
|
||||
context.makeCurrent(shared_context->surface());
|
||||
}
|
||||
|
||||
void DoneCurrent() override {
|
||||
context->doneCurrent();
|
||||
context.doneCurrent();
|
||||
}
|
||||
|
||||
void SwapBuffers() override {}
|
||||
|
||||
private:
|
||||
std::unique_ptr<QOpenGLContext> context;
|
||||
QOffscreenSurface surface;
|
||||
QOpenGLContext* shared_context;
|
||||
QOpenGLContext context;
|
||||
};
|
||||
|
||||
// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL
|
||||
@@ -188,7 +188,9 @@ private:
|
||||
GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
|
||||
: QWidget(parent), emu_thread(emu_thread) {
|
||||
setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
|
||||
.arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc));
|
||||
.arg(QString::fromUtf8(Common::g_build_name),
|
||||
QString::fromUtf8(Common::g_scm_branch),
|
||||
QString::fromUtf8(Common::g_scm_desc)));
|
||||
setAttribute(Qt::WA_AcceptTouchEvents);
|
||||
|
||||
InputCommon::Init();
|
||||
@@ -217,7 +219,7 @@ void GRenderWindow::SwapBuffers() {
|
||||
// However:
|
||||
// - The Qt debug runtime prints a bogus warning on the console if `makeCurrent` wasn't called
|
||||
// since the last time `swapBuffers` was executed;
|
||||
// - On macOS, if `makeCurrent` isn't called explicitely, resizing the buffer breaks.
|
||||
// - On macOS, if `makeCurrent` isn't called explicitly, resizing the buffer breaks.
|
||||
context->makeCurrent(child);
|
||||
|
||||
context->swapBuffers(child);
|
||||
@@ -356,7 +358,7 @@ void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) {
|
||||
}
|
||||
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {
|
||||
return std::make_unique<GGLContext>(shared_context.get());
|
||||
return std::make_unique<GGLContext>(context.get());
|
||||
}
|
||||
|
||||
void GRenderWindow::InitRenderTarget() {
|
||||
@@ -379,6 +381,7 @@ void GRenderWindow::InitRenderTarget() {
|
||||
fmt.setVersion(4, 3);
|
||||
if (Settings::values.use_compatibility_profile) {
|
||||
fmt.setProfile(QSurfaceFormat::CompatibilityProfile);
|
||||
fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions);
|
||||
} else {
|
||||
fmt.setProfile(QSurfaceFormat::CoreProfile);
|
||||
}
|
||||
|
||||
+600
-363
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,6 @@
|
||||
#include <string>
|
||||
#include <QVariant>
|
||||
#include "core/settings.h"
|
||||
#include "yuzu/ui_settings.h"
|
||||
|
||||
class QSettings;
|
||||
|
||||
@@ -37,19 +36,51 @@ private:
|
||||
void ReadTouchscreenValues();
|
||||
void ApplyDefaultProfileIfInputInvalid();
|
||||
|
||||
// Read functions bases off the respective config section names.
|
||||
void ReadAudioValues();
|
||||
void ReadControlValues();
|
||||
void ReadCoreValues();
|
||||
void ReadDataStorageValues();
|
||||
void ReadDebuggingValues();
|
||||
void ReadDisabledAddOnValues();
|
||||
void ReadMiscellaneousValues();
|
||||
void ReadPathValues();
|
||||
void ReadRendererValues();
|
||||
void ReadShortcutValues();
|
||||
void ReadSystemValues();
|
||||
void ReadUIValues();
|
||||
void ReadUIGamelistValues();
|
||||
void ReadUILayoutValues();
|
||||
void ReadWebServiceValues();
|
||||
|
||||
void SaveValues();
|
||||
void SavePlayerValues();
|
||||
void SaveDebugValues();
|
||||
void SaveMouseValues();
|
||||
void SaveTouchscreenValues();
|
||||
|
||||
// Save functions based off the respective config section names.
|
||||
void SaveAudioValues();
|
||||
void SaveControlValues();
|
||||
void SaveCoreValues();
|
||||
void SaveDataStorageValues();
|
||||
void SaveDebuggingValues();
|
||||
void SaveDisabledAddOnValues();
|
||||
void SaveMiscellaneousValues();
|
||||
void SavePathValues();
|
||||
void SaveRendererValues();
|
||||
void SaveShortcutValues();
|
||||
void SaveSystemValues();
|
||||
void SaveUIValues();
|
||||
void SaveUIGamelistValues();
|
||||
void SaveUILayoutValues();
|
||||
void SaveWebServiceValues();
|
||||
|
||||
QVariant ReadSetting(const QString& name) const;
|
||||
QVariant ReadSetting(const QString& name, const QVariant& default_value) const;
|
||||
void WriteSetting(const QString& name, const QVariant& value);
|
||||
void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value);
|
||||
|
||||
static const std::array<UISettings::Shortcut, 15> default_hotkeys;
|
||||
|
||||
std::unique_ptr<QSettings> qt_config;
|
||||
std::string qt_config_loc;
|
||||
};
|
||||
|
||||
@@ -16,21 +16,21 @@ ConfigureAudio::ConfigureAudio(QWidget* parent)
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->output_sink_combo_box->clear();
|
||||
ui->output_sink_combo_box->addItem("auto");
|
||||
ui->output_sink_combo_box->addItem(QString::fromUtf8(AudioCore::auto_device_name));
|
||||
for (const char* id : AudioCore::GetSinkIDs()) {
|
||||
ui->output_sink_combo_box->addItem(id);
|
||||
ui->output_sink_combo_box->addItem(QString::fromUtf8(id));
|
||||
}
|
||||
|
||||
connect(ui->volume_slider, &QSlider::valueChanged, this,
|
||||
&ConfigureAudio::setVolumeIndicatorText);
|
||||
|
||||
this->setConfiguration();
|
||||
connect(ui->output_sink_combo_box,
|
||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
|
||||
connect(ui->output_sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
&ConfigureAudio::updateAudioDevices);
|
||||
|
||||
ui->output_sink_combo_box->setEnabled(!Core::System::GetInstance().IsPoweredOn());
|
||||
ui->audio_device_combo_box->setEnabled(!Core::System::GetInstance().IsPoweredOn());
|
||||
const bool is_powered_on = Core::System::GetInstance().IsPoweredOn();
|
||||
ui->output_sink_combo_box->setEnabled(!is_powered_on);
|
||||
ui->audio_device_combo_box->setEnabled(!is_powered_on);
|
||||
}
|
||||
|
||||
ConfigureAudio::~ConfigureAudio() = default;
|
||||
@@ -94,7 +94,7 @@ void ConfigureAudio::applyConfiguration() {
|
||||
|
||||
void ConfigureAudio::updateAudioDevices(int sink_index) {
|
||||
ui->audio_device_combo_box->clear();
|
||||
ui->audio_device_combo_box->addItem(AudioCore::auto_device_name);
|
||||
ui->audio_device_combo_box->addItem(QString::fromUtf8(AudioCore::auto_device_name));
|
||||
|
||||
const std::string sink_id = ui->output_sink_combo_box->itemText(sink_index).toStdString();
|
||||
for (const auto& device : AudioCore::GetDeviceListForSink(sink_id)) {
|
||||
|
||||
@@ -25,9 +25,6 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry)
|
||||
|
||||
adjustSize();
|
||||
ui->selectorList->setCurrentRow(0);
|
||||
|
||||
// Synchronise lists upon initialisation
|
||||
ui->hotkeysTab->EmitHotkeysChanged();
|
||||
}
|
||||
|
||||
ConfigureDialog::~ConfigureDialog() = default;
|
||||
|
||||
@@ -100,13 +100,15 @@ void ConfigureGameList::RetranslateUI() {
|
||||
|
||||
void ConfigureGameList::InitializeIconSizeComboBox() {
|
||||
for (const auto& size : default_icon_sizes) {
|
||||
ui->icon_size_combobox->addItem(size.second, size.first);
|
||||
ui->icon_size_combobox->addItem(QString::fromUtf8(size.second), size.first);
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureGameList::InitializeRowComboBoxes() {
|
||||
for (std::size_t i = 0; i < row_text_names.size(); ++i) {
|
||||
ui->row_1_text_combobox->addItem(row_text_names[i], QVariant::fromValue(i));
|
||||
ui->row_2_text_combobox->addItem(row_text_names[i], QVariant::fromValue(i));
|
||||
const QString row_text_name = QString::fromUtf8(row_text_names[i]);
|
||||
|
||||
ui->row_1_text_combobox->addItem(row_text_name, QVariant::fromValue(i));
|
||||
ui->row_2_text_combobox->addItem(row_text_name, QVariant::fromValue(i));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
|
||||
ui->setupUi(this);
|
||||
|
||||
for (const auto& theme : UISettings::themes) {
|
||||
ui->theme_combobox->addItem(theme.first, theme.second);
|
||||
ui->theme_combobox->addItem(QString::fromUtf8(theme.first),
|
||||
QString::fromUtf8(theme.second));
|
||||
}
|
||||
|
||||
this->setConfiguration();
|
||||
|
||||
@@ -69,16 +69,20 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
|
||||
ConfigureGraphics::~ConfigureGraphics() = default;
|
||||
|
||||
void ConfigureGraphics::setConfiguration() {
|
||||
const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
|
||||
|
||||
ui->resolution_factor_combobox->setCurrentIndex(
|
||||
static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor)));
|
||||
ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit);
|
||||
ui->frame_limit->setValue(Settings::values.frame_limit);
|
||||
ui->use_compatibility_profile->setEnabled(runtime_lock);
|
||||
ui->use_compatibility_profile->setChecked(Settings::values.use_compatibility_profile);
|
||||
ui->use_disk_shader_cache->setEnabled(runtime_lock);
|
||||
ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache);
|
||||
ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation);
|
||||
ui->use_asynchronous_gpu_emulation->setEnabled(!Core::System::GetInstance().IsPoweredOn());
|
||||
ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock);
|
||||
ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation);
|
||||
ui->force_30fps_mode->setEnabled(!Core::System::GetInstance().IsPoweredOn());
|
||||
ui->force_30fps_mode->setEnabled(runtime_lock);
|
||||
ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode);
|
||||
UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green,
|
||||
Settings::values.bg_blue));
|
||||
|
||||
@@ -31,22 +31,6 @@ ConfigureHotkeys::ConfigureHotkeys(QWidget* parent)
|
||||
|
||||
ConfigureHotkeys::~ConfigureHotkeys() = default;
|
||||
|
||||
void ConfigureHotkeys::EmitHotkeysChanged() {
|
||||
emit HotkeysChanged(GetUsedKeyList());
|
||||
}
|
||||
|
||||
QList<QKeySequence> ConfigureHotkeys::GetUsedKeyList() const {
|
||||
QList<QKeySequence> list;
|
||||
for (int r = 0; r < model->rowCount(); r++) {
|
||||
const QStandardItem* parent = model->item(r, 0);
|
||||
for (int r2 = 0; r2 < parent->rowCount(); r2++) {
|
||||
const QStandardItem* keyseq = parent->child(r2, 1);
|
||||
list << QKeySequence::fromString(keyseq->text(), QKeySequence::NativeText);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) {
|
||||
for (const auto& group : registry.hotkey_groups) {
|
||||
auto* parent_item = new QStandardItem(group.first);
|
||||
@@ -83,16 +67,29 @@ void ConfigureHotkeys::Configure(QModelIndex index) {
|
||||
}
|
||||
|
||||
if (IsUsedKey(key_sequence) && key_sequence != QKeySequence(previous_key.toString())) {
|
||||
QMessageBox::critical(this, tr("Error in inputted key"),
|
||||
tr("You're using a key that's already bound."));
|
||||
QMessageBox::warning(this, tr("Conflicting Key Sequence"),
|
||||
tr("The entered key sequence is already assigned to another hotkey."));
|
||||
} else {
|
||||
model->setData(index, key_sequence.toString(QKeySequence::NativeText));
|
||||
EmitHotkeysChanged();
|
||||
}
|
||||
}
|
||||
|
||||
bool ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence) const {
|
||||
return GetUsedKeyList().contains(key_sequence);
|
||||
for (int r = 0; r < model->rowCount(); r++) {
|
||||
const QStandardItem* const parent = model->item(r, 0);
|
||||
|
||||
for (int r2 = 0; r2 < parent->rowCount(); r2++) {
|
||||
const QStandardItem* const key_seq_item = parent->child(r2, 1);
|
||||
const auto key_seq_str = key_seq_item->text();
|
||||
const auto key_seq = QKeySequence::fromString(key_seq_str, QKeySequence::NativeText);
|
||||
|
||||
if (key_sequence == key_seq) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ConfigureHotkeys::applyConfiguration(HotkeyRegistry& registry) {
|
||||
@@ -114,7 +111,6 @@ void ConfigureHotkeys::applyConfiguration(HotkeyRegistry& registry) {
|
||||
}
|
||||
|
||||
registry.SaveHotkeys();
|
||||
Settings::Apply();
|
||||
}
|
||||
|
||||
void ConfigureHotkeys::retranslateUi() {
|
||||
|
||||
@@ -24,8 +24,6 @@ public:
|
||||
void applyConfiguration(HotkeyRegistry& registry);
|
||||
void retranslateUi();
|
||||
|
||||
void EmitHotkeysChanged();
|
||||
|
||||
/**
|
||||
* Populates the hotkey list widget using data from the provided registry.
|
||||
* Called everytime the Configure dialog is opened.
|
||||
@@ -33,13 +31,9 @@ public:
|
||||
*/
|
||||
void Populate(const HotkeyRegistry& registry);
|
||||
|
||||
signals:
|
||||
void HotkeysChanged(QList<QKeySequence> new_key_list);
|
||||
|
||||
private:
|
||||
void Configure(QModelIndex index);
|
||||
bool IsUsedKey(QKeySequence key_sequence) const;
|
||||
QList<QKeySequence> GetUsedKeyList() const;
|
||||
|
||||
std::unique_ptr<Ui::ConfigureHotkeys> ui;
|
||||
|
||||
|
||||
@@ -75,8 +75,8 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
};
|
||||
|
||||
for (auto* controller_box : players_controller) {
|
||||
controller_box->addItems({"None", "Pro Controller", "Dual Joycons", "Single Right Joycon",
|
||||
"Single Left Joycon"});
|
||||
controller_box->addItems({tr("None"), tr("Pro Controller"), tr("Dual Joycons"),
|
||||
tr("Single Right Joycon"), tr("Single Left Joycon")});
|
||||
}
|
||||
|
||||
this->loadConfiguration();
|
||||
@@ -85,9 +85,10 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
connect(ui->restore_defaults_button, &QPushButton::pressed, this,
|
||||
&ConfigureInput::restoreDefaults);
|
||||
|
||||
for (auto* enabled : players_controller)
|
||||
for (auto* enabled : players_controller) {
|
||||
connect(enabled, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||
&ConfigureInput::updateUIEnabled);
|
||||
}
|
||||
connect(ui->use_docked_mode, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled);
|
||||
connect(ui->handheld_connected, &QCheckBox::stateChanged, this,
|
||||
&ConfigureInput::updateUIEnabled);
|
||||
@@ -147,10 +148,12 @@ void ConfigureInput::updateUIEnabled() {
|
||||
bool hit_disabled = false;
|
||||
for (auto* player : players_controller) {
|
||||
player->setDisabled(hit_disabled);
|
||||
if (hit_disabled)
|
||||
if (hit_disabled) {
|
||||
player->setCurrentIndex(0);
|
||||
if (!hit_disabled && player->currentIndex() == 0)
|
||||
}
|
||||
if (!hit_disabled && player->currentIndex() == 0) {
|
||||
hit_disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < players_controller.size(); ++i) {
|
||||
|
||||
@@ -45,7 +45,7 @@ static QString GetKeyName(int key_code) {
|
||||
case Qt::Key_Alt:
|
||||
return QObject::tr("Alt");
|
||||
case Qt::Key_Meta:
|
||||
return "";
|
||||
return {};
|
||||
default:
|
||||
return QKeySequence(key_code).toString();
|
||||
}
|
||||
@@ -65,46 +65,70 @@ static void SetAnalogButton(const Common::ParamPackage& input_param,
|
||||
static QString ButtonToText(const Common::ParamPackage& param) {
|
||||
if (!param.Has("engine")) {
|
||||
return QObject::tr("[not set]");
|
||||
} else if (param.Get("engine", "") == "keyboard") {
|
||||
return GetKeyName(param.Get("code", 0));
|
||||
} else if (param.Get("engine", "") == "sdl") {
|
||||
if (param.Has("hat")) {
|
||||
return QString(QObject::tr("Hat %1 %2"))
|
||||
.arg(param.Get("hat", "").c_str(), param.Get("direction", "").c_str());
|
||||
}
|
||||
if (param.Has("axis")) {
|
||||
return QString(QObject::tr("Axis %1%2"))
|
||||
.arg(param.Get("axis", "").c_str(), param.Get("direction", "").c_str());
|
||||
}
|
||||
if (param.Has("button")) {
|
||||
return QString(QObject::tr("Button %1")).arg(param.Get("button", "").c_str());
|
||||
}
|
||||
return QString();
|
||||
} else {
|
||||
return QObject::tr("[unknown]");
|
||||
}
|
||||
};
|
||||
|
||||
if (param.Get("engine", "") == "keyboard") {
|
||||
return GetKeyName(param.Get("code", 0));
|
||||
}
|
||||
|
||||
if (param.Get("engine", "") == "sdl") {
|
||||
if (param.Has("hat")) {
|
||||
const QString hat_str = QString::fromStdString(param.Get("hat", ""));
|
||||
const QString direction_str = QString::fromStdString(param.Get("direction", ""));
|
||||
|
||||
return QObject::tr("Hat %1 %2").arg(hat_str, direction_str);
|
||||
}
|
||||
|
||||
if (param.Has("axis")) {
|
||||
const QString axis_str = QString::fromStdString(param.Get("axis", ""));
|
||||
const QString direction_str = QString::fromStdString(param.Get("direction", ""));
|
||||
|
||||
return QObject::tr("Axis %1%2").arg(axis_str, direction_str);
|
||||
}
|
||||
|
||||
if (param.Has("button")) {
|
||||
const QString button_str = QString::fromStdString(param.Get("button", ""));
|
||||
|
||||
return QObject::tr("Button %1").arg(button_str);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
return QObject::tr("[unknown]");
|
||||
}
|
||||
|
||||
static QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) {
|
||||
if (!param.Has("engine")) {
|
||||
return QObject::tr("[not set]");
|
||||
} else if (param.Get("engine", "") == "analog_from_button") {
|
||||
}
|
||||
|
||||
if (param.Get("engine", "") == "analog_from_button") {
|
||||
return ButtonToText(Common::ParamPackage{param.Get(dir, "")});
|
||||
} else if (param.Get("engine", "") == "sdl") {
|
||||
}
|
||||
|
||||
if (param.Get("engine", "") == "sdl") {
|
||||
if (dir == "modifier") {
|
||||
return QString(QObject::tr("[unused]"));
|
||||
return QObject::tr("[unused]");
|
||||
}
|
||||
|
||||
if (dir == "left" || dir == "right") {
|
||||
return QString(QObject::tr("Axis %1")).arg(param.Get("axis_x", "").c_str());
|
||||
} else if (dir == "up" || dir == "down") {
|
||||
return QString(QObject::tr("Axis %1")).arg(param.Get("axis_y", "").c_str());
|
||||
const QString axis_x_str = QString::fromStdString(param.Get("axis_x", ""));
|
||||
|
||||
return QObject::tr("Axis %1").arg(axis_x_str);
|
||||
}
|
||||
return QString();
|
||||
} else {
|
||||
return QObject::tr("[unknown]");
|
||||
|
||||
if (dir == "up" || dir == "down") {
|
||||
const QString axis_y_str = QString::fromStdString(param.Get("axis_y", ""));
|
||||
|
||||
return QObject::tr("Axis %1").arg(axis_y_str);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
return QObject::tr("[unknown]");
|
||||
}
|
||||
|
||||
ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index, bool debug)
|
||||
: 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
|
||||
analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog};
|
||||
|
||||
for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
|
||||
if (!button_map[button_id])
|
||||
auto* const button = button_map[button_id];
|
||||
if (button == nullptr) {
|
||||
continue;
|
||||
button_map[button_id]->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(button_map[button_id], &QPushButton::released, [=]() {
|
||||
}
|
||||
|
||||
button->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(button, &QPushButton::released, [=] {
|
||||
handleClick(
|
||||
button_map[button_id],
|
||||
[=](const Common::ParamPackage& params) { buttons_param[button_id] = params; },
|
||||
InputCommon::Polling::DeviceType::Button);
|
||||
});
|
||||
connect(button_map[button_id], &QPushButton::customContextMenuRequested,
|
||||
[=](const QPoint& menu_location) {
|
||||
QMenu context_menu;
|
||||
context_menu.addAction(tr("Clear"), [&] {
|
||||
buttons_param[button_id].Clear();
|
||||
button_map[button_id]->setText(tr("[not set]"));
|
||||
});
|
||||
context_menu.addAction(tr("Restore Default"), [&] {
|
||||
buttons_param[button_id] = Common::ParamPackage{
|
||||
InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
|
||||
button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
|
||||
});
|
||||
context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
|
||||
});
|
||||
connect(button, &QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) {
|
||||
QMenu context_menu;
|
||||
context_menu.addAction(tr("Clear"), [&] {
|
||||
buttons_param[button_id].Clear();
|
||||
button_map[button_id]->setText(tr("[not set]"));
|
||||
});
|
||||
context_menu.addAction(tr("Restore Default"), [&] {
|
||||
buttons_param[button_id] = Common::ParamPackage{
|
||||
InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
|
||||
button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
|
||||
});
|
||||
context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
|
||||
});
|
||||
}
|
||||
|
||||
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
|
||||
for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
|
||||
if (!analog_map_buttons[analog_id][sub_button_id])
|
||||
auto* const analog_button = analog_map_buttons[analog_id][sub_button_id];
|
||||
if (analog_button == nullptr) {
|
||||
continue;
|
||||
analog_map_buttons[analog_id][sub_button_id]->setContextMenuPolicy(
|
||||
Qt::CustomContextMenu);
|
||||
connect(analog_map_buttons[analog_id][sub_button_id], &QPushButton::released, [=]() {
|
||||
}
|
||||
|
||||
analog_button->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(analog_button, &QPushButton::released, [=]() {
|
||||
handleClick(analog_map_buttons[analog_id][sub_button_id],
|
||||
[=](const Common::ParamPackage& params) {
|
||||
SetAnalogButton(params, analogs_param[analog_id],
|
||||
@@ -253,8 +281,8 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
||||
},
|
||||
InputCommon::Polling::DeviceType::Button);
|
||||
});
|
||||
connect(analog_map_buttons[analog_id][sub_button_id],
|
||||
&QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) {
|
||||
connect(analog_button, &QPushButton::customContextMenuRequested,
|
||||
[=](const QPoint& menu_location) {
|
||||
QMenu context_menu;
|
||||
context_menu.addAction(tr("Clear"), [&] {
|
||||
analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
|
||||
@@ -272,7 +300,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
||||
menu_location));
|
||||
});
|
||||
}
|
||||
connect(analog_map_stick[analog_id], &QPushButton::released, [=]() {
|
||||
connect(analog_map_stick[analog_id], &QPushButton::released, [=] {
|
||||
QMessageBox::information(this, tr("Information"),
|
||||
tr("After pressing OK, first move your joystick horizontally, "
|
||||
"and then vertically."));
|
||||
@@ -351,7 +379,7 @@ void ConfigureInputPlayer::OnControllerButtonClick(int i) {
|
||||
return;
|
||||
controller_colors[i] = new_bg_color;
|
||||
controller_color_buttons[i]->setStyleSheet(
|
||||
QString("QPushButton { background-color: %1 }").arg(controller_colors[i].name()));
|
||||
QStringLiteral("QPushButton { background-color: %1 }").arg(controller_colors[i].name()));
|
||||
}
|
||||
|
||||
void ConfigureInputPlayer::loadConfiguration() {
|
||||
@@ -388,7 +416,8 @@ void ConfigureInputPlayer::loadConfiguration() {
|
||||
|
||||
for (std::size_t i = 0; i < colors.size(); ++i) {
|
||||
controller_color_buttons[i]->setStyleSheet(
|
||||
QString("QPushButton { background-color: %1 }").arg(controller_colors[i].name()));
|
||||
QStringLiteral("QPushButton { background-color: %1 }")
|
||||
.arg(controller_colors[i].name()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,14 +439,22 @@ void ConfigureInputPlayer::restoreDefaults() {
|
||||
|
||||
void ConfigureInputPlayer::ClearAll() {
|
||||
for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
|
||||
if (button_map[button_id] && button_map[button_id]->isEnabled())
|
||||
buttons_param[button_id].Clear();
|
||||
const auto* const button = button_map[button_id];
|
||||
if (button == nullptr || !button->isEnabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
buttons_param[button_id].Clear();
|
||||
}
|
||||
|
||||
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
|
||||
for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
|
||||
if (analog_map_buttons[analog_id][sub_button_id] &&
|
||||
analog_map_buttons[analog_id][sub_button_id]->isEnabled())
|
||||
analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
|
||||
const auto* const analog_button = analog_map_buttons[analog_id][sub_button_id];
|
||||
if (analog_button == nullptr || !analog_button->isEnabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,10 +468,14 @@ void ConfigureInputPlayer::updateButtonLabels() {
|
||||
|
||||
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
|
||||
for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
|
||||
if (analog_map_buttons[analog_id][sub_button_id]) {
|
||||
analog_map_buttons[analog_id][sub_button_id]->setText(
|
||||
AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
|
||||
auto* const analog_button = analog_map_buttons[analog_id][sub_button_id];
|
||||
|
||||
if (analog_button == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
analog_button->setText(
|
||||
AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
|
||||
}
|
||||
analog_map_stick[analog_id]->setText(tr("Set Analog Stick"));
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user