diff options
Diffstat (limited to 'src')
166 files changed, 5740 insertions, 3114 deletions
diff --git a/src/common/assert.h b/src/common/assert.h index 3ee07f6a20..0d4eddc194 100644 --- a/src/common/assert.h +++ b/src/common/assert.h @@ -30,15 +30,14 @@ __declspec(noinline, noreturn) #define ASSERT(_a_) \ do \ if (!(_a_)) { \ - assert_noinline_call([] { NGLOG_CRITICAL(Debug, "Assertion Failed!"); }); \ + assert_noinline_call([] { LOG_CRITICAL(Debug, "Assertion Failed!"); }); \ } \ while (0) #define ASSERT_MSG(_a_, ...) \ do \ if (!(_a_)) { \ - assert_noinline_call( \ - [&] { NGLOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \ + assert_noinline_call([&] { LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \ } \ while (0) @@ -53,5 +52,5 @@ __declspec(noinline, noreturn) #define DEBUG_ASSERT_MSG(_a_, _desc_, ...) #endif -#define UNIMPLEMENTED() DEBUG_ASSERT_MSG(false, "Unimplemented code!") +#define UNIMPLEMENTED() LOG_CRITICAL(Debug, "Unimplemented code!") #define UNIMPLEMENTED_MSG(...) ASSERT_MSG(false, __VA_ARGS__) diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index 7cf7b79979..995938d0b4 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h @@ -4,7 +4,7 @@ #pragma once -#if !defined(ARCHITECTURE_x86_64) && !defined(_M_ARM) +#if !defined(ARCHITECTURE_x86_64) && !defined(ARCHITECTURE_ARM) #include <cstdlib> // for exit #endif #include "common/common_types.h" @@ -30,7 +30,7 @@ #ifdef ARCHITECTURE_x86_64 #define Crash() __asm__ __volatile__("int $3") -#elif defined(_M_ARM) +#elif defined(ARCHITECTURE_ARM) #define Crash() __asm__ __volatile__("trap") #else #define Crash() exit(1) diff --git a/src/common/common_paths.h b/src/common/common_paths.h index 0a6132dabf..9bf3efaf27 100644 --- a/src/common/common_paths.h +++ b/src/common/common_paths.h @@ -32,12 +32,15 @@ #define SDMC_DIR "sdmc" #define NAND_DIR "nand" #define SYSDATA_DIR "sysdata" +#define LOG_DIR "log" // Filenames // Files in the directory returned by GetUserPath(D_CONFIG_IDX) #define EMU_CONFIG "emu.ini" #define DEBUGGER_CONFIG "debugger.ini" #define LOGGER_CONFIG "logger.ini" +// Files in the directory returned by GetUserPath(D_LOGS_IDX) +#define LOG_FILE "yuzu_log.txt" // Sys files #define SHARED_FONT "shared_font.bin" diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 2d0b81c6ee..7213abe18d 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -118,7 +118,7 @@ bool IsDirectory(const std::string& filename) { #endif if (result < 0) { - NGLOG_DEBUG(Common_Filesystem, "stat failed on {}: {}", filename, GetLastErrorMsg()); + LOG_DEBUG(Common_Filesystem, "stat failed on {}: {}", filename, GetLastErrorMsg()); return false; } @@ -128,29 +128,29 @@ bool IsDirectory(const std::string& filename) { // Deletes a given filename, return true on success // Doesn't supports deleting a directory bool Delete(const std::string& filename) { - NGLOG_TRACE(Common_Filesystem, "file {}", filename); + LOG_TRACE(Common_Filesystem, "file {}", filename); // Return true because we care about the file no // being there, not the actual delete. if (!Exists(filename)) { - NGLOG_DEBUG(Common_Filesystem, "{} does not exist", filename); + LOG_DEBUG(Common_Filesystem, "{} does not exist", filename); return true; } // We can't delete a directory if (IsDirectory(filename)) { - NGLOG_ERROR(Common_Filesystem, "Failed: {} is a directory", filename); + LOG_ERROR(Common_Filesystem, "Failed: {} is a directory", filename); return false; } #ifdef _WIN32 if (!DeleteFileW(Common::UTF8ToUTF16W(filename).c_str())) { - NGLOG_ERROR(Common_Filesystem, "DeleteFile failed on {}: {}", filename, GetLastErrorMsg()); + LOG_ERROR(Common_Filesystem, "DeleteFile failed on {}: {}", filename, GetLastErrorMsg()); return false; } #else if (unlink(filename.c_str()) == -1) { - NGLOG_ERROR(Common_Filesystem, "unlink failed on {}: {}", filename, GetLastErrorMsg()); + LOG_ERROR(Common_Filesystem, "unlink failed on {}: {}", filename, GetLastErrorMsg()); return false; } #endif @@ -160,16 +160,16 @@ bool Delete(const std::string& filename) { // Returns true if successful, or path already exists. bool CreateDir(const std::string& path) { - NGLOG_TRACE(Common_Filesystem, "directory {}", path); + LOG_TRACE(Common_Filesystem, "directory {}", path); #ifdef _WIN32 if (::CreateDirectoryW(Common::UTF8ToUTF16W(path).c_str(), nullptr)) return true; DWORD error = GetLastError(); if (error == ERROR_ALREADY_EXISTS) { - NGLOG_DEBUG(Common_Filesystem, "CreateDirectory failed on {}: already exists", path); + LOG_DEBUG(Common_Filesystem, "CreateDirectory failed on {}: already exists", path); return true; } - NGLOG_ERROR(Common_Filesystem, "CreateDirectory failed on {}: {}", path, error); + LOG_ERROR(Common_Filesystem, "CreateDirectory failed on {}: {}", path, error); return false; #else if (mkdir(path.c_str(), 0755) == 0) @@ -178,11 +178,11 @@ bool CreateDir(const std::string& path) { int err = errno; if (err == EEXIST) { - NGLOG_DEBUG(Common_Filesystem, "mkdir failed on {}: already exists", path); + LOG_DEBUG(Common_Filesystem, "mkdir failed on {}: already exists", path); return true; } - NGLOG_ERROR(Common_Filesystem, "mkdir failed on {}: {}", path, strerror(err)); + LOG_ERROR(Common_Filesystem, "mkdir failed on {}: {}", path, strerror(err)); return false; #endif } @@ -190,10 +190,10 @@ bool CreateDir(const std::string& path) { // Creates the full path of fullPath returns true on success bool CreateFullPath(const std::string& fullPath) { int panicCounter = 100; - NGLOG_TRACE(Common_Filesystem, "path {}", fullPath); + LOG_TRACE(Common_Filesystem, "path {}", fullPath); if (FileUtil::Exists(fullPath)) { - NGLOG_DEBUG(Common_Filesystem, "path exists {}", fullPath); + LOG_DEBUG(Common_Filesystem, "path exists {}", fullPath); return true; } @@ -209,14 +209,14 @@ bool CreateFullPath(const std::string& fullPath) { // Include the '/' so the first call is CreateDir("/") rather than CreateDir("") std::string const subPath(fullPath.substr(0, position + 1)); if (!FileUtil::IsDirectory(subPath) && !FileUtil::CreateDir(subPath)) { - NGLOG_ERROR(Common, "CreateFullPath: directory creation failed"); + LOG_ERROR(Common, "CreateFullPath: directory creation failed"); return false; } // A safety check panicCounter--; if (panicCounter <= 0) { - NGLOG_ERROR(Common, "CreateFullPath: directory structure is too deep"); + LOG_ERROR(Common, "CreateFullPath: directory structure is too deep"); return false; } position++; @@ -225,11 +225,11 @@ bool CreateFullPath(const std::string& fullPath) { // Deletes a directory filename, returns true on success bool DeleteDir(const std::string& filename) { - NGLOG_TRACE(Common_Filesystem, "directory {}", filename); + LOG_TRACE(Common_Filesystem, "directory {}", filename); // check if a directory if (!FileUtil::IsDirectory(filename)) { - NGLOG_ERROR(Common_Filesystem, "Not a directory {}", filename); + LOG_ERROR(Common_Filesystem, "Not a directory {}", filename); return false; } @@ -240,14 +240,14 @@ bool DeleteDir(const std::string& filename) { if (rmdir(filename.c_str()) == 0) return true; #endif - NGLOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg()); + LOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg()); return false; } // renames file srcFilename to destFilename, returns true on success bool Rename(const std::string& srcFilename, const std::string& destFilename) { - NGLOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename); + LOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename); #ifdef _WIN32 if (_wrename(Common::UTF8ToUTF16W(srcFilename).c_str(), Common::UTF8ToUTF16W(destFilename).c_str()) == 0) @@ -256,21 +256,21 @@ bool Rename(const std::string& srcFilename, const std::string& destFilename) { if (rename(srcFilename.c_str(), destFilename.c_str()) == 0) return true; #endif - NGLOG_ERROR(Common_Filesystem, "failed {} --> {}: {}", srcFilename, destFilename, - GetLastErrorMsg()); + LOG_ERROR(Common_Filesystem, "failed {} --> {}: {}", srcFilename, destFilename, + GetLastErrorMsg()); return false; } // copies file srcFilename to destFilename, returns true on success bool Copy(const std::string& srcFilename, const std::string& destFilename) { - NGLOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename); + LOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename); #ifdef _WIN32 if (CopyFileW(Common::UTF8ToUTF16W(srcFilename).c_str(), Common::UTF8ToUTF16W(destFilename).c_str(), FALSE)) return true; - NGLOG_ERROR(Common_Filesystem, "failed {} --> {}: {}", srcFilename, destFilename, - GetLastErrorMsg()); + LOG_ERROR(Common_Filesystem, "failed {} --> {}: {}", srcFilename, destFilename, + GetLastErrorMsg()); return false; #else @@ -282,8 +282,8 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) { // Open input file FILE* input = fopen(srcFilename.c_str(), "rb"); if (!input) { - NGLOG_ERROR(Common_Filesystem, "opening input failed {} --> {}: {}", srcFilename, - destFilename, GetLastErrorMsg()); + LOG_ERROR(Common_Filesystem, "opening input failed {} --> {}: {}", srcFilename, + destFilename, GetLastErrorMsg()); return false; } @@ -291,8 +291,8 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) { FILE* output = fopen(destFilename.c_str(), "wb"); if (!output) { fclose(input); - NGLOG_ERROR(Common_Filesystem, "opening output failed {} --> {}: {}", srcFilename, - destFilename, GetLastErrorMsg()); + LOG_ERROR(Common_Filesystem, "opening output failed {} --> {}: {}", srcFilename, + destFilename, GetLastErrorMsg()); return false; } @@ -302,8 +302,8 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) { size_t rnum = fread(buffer, sizeof(char), BSIZE, input); if (rnum != BSIZE) { if (ferror(input) != 0) { - NGLOG_ERROR(Common_Filesystem, "failed reading from source, {} --> {}: {}", - srcFilename, destFilename, GetLastErrorMsg()); + LOG_ERROR(Common_Filesystem, "failed reading from source, {} --> {}: {}", + srcFilename, destFilename, GetLastErrorMsg()); goto bail; } } @@ -311,8 +311,8 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) { // write output size_t wnum = fwrite(buffer, sizeof(char), rnum, output); if (wnum != rnum) { - NGLOG_ERROR(Common_Filesystem, "failed writing to output, {} --> {}: {}", srcFilename, - destFilename, GetLastErrorMsg()); + LOG_ERROR(Common_Filesystem, "failed writing to output, {} --> {}: {}", srcFilename, + destFilename, GetLastErrorMsg()); goto bail; } } @@ -332,12 +332,12 @@ bail: // Returns the size of filename (64bit) u64 GetSize(const std::string& filename) { if (!Exists(filename)) { - NGLOG_ERROR(Common_Filesystem, "failed {}: No such file", filename); + LOG_ERROR(Common_Filesystem, "failed {}: No such file", filename); return 0; } if (IsDirectory(filename)) { - NGLOG_ERROR(Common_Filesystem, "failed {}: is a directory", filename); + LOG_ERROR(Common_Filesystem, "failed {}: is a directory", filename); return 0; } @@ -348,11 +348,11 @@ u64 GetSize(const std::string& filename) { if (stat(filename.c_str(), &buf) == 0) #endif { - NGLOG_TRACE(Common_Filesystem, "{}: {}", filename, buf.st_size); + LOG_TRACE(Common_Filesystem, "{}: {}", filename, buf.st_size); return buf.st_size; } - NGLOG_ERROR(Common_Filesystem, "Stat failed {}: {}", filename, GetLastErrorMsg()); + LOG_ERROR(Common_Filesystem, "Stat failed {}: {}", filename, GetLastErrorMsg()); return 0; } @@ -360,7 +360,7 @@ u64 GetSize(const std::string& filename) { u64 GetSize(const int fd) { struct stat buf; if (fstat(fd, &buf) != 0) { - NGLOG_ERROR(Common_Filesystem, "GetSize: stat failed {}: {}", fd, GetLastErrorMsg()); + LOG_ERROR(Common_Filesystem, "GetSize: stat failed {}: {}", fd, GetLastErrorMsg()); return 0; } return buf.st_size; @@ -371,14 +371,12 @@ u64 GetSize(FILE* f) { // can't use off_t here because it can be 32-bit u64 pos = ftello(f); if (fseeko(f, 0, SEEK_END) != 0) { - NGLOG_ERROR(Common_Filesystem, "GetSize: seek failed {}: {}", fmt::ptr(f), - GetLastErrorMsg()); + LOG_ERROR(Common_Filesystem, "GetSize: seek failed {}: {}", fmt::ptr(f), GetLastErrorMsg()); return 0; } u64 size = ftello(f); if ((size != pos) && (fseeko(f, pos, SEEK_SET) != 0)) { - NGLOG_ERROR(Common_Filesystem, "GetSize: seek failed {}: {}", fmt::ptr(f), - GetLastErrorMsg()); + LOG_ERROR(Common_Filesystem, "GetSize: seek failed {}: {}", fmt::ptr(f), GetLastErrorMsg()); return 0; } return size; @@ -386,10 +384,10 @@ u64 GetSize(FILE* f) { // creates an empty file filename, returns true on success bool CreateEmptyFile(const std::string& filename) { - NGLOG_TRACE(Common_Filesystem, "{}", filename); + LOG_TRACE(Common_Filesystem, "{}", filename); if (!FileUtil::IOFile(filename, "wb")) { - NGLOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg()); + LOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg()); return false; } @@ -398,7 +396,7 @@ bool CreateEmptyFile(const std::string& filename) { bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string& directory, DirectoryEntryCallable callback) { - NGLOG_TRACE(Common_Filesystem, "directory {}", directory); + LOG_TRACE(Common_Filesystem, "directory {}", directory); // How many files + directories we found unsigned found_entries = 0; @@ -556,7 +554,7 @@ std::string GetCurrentDir() { char* dir; if (!(dir = getcwd(nullptr, 0))) { #endif - NGLOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: {}", GetLastErrorMsg()); + LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: {}", GetLastErrorMsg()); return nullptr; } #ifdef _WIN32 @@ -676,11 +674,11 @@ std::string GetSysDirectory() { #endif sysDir += DIR_SEP; - NGLOG_DEBUG(Common_Filesystem, "Setting to {}:", sysDir); + LOG_DEBUG(Common_Filesystem, "Setting to {}:", sysDir); return sysDir; } -// Returns a string with a Citra data dir or file in the user's home +// 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(const unsigned int DirIDX, const std::string& newPath) { static std::string paths[NUM_PATH_INDICES]; @@ -692,7 +690,7 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string& new if (!FileUtil::IsDirectory(paths[D_USER_IDX])) { paths[D_USER_IDX] = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP; } else { - NGLOG_INFO(Common_Filesystem, "Using the local user directory"); + LOG_INFO(Common_Filesystem, "Using the local user directory"); } paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP; @@ -715,11 +713,13 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string& new paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP; paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP; + // TODO: Put the logs in a better location for each OS + paths[D_LOGS_IDX] = paths[D_USER_IDX] + LOG_DIR DIR_SEP; } if (!newPath.empty()) { if (!FileUtil::IsDirectory(newPath)) { - NGLOG_ERROR(Common_Filesystem, "Invalid path specified {}", newPath); + LOG_ERROR(Common_Filesystem, "Invalid path specified {}", newPath); return paths[DirIDX]; } else { paths[DirIDX] = newPath; @@ -801,8 +801,8 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam IOFile::IOFile() {} -IOFile::IOFile(const std::string& filename, const char openmode[]) { - Open(filename, openmode); +IOFile::IOFile(const std::string& filename, const char openmode[], int flags) { + Open(filename, openmode, flags); } IOFile::~IOFile() { @@ -823,11 +823,16 @@ void IOFile::Swap(IOFile& other) noexcept { std::swap(m_good, other.m_good); } -bool IOFile::Open(const std::string& filename, const char openmode[]) { +bool IOFile::Open(const std::string& filename, const char openmode[], int flags) { Close(); #ifdef _WIN32 - _wfopen_s(&m_file, Common::UTF8ToUTF16W(filename).c_str(), - Common::UTF8ToUTF16W(openmode).c_str()); + if (flags != 0) { + m_file = _wfsopen(Common::UTF8ToUTF16W(filename).c_str(), + Common::UTF8ToUTF16W(openmode).c_str(), flags); + } else { + _wfopen_s(&m_file, Common::UTF8ToUTF16W(filename).c_str(), + Common::UTF8ToUTF16W(openmode).c_str()); + } #else m_file = fopen(filename.c_str(), openmode); #endif diff --git a/src/common/file_util.h b/src/common/file_util.h index fc6b3ea466..5bc7fbf7c5 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h @@ -156,7 +156,10 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam class IOFile : public NonCopyable { public: IOFile(); - IOFile(const std::string& filename, const char openmode[]); + // flags is used for windows specific file open mode flags, which + // allows yuzu to open the logs in shared write mode, so that the file + // isn't considered "locked" while yuzu is open and people can open the log file and view it + IOFile(const std::string& filename, const char openmode[], int flags = 0); ~IOFile(); @@ -165,7 +168,7 @@ public: void Swap(IOFile& other) noexcept; - bool Open(const std::string& filename, const char openmode[]); + bool Open(const std::string& filename, const char openmode[], int flags = 0); bool Close(); template <typename T> @@ -220,6 +223,10 @@ public: return WriteArray(&object, 1); } + size_t WriteString(const std::string& str) { + return WriteArray(str.c_str(), str.length()); + } + bool IsOpen() const { return nullptr != m_file; } diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index c26b200620..242914c6ae 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -2,16 +2,145 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <utility> +#include <algorithm> +#include <array> +#include <chrono> +#include <condition_variable> +#include <memory> +#include <thread> +#ifdef _WIN32 +#include <share.h> // For _SH_DENYWR +#else +#define _SH_DENYWR 0 +#endif #include "common/assert.h" +#include "common/common_funcs.h" // snprintf compatibility define #include "common/logging/backend.h" -#include "common/logging/filter.h" #include "common/logging/log.h" #include "common/logging/text_formatter.h" #include "common/string_util.h" +#include "common/threadsafe_queue.h" namespace Log { +/** + * Static state as a singleton. + */ +class Impl { +public: + static Impl& Instance() { + static Impl backend; + return backend; + } + + Impl(Impl const&) = delete; + const Impl& operator=(Impl const&) = delete; + + void PushEntry(Entry e) { + std::lock_guard<std::mutex> lock(message_mutex); + message_queue.Push(std::move(e)); + message_cv.notify_one(); + } + + void AddBackend(std::unique_ptr<Backend> backend) { + std::lock_guard<std::mutex> lock(writing_mutex); + backends.push_back(std::move(backend)); + } + + void RemoveBackend(const std::string& backend_name) { + std::lock_guard<std::mutex> lock(writing_mutex); + auto it = std::remove_if(backends.begin(), backends.end(), [&backend_name](const auto& i) { + return !strcmp(i->GetName(), backend_name.c_str()); + }); + backends.erase(it, backends.end()); + } + + const Filter& GetGlobalFilter() const { + return filter; + } + + void SetGlobalFilter(const Filter& f) { + filter = f; + } + + Backend* GetBackend(const std::string& backend_name) { + auto it = std::find_if(backends.begin(), backends.end(), [&backend_name](const auto& i) { + return !strcmp(i->GetName(), backend_name.c_str()); + }); + if (it == backends.end()) + return nullptr; + return it->get(); + } + +private: + Impl() { + backend_thread = std::thread([&] { + Entry entry; + auto write_logs = [&](Entry& e) { + std::lock_guard<std::mutex> lock(writing_mutex); + for (const auto& backend : backends) { + backend->Write(e); + } + }; + while (true) { + std::unique_lock<std::mutex> lock(message_mutex); + message_cv.wait(lock, [&] { return !running || message_queue.Pop(entry); }); + if (!running) { + break; + } + write_logs(entry); + } + // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a case + // where a system is repeatedly spamming logs even on close. + constexpr int MAX_LOGS_TO_WRITE = 100; + int logs_written = 0; + while (logs_written++ < MAX_LOGS_TO_WRITE && message_queue.Pop(entry)) { + write_logs(entry); + } + }); + } + + ~Impl() { + running = false; + message_cv.notify_one(); + backend_thread.join(); + } + + std::atomic_bool running{true}; + std::mutex message_mutex, writing_mutex; + std::condition_variable message_cv; + std::thread backend_thread; + std::vector<std::unique_ptr<Backend>> backends; + Common::MPSCQueue<Log::Entry> message_queue; + Filter filter; +}; + +void ConsoleBackend::Write(const Entry& entry) { + PrintMessage(entry); +} + +void ColorConsoleBackend::Write(const Entry& entry) { + PrintColoredMessage(entry); +} + +// _SH_DENYWR allows read only access to the file for other programs. +// It is #defined to 0 on other platforms +FileBackend::FileBackend(const std::string& filename) + : file(filename, "w", _SH_DENYWR), bytes_written(0) {} + +void FileBackend::Write(const Entry& entry) { + // prevent logs from going over the maximum size (in case its spamming and the user doesn't + // know) + constexpr size_t MAX_BYTES_WRITTEN = 50 * 1024L * 1024L; + if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) { + return; + } + bytes_written += file.WriteString(FormatLogMessage(entry) + '\n'); + if (entry.log_level >= Level::Error) { + file.Flush(); + } +} + /// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this. #define ALL_LOG_CLASSES() \ CLS(Log) \ @@ -125,20 +254,32 @@ Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsign return entry; } -static Filter* filter = nullptr; +void SetGlobalFilter(const Filter& filter) { + Impl::Instance().SetGlobalFilter(filter); +} + +void AddBackend(std::unique_ptr<Backend> backend) { + Impl::Instance().AddBackend(std::move(backend)); +} -void SetFilter(Filter* new_filter) { - filter = new_filter; +void RemoveBackend(const std::string& backend_name) { + Impl::Instance().RemoveBackend(backend_name); +} + +Backend* GetBackend(const std::string& backend_name) { + return Impl::Instance().GetBackend(backend_name); } void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, unsigned int line_num, const char* function, const char* format, const fmt::format_args& args) { - if (filter && !filter->CheckMessage(log_class, log_level)) + auto filter = Impl::Instance().GetGlobalFilter(); + if (!filter.CheckMessage(log_class, log_level)) return; + Entry entry = CreateEntry(log_class, log_level, filename, line_num, function, fmt::vformat(format, args)); - PrintColoredMessage(entry); + Impl::Instance().PushEntry(std::move(entry)); } -} // namespace Log +} // namespace Log
\ No newline at end of file diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h index 7e81efb238..57cdf6b2de 100644 --- a/src/common/logging/backend.h +++ b/src/common/logging/backend.h @@ -1,13 +1,15 @@ // Copyright 2014 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. - #pragma once #include <chrono> #include <cstdarg> +#include <memory> #include <string> #include <utility> +#include "common/file_util.h" +#include "common/logging/filter.h" #include "common/logging/log.h" namespace Log { @@ -35,6 +37,80 @@ struct Entry { }; /** + * Interface for logging backends. As loggers can be created and removed at runtime, this can be + * used by a frontend for adding a custom logging backend as needed + */ +class Backend { +public: + virtual ~Backend() = default; + virtual void SetFilter(const Filter& new_filter) { + filter = new_filter; + } + virtual const char* GetName() const = 0; + virtual void Write(const Entry& entry) = 0; + +private: + Filter filter; +}; + +/** + * Backend that writes to stderr without any color commands + */ +class ConsoleBackend : public Backend { +public: + static const char* Name() { + return "console"; + } + const char* GetName() const override { + return Name(); + } + void Write(const Entry& entry) override; +}; + +/** + * Backend that writes to stderr and with color + */ +class ColorConsoleBackend : public Backend { +public: + static const char* Name() { + return "color_console"; + } + + const char* GetName() const override { + return Name(); + } + void Write(const Entry& entry) override; +}; + +/** + * Backend that writes to a file passed into the constructor + */ +class FileBackend : public Backend { +public: + explicit FileBackend(const std::string& filename); + + static const char* Name() { + return "file"; + } + + const char* GetName() const override { + return Name(); + } + + void Write(const Entry& entry) override; + +private: + FileUtil::IOFile file; + size_t bytes_written; +}; + +void AddBackend(std::unique_ptr<Backend> backend); + +void RemoveBackend(const std::string& backend_name); + +Backend* GetBackend(const std::string& backend_name); + +/** * Returns the name of the passed log class as a C-string. Subclasses are separated by periods * instead of underscores as in the enumeration. */ @@ -49,5 +125,10 @@ const char* GetLevelName(Level log_level); Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr, const char* function, std::string message); -void SetFilter(Filter* filter); -} // namespace Log +/** + * The global filter will prevent any messages from even being processed if they are filtered. Each + * backend can have a filter, but if the level is lower than the global filter, the backend will + * never get the message + */ +void SetGlobalFilter(const Filter& filter); +} // namespace Log
\ No newline at end of file diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 428723dcee..4e783a577b 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -65,14 +65,14 @@ bool Filter::ParseFilterRule(const std::string::const_iterator begin, const std::string::const_iterator end) { auto level_separator = std::find(begin, end, ':'); if (level_separator == end) { - NGLOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: %s", - std::string(begin, end).c_str()); + LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: {}", + std::string(begin, end)); return false; } const Level level = GetLevelByName(level_separator + 1, end); if (level == Level::Count) { - NGLOG_ERROR(Log, "Unknown log level in filter: %s", std::string(begin, end).c_str()); + LOG_ERROR(Log, "Unknown log level in filter: {}", std::string(begin, end)); return false; } @@ -83,7 +83,7 @@ bool Filter::ParseFilterRule(const std::string::const_iterator begin, const Class log_class = GetClassByName(begin, level_separator); if (log_class == Class::Count) { - NGLOG_ERROR(Log, "Unknown log class in filter: %s", std::string(begin, end).c_str()); + LOG_ERROR(Log, "Unknown log class in filter: {}", std::string(begin, end)); return false; } diff --git a/src/common/logging/log.h b/src/common/logging/log.h index c5015531c5..e96c90e16b 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h @@ -109,25 +109,25 @@ void FmtLogMessage(Class log_class, Level log_level, const char* filename, unsig } // namespace Log #ifdef _DEBUG -#define NGLOG_TRACE(log_class, ...) \ +#define LOG_TRACE(log_class, ...) \ ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Trace, __FILE__, __LINE__, \ __func__, __VA_ARGS__) #else -#define NGLOG_TRACE(log_class, fmt, ...) (void(0)) +#define LOG_TRACE(log_class, fmt, ...) (void(0)) #endif -#define NGLOG_DEBUG(log_class, ...) \ +#define LOG_DEBUG(log_class, ...) \ ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Debug, __FILE__, __LINE__, \ __func__, __VA_ARGS__) -#define NGLOG_INFO(log_class, ...) \ +#define LOG_INFO(log_class, ...) \ ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Info, __FILE__, __LINE__, \ __func__, __VA_ARGS__) -#define NGLOG_WARNING(log_class, ...) \ +#define LOG_WARNING(log_class, ...) \ ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Warning, __FILE__, __LINE__, \ __func__, __VA_ARGS__) -#define NGLOG_ERROR(log_class, ...) \ +#define LOG_ERROR(log_class, ...) \ ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Error, __FILE__, __LINE__, \ __func__, __VA_ARGS__) -#define NGLOG_CRITICAL(log_class, ...) \ +#define LOG_CRITICAL(log_class, ...) \ ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Critical, __FILE__, __LINE__, \ __func__, __VA_ARGS__) diff --git a/src/common/memory_util.cpp b/src/common/memory_util.cpp index 4d1ec8fb98..09462ccee0 100644 --- a/src/common/memory_util.cpp +++ b/src/common/memory_util.cpp @@ -16,7 +16,7 @@ #include <sys/mman.h> #endif -#if !defined(_WIN32) && defined(ARCHITECTURE_X64) && !defined(MAP_32BIT) +#if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT) #include <unistd.h> #define PAGE_MASK (getpagesize() - 1) #define round_page(x) ((((unsigned long)(x)) + PAGE_MASK) & ~(PAGE_MASK)) @@ -30,7 +30,7 @@ void* AllocateExecutableMemory(size_t size, bool low) { void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); #else static char* map_hint = nullptr; -#if defined(ARCHITECTURE_X64) && !defined(MAP_32BIT) +#if defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT) // This OS has no flag to enforce allocation below the 4 GB boundary, // but if we hint that we want a low address it is very likely we will // get one. @@ -42,7 +42,7 @@ void* AllocateExecutableMemory(size_t size, bool low) { #endif void* ptr = mmap(map_hint, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE -#if defined(ARCHITECTURE_X64) && defined(MAP_32BIT) +#if defined(ARCHITECTURE_x86_64) && defined(MAP_32BIT) | (low ? MAP_32BIT : 0) #endif , @@ -55,9 +55,9 @@ void* AllocateExecutableMemory(size_t size, bool low) { if (ptr == MAP_FAILED) { ptr = nullptr; #endif - NGLOG_ERROR(Common_Memory, "Failed to allocate executable memory"); + LOG_ERROR(Common_Memory, "Failed to allocate executable memory"); } -#if !defined(_WIN32) && defined(ARCHITECTURE_X64) && !defined(MAP_32BIT) +#if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT) else { if (low) { map_hint += size; @@ -68,7 +68,7 @@ void* AllocateExecutableMemory(size_t size, bool low) { #if EMU_ARCH_BITS == 64 if ((u64)ptr >= 0x80000000 && low == true) - NGLOG_ERROR(Common_Memory, "Executable memory ended up above 2GB!"); + LOG_ERROR(Common_Memory, "Executable memory ended up above 2GB!"); #endif return ptr; @@ -85,7 +85,7 @@ void* AllocateMemoryPages(size_t size) { #endif if (ptr == nullptr) - NGLOG_ERROR(Common_Memory, "Failed to allocate raw memory"); + LOG_ERROR(Common_Memory, "Failed to allocate raw memory"); return ptr; } @@ -99,12 +99,12 @@ void* AllocateAlignedMemory(size_t size, size_t alignment) { ptr = memalign(alignment, size); #else if (posix_memalign(&ptr, alignment, size) != 0) - NGLOG_ERROR(Common_Memory, "Failed to allocate aligned memory"); + LOG_ERROR(Common_Memory, "Failed to allocate aligned memory"); #endif #endif if (ptr == nullptr) - NGLOG_ERROR(Common_Memory, "Failed to allocate aligned memory"); + LOG_ERROR(Common_Memory, "Failed to allocate aligned memory"); return ptr; } @@ -113,7 +113,7 @@ void FreeMemoryPages(void* ptr, size_t size) { if (ptr) { #ifdef _WIN32 if (!VirtualFree(ptr, 0, MEM_RELEASE)) - NGLOG_ERROR(Common_Memory, "FreeMemoryPages failed!\n{}", GetLastErrorMsg()); + LOG_ERROR(Common_Memory, "FreeMemoryPages failed!\n{}", GetLastErrorMsg()); #else munmap(ptr, size); #endif @@ -134,7 +134,7 @@ void WriteProtectMemory(void* ptr, size_t size, bool allowExecute) { #ifdef _WIN32 DWORD oldValue; if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue)) - NGLOG_ERROR(Common_Memory, "WriteProtectMemory failed!\n{}", GetLastErrorMsg()); + LOG_ERROR(Common_Memory, "WriteProtectMemory failed!\n{}", GetLastErrorMsg()); #else mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ); #endif @@ -145,7 +145,7 @@ void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute) { DWORD oldValue; if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, &oldValue)) - NGLOG_ERROR(Common_Memory, "UnWriteProtectMemory failed!\n{}", GetLastErrorMsg()); + LOG_ERROR(Common_Memory, "UnWriteProtectMemory failed!\n{}", GetLastErrorMsg()); #else mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ); diff --git a/src/common/param_package.cpp b/src/common/param_package.cpp index ab01541339..e0df430abd 100644 --- a/src/common/param_package.cpp +++ b/src/common/param_package.cpp @@ -25,7 +25,7 @@ ParamPackage::ParamPackage(const std::string& serialized) { std::vector<std::string> key_value; Common::SplitString(pair, KEY_VALUE_SEPARATOR, key_value); if (key_value.size() != 2) { - NGLOG_ERROR(Common, "invalid key pair {}", pair); + LOG_ERROR(Common, "invalid key pair {}", pair); continue; } @@ -64,7 +64,7 @@ std::string ParamPackage::Serialize() const { std::string ParamPackage::Get(const std::string& key, const std::string& default_value) const { auto pair = data.find(key); if (pair == data.end()) { - NGLOG_DEBUG(Common, "key '{}' not found", key); + LOG_DEBUG(Common, "key '{}' not found", key); return default_value; } @@ -74,14 +74,14 @@ std::string ParamPackage::Get(const std::string& key, const std::string& default int ParamPackage::Get(const std::string& key, int default_value) const { auto pair = data.find(key); if (pair == data.end()) { - NGLOG_DEBUG(Common, "key '{}' not found", key); + LOG_DEBUG(Common, "key '{}' not found", key); return default_value; } try { return std::stoi(pair->second); } catch (const std::logic_error&) { - NGLOG_ERROR(Common, "failed to convert {} to int", pair->second); + LOG_ERROR(Common, "failed to convert {} to int", pair->second); return default_value; } } @@ -89,14 +89,14 @@ int ParamPackage::Get(const std::string& key, int default_value) const { float ParamPackage::Get(const std::string& key, float default_value) const { auto pair = data.find(key); if (pair == data.end()) { - NGLOG_DEBUG(Common, "key {} not found", key); + LOG_DEBUG(Common, "key {} not found", key); return default_value; } try { return std::stof(pair->second); } catch (const std::logic_error&) { - NGLOG_ERROR(Common, "failed to convert {} to float", pair->second); + LOG_ERROR(Common, "failed to convert {} to float", pair->second); return default_value; } } diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index 646400db0e..ea9d8f77c5 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp @@ -281,7 +281,7 @@ static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>& iconv_t const conv_desc = iconv_open("UTF-8", fromcode); if ((iconv_t)(-1) == conv_desc) { - NGLOG_ERROR(Common, "Iconv initialization failure [{}]: {}", fromcode, strerror(errno)); + LOG_ERROR(Common, "Iconv initialization failure [{}]: {}", fromcode, strerror(errno)); iconv_close(conv_desc); return {}; } @@ -310,7 +310,7 @@ static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>& ++src_buffer; } } else { - NGLOG_ERROR(Common, "iconv failure [{}]: {}", fromcode, strerror(errno)); + LOG_ERROR(Common, "iconv failure [{}]: {}", fromcode, strerror(errno)); break; } } @@ -329,7 +329,7 @@ std::u16string UTF8ToUTF16(const std::string& input) { iconv_t const conv_desc = iconv_open("UTF-16LE", "UTF-8"); if ((iconv_t)(-1) == conv_desc) { - NGLOG_ERROR(Common, "Iconv initialization failure [UTF-8]: {}", strerror(errno)); + LOG_ERROR(Common, "Iconv initialization failure [UTF-8]: {}", strerror(errno)); iconv_close(conv_desc); return {}; } @@ -358,7 +358,7 @@ std::u16string UTF8ToUTF16(const std::string& input) { ++src_buffer; } } else { - NGLOG_ERROR(Common, "iconv failure [UTF-8]: {}", strerror(errno)); + LOG_ERROR(Common, "iconv failure [UTF-8]: {}", strerror(errno)); break; } } diff --git a/src/common/swap.h b/src/common/swap.h index 4a4012d1ad..f025f7450f 100644 --- a/src/common/swap.h +++ b/src/common/swap.h @@ -69,7 +69,7 @@ inline u32 swap32(u32 _data) { inline u64 swap64(u64 _data) { return _byteswap_uint64(_data); } -#elif _M_ARM +#elif ARCHITECTURE_ARM inline u16 swap16(u16 _data) { u32 data = _data; __asm__("rev16 %0, %1\n" : "=l"(data) : "l"(data)); diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index ba5b021745..3dff068df6 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -40,6 +40,8 @@ add_library(core STATIC hle/config_mem.h hle/ipc.h hle/ipc_helpers.h + hle/kernel/address_arbiter.cpp + hle/kernel/address_arbiter.h hle/kernel/client_port.cpp hle/kernel/client_port.h hle/kernel/client_session.cpp @@ -124,6 +126,8 @@ add_library(core STATIC hle/service/audio/audren_u.h hle/service/audio/codecctl.cpp hle/service/audio/codecctl.h + hle/service/audio/hwopus.cpp + hle/service/audio/hwopus.h hle/service/bcat/module.cpp hle/service/bcat/module.h hle/service/bcat/bcat.cpp @@ -257,6 +261,8 @@ add_library(core STATIC loader/linker.h loader/loader.cpp loader/loader.h + loader/nca.cpp + loader/nca.h loader/nro.cpp loader/nro.h loader/nso.cpp diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index b5db476676..42605374b6 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp @@ -55,8 +55,8 @@ public: } void InterpreterFallback(u64 pc, size_t num_instructions) override { - NGLOG_INFO(Core_ARM, "Unicorn fallback @ 0x{:X} for {} instructions (instr = {:08X})", pc, - num_instructions, MemoryReadCode(pc)); + LOG_INFO(Core_ARM, "Unicorn fallback @ 0x{:X} for {} instructions (instr = {:08X})", pc, + num_instructions, MemoryReadCode(pc)); ARM_Interface::ThreadContext ctx; parent.SaveContext(ctx); diff --git a/src/core/core.cpp b/src/core/core.cpp index 84ab876cc1..8335d502e3 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -87,15 +87,15 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file app_loader = Loader::GetLoader(filepath); if (!app_loader) { - NGLOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); + LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); return ResultStatus::ErrorGetLoader; } std::pair<boost::optional<u32>, Loader::ResultStatus> system_mode = app_loader->LoadKernelSystemMode(); if (system_mode.second != Loader::ResultStatus::Success) { - NGLOG_CRITICAL(Core, "Failed to determine system mode (Error {})!", - static_cast<int>(system_mode.second)); + LOG_CRITICAL(Core, "Failed to determine system mode (Error {})!", + static_cast<int>(system_mode.second)); switch (system_mode.second) { case Loader::ResultStatus::ErrorEncrypted: @@ -111,15 +111,15 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file ResultStatus init_result{Init(emu_window, system_mode.first.get())}; if (init_result != ResultStatus::Success) { - NGLOG_CRITICAL(Core, "Failed to initialize system (Error {})!", - static_cast<int>(init_result)); + LOG_CRITICAL(Core, "Failed to initialize system (Error {})!", + static_cast<int>(init_result)); System::Shutdown(); return init_result; } const Loader::ResultStatus load_result{app_loader->Load(current_process)}; if (Loader::ResultStatus::Success != load_result) { - NGLOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result)); + LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result)); System::Shutdown(); switch (load_result) { @@ -161,7 +161,7 @@ Cpu& System::CpuCore(size_t core_index) { } System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { - NGLOG_DEBUG(HW_Memory, "initialized OK"); + LOG_DEBUG(HW_Memory, "initialized OK"); CoreTiming::Init(); @@ -196,7 +196,7 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { } } - NGLOG_DEBUG(Core, "Initialized OK"); + LOG_DEBUG(Core, "Initialized OK"); // Reset counters and set time origin to current frame GetAndResetPerfStats(); @@ -245,7 +245,7 @@ void System::Shutdown() { // Close app loader app_loader.reset(); - NGLOG_DEBUG(Core, "Shutdown OK"); + LOG_DEBUG(Core, "Shutdown OK"); } Service::SM::ServiceManager& System::ServiceManager() { diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp index 099f2bb1ab..f22d6a9d06 100644 --- a/src/core/core_cpu.cpp +++ b/src/core/core_cpu.cpp @@ -56,7 +56,7 @@ Cpu::Cpu(std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index) arm_interface = std::make_shared<ARM_Dynarmic>(); #else cpu_core = std::make_shared<ARM_Unicorn>(); - NGLOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); + LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); #endif } else { arm_interface = std::make_shared<ARM_Unicorn>(); @@ -75,7 +75,7 @@ void Cpu::RunLoop(bool tight_loop) { // If we don't have a currently active thread then don't execute instructions, // instead advance to the next event and try to yield to the next thread if (Kernel::GetCurrentThread() == nullptr) { - NGLOG_TRACE(Core, "Core-{} idling", core_index); + LOG_TRACE(Core, "Core-{} idling", core_index); if (IsMainCore()) { CoreTiming::Idle(); diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index dc1d8668f1..50d1e3fc96 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -74,11 +74,11 @@ static void EmptyTimedCallback(u64 userdata, s64 cyclesLate) {} s64 usToCycles(s64 us) { if (us / 1000000 > MAX_VALUE_TO_MULTIPLY) { - NGLOG_ERROR(Core_Timing, "Integer overflow, use max value"); + LOG_ERROR(Core_Timing, "Integer overflow, use max value"); return std::numeric_limits<s64>::max(); } if (us > MAX_VALUE_TO_MULTIPLY) { - NGLOG_DEBUG(Core_Timing, "Time very big, do rounding"); + LOG_DEBUG(Core_Timing, "Time very big, do rounding"); return BASE_CLOCK_RATE * (us / 1000000); } return (BASE_CLOCK_RATE * us) / 1000000; @@ -86,11 +86,11 @@ s64 usToCycles(s64 us) { s64 usToCycles(u64 us) { if (us / 1000000 > MAX_VALUE_TO_MULTIPLY) { - NGLOG_ERROR(Core_Timing, "Integer overflow, use max value"); + LOG_ERROR(Core_Timing, "Integer overflow, use max value"); return std::numeric_limits<s64>::max(); } if (us > MAX_VALUE_TO_MULTIPLY) { - NGLOG_DEBUG(Core_Timing, "Time very big, do rounding"); + LOG_DEBUG(Core_Timing, "Time very big, do rounding"); return BASE_CLOCK_RATE * static_cast<s64>(us / 1000000); } return (BASE_CLOCK_RATE * static_cast<s64>(us)) / 1000000; @@ -98,11 +98,11 @@ s64 usToCycles(u64 us) { s64 nsToCycles(s64 ns) { if (ns / 1000000000 > MAX_VALUE_TO_MULTIPLY) { - NGLOG_ERROR(Core_Timing, "Integer overflow, use max value"); + LOG_ERROR(Core_Timing, "Integer overflow, use max value"); return std::numeric_limits<s64>::max(); } if (ns > MAX_VALUE_TO_MULTIPLY) { - NGLOG_DEBUG(Core_Timing, "Time very big, do rounding"); + LOG_DEBUG(Core_Timing, "Time very big, do rounding"); return BASE_CLOCK_RATE * (ns / 1000000000); } return (BASE_CLOCK_RATE * ns) / 1000000000; @@ -110,11 +110,11 @@ s64 nsToCycles(s64 ns) { s64 nsToCycles(u64 ns) { if (ns / 1000000000 > MAX_VALUE_TO_MULTIPLY) { - NGLOG_ERROR(Core_Timing, "Integer overflow, use max value"); + LOG_ERROR(Core_Timing, "Integer overflow, use max value"); return std::numeric_limits<s64>::max(); } if (ns > MAX_VALUE_TO_MULTIPLY) { - NGLOG_DEBUG(Core_Timing, "Time very big, do rounding"); + LOG_DEBUG(Core_Timing, "Time very big, do rounding"); return BASE_CLOCK_RATE * (static_cast<s64>(ns) / 1000000000); } return (BASE_CLOCK_RATE * static_cast<s64>(ns)) / 1000000000; diff --git a/src/core/file_sys/disk_filesystem.cpp b/src/core/file_sys/disk_filesystem.cpp index 8aa0e0aa41..8c6f15bb5c 100644 --- a/src/core/file_sys/disk_filesystem.cpp +++ b/src/core/file_sys/disk_filesystem.cpp @@ -80,19 +80,19 @@ ResultCode Disk_FileSystem::RenameFile(const std::string& src_path, } ResultCode Disk_FileSystem::DeleteDirectory(const Path& path) const { - NGLOG_WARNING(Service_FS, "(STUBBED) called"); + LOG_WARNING(Service_FS, "(STUBBED) called"); // TODO(wwylele): Use correct error code return ResultCode(-1); } ResultCode Disk_FileSystem::DeleteDirectoryRecursively(const Path& path) const { - NGLOG_WARNING(Service_FS, "(STUBBED) called"); + LOG_WARNING(Service_FS, "(STUBBED) called"); // TODO(wwylele): Use correct error code return ResultCode(-1); } ResultCode Disk_FileSystem::CreateFile(const std::string& path, u64 size) const { - NGLOG_WARNING(Service_FS, "(STUBBED) called"); + LOG_WARNING(Service_FS, "(STUBBED) called"); std::string full_path = base_directory + path; if (size == 0) { @@ -107,7 +107,7 @@ ResultCode Disk_FileSystem::CreateFile(const std::string& path, u64 size) const return RESULT_SUCCESS; } - NGLOG_ERROR(Service_FS, "Too large file"); + LOG_ERROR(Service_FS, "Too large file"); // TODO(Subv): Find out the correct error code return ResultCode(-1); } @@ -120,13 +120,13 @@ ResultCode Disk_FileSystem::CreateDirectory(const std::string& path) const { return RESULT_SUCCESS; } - NGLOG_CRITICAL(Service_FS, "(unreachable) Unknown error creating {}", full_path); + LOG_CRITICAL(Service_FS, "(unreachable) Unknown error creating {}", full_path); // TODO(wwylele): Use correct error code return ResultCode(-1); } ResultCode Disk_FileSystem::RenameDirectory(const Path& src_path, const Path& dest_path) const { - NGLOG_WARNING(Service_FS, "(STUBBED) called"); + LOG_WARNING(Service_FS, "(STUBBED) called"); // TODO(wwylele): Use correct error code return ResultCode(-1); } @@ -146,7 +146,7 @@ ResultVal<std::unique_ptr<DirectoryBackend>> Disk_FileSystem::OpenDirectory( } u64 Disk_FileSystem::GetFreeSpaceSize() const { - NGLOG_WARNING(Service_FS, "(STUBBED) called"); + LOG_WARNING(Service_FS, "(STUBBED) called"); return 0; } @@ -163,14 +163,14 @@ ResultVal<FileSys::EntryType> Disk_FileSystem::GetEntryType(const std::string& p } ResultVal<size_t> Disk_Storage::Read(const u64 offset, const size_t length, u8* buffer) const { - NGLOG_TRACE(Service_FS, "called offset={}, length={}", offset, length); + LOG_TRACE(Service_FS, "called offset={}, length={}", offset, length); file->Seek(offset, SEEK_SET); return MakeResult<size_t>(file->ReadBytes(buffer, length)); } ResultVal<size_t> Disk_Storage::Write(const u64 offset, const size_t length, const bool flush, const u8* buffer) const { - NGLOG_WARNING(Service_FS, "(STUBBED) called"); + LOG_WARNING(Service_FS, "(STUBBED) called"); file->Seek(offset, SEEK_SET); size_t written = file->WriteBytes(buffer, length); if (flush) { @@ -204,7 +204,7 @@ u64 Disk_Directory::Read(const u64 count, Entry* entries) { const std::string& filename = file.virtualName; Entry& entry = entries[entries_read]; - NGLOG_TRACE(Service_FS, "File {}: size={} dir={}", filename, file.size, file.isDirectory); + LOG_TRACE(Service_FS, "File {}: size={} dir={}", filename, file.size, file.isDirectory); // TODO(Link Mauve): use a proper conversion to UTF-16. for (size_t j = 0; j < FILENAME_LENGTH; ++j) { diff --git a/src/core/file_sys/filesystem.cpp b/src/core/file_sys/filesystem.cpp index 87083878b8..82fdb3c464 100644 --- a/src/core/file_sys/filesystem.cpp +++ b/src/core/file_sys/filesystem.cpp @@ -71,7 +71,7 @@ std::string Path::AsString() const { case Binary: default: // TODO(yuriks): Add assert - NGLOG_ERROR(Service_FS, "LowPathType cannot be converted to string!"); + LOG_ERROR(Service_FS, "LowPathType cannot be converted to string!"); return {}; } } @@ -87,7 +87,7 @@ std::u16string Path::AsU16Str() const { case Invalid: case Binary: // TODO(yuriks): Add assert - NGLOG_ERROR(Service_FS, "LowPathType cannot be converted to u16string!"); + LOG_ERROR(Service_FS, "LowPathType cannot be converted to u16string!"); return {}; } @@ -115,7 +115,7 @@ std::vector<u8> Path::AsBinary() const { case Invalid: default: // TODO(yuriks): Add assert - NGLOG_ERROR(Service_FS, "LowPathType cannot be converted to binary!"); + LOG_ERROR(Service_FS, "LowPathType cannot be converted to binary!"); return {}; } } diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp index 808254ecc8..46d438aca3 100644 --- a/src/core/file_sys/partition_filesystem.cpp +++ b/src/core/file_sys/partition_filesystem.cpp @@ -19,13 +19,20 @@ Loader::ResultStatus PartitionFilesystem::Load(const std::string& file_path, siz if (file.GetSize() < sizeof(Header)) return Loader::ResultStatus::Error; + file.Seek(offset, SEEK_SET); // For cartridges, HFSs can get very large, so we need to calculate the size up to // the actual content itself instead of just blindly reading in the entire file. Header pfs_header; if (!file.ReadBytes(&pfs_header, sizeof(Header))) return Loader::ResultStatus::Error; - bool is_hfs = (memcmp(pfs_header.magic.data(), "HFS", 3) == 0); + if (pfs_header.magic != Common::MakeMagic('H', 'F', 'S', '0') && + pfs_header.magic != Common::MakeMagic('P', 'F', 'S', '0')) { + return Loader::ResultStatus::ErrorInvalidFormat; + } + + bool is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0'); + size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry); size_t metadata_size = sizeof(Header) + (pfs_header.num_entries * entry_size) + pfs_header.strtab_size; @@ -39,7 +46,7 @@ Loader::ResultStatus PartitionFilesystem::Load(const std::string& file_path, siz Loader::ResultStatus result = Load(file_data); if (result != Loader::ResultStatus::Success) - NGLOG_ERROR(Service_FS, "Failed to load PFS from file {}!", file_path); + LOG_ERROR(Service_FS, "Failed to load PFS from file {}!", file_path); return result; } @@ -50,7 +57,12 @@ Loader::ResultStatus PartitionFilesystem::Load(const std::vector<u8>& file_data, return Loader::ResultStatus::Error; memcpy(&pfs_header, &file_data[offset], sizeof(Header)); - is_hfs = (memcmp(pfs_header.magic.data(), "HFS", 3) == 0); + if (pfs_header.magic != Common::MakeMagic('H', 'F', 'S', '0') && + pfs_header.magic != Common::MakeMagic('P', 'F', 'S', '0')) { + return Loader::ResultStatus::ErrorInvalidFormat; + } + + is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0'); size_t entries_offset = offset + sizeof(Header); size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry); @@ -73,21 +85,21 @@ u32 PartitionFilesystem::GetNumEntries() const { return pfs_header.num_entries; } -u64 PartitionFilesystem::GetEntryOffset(int index) const { +u64 PartitionFilesystem::GetEntryOffset(u32 index) const { if (index > GetNumEntries()) return 0; return content_offset + pfs_entries[index].fs_entry.offset; } -u64 PartitionFilesystem::GetEntrySize(int index) const { +u64 PartitionFilesystem::GetEntrySize(u32 index) const { if (index > GetNumEntries()) return 0; return pfs_entries[index].fs_entry.size; } -std::string PartitionFilesystem::GetEntryName(int index) const { +std::string PartitionFilesystem::GetEntryName(u32 index) const { if (index > GetNumEntries()) return ""; @@ -113,12 +125,12 @@ u64 PartitionFilesystem::GetFileSize(const std::string& name) const { } void PartitionFilesystem::Print() const { - NGLOG_DEBUG(Service_FS, "Magic: {:.4}", pfs_header.magic.data()); - NGLOG_DEBUG(Service_FS, "Files: {}", pfs_header.num_entries); + LOG_DEBUG(Service_FS, "Magic: {}", pfs_header.magic); + LOG_DEBUG(Service_FS, "Files: {}", pfs_header.num_entries); for (u32 i = 0; i < pfs_header.num_entries; i++) { - NGLOG_DEBUG(Service_FS, " > File {}: {} (0x{:X} bytes, at 0x{:X})", i, - pfs_entries[i].name.c_str(), pfs_entries[i].fs_entry.size, - GetFileOffset(pfs_entries[i].name)); + LOG_DEBUG(Service_FS, " > File {}: {} (0x{:X} bytes, at 0x{:X})", i, + pfs_entries[i].name.c_str(), pfs_entries[i].fs_entry.size, + GetFileOffset(pfs_entries[i].name)); } } } // namespace FileSys diff --git a/src/core/file_sys/partition_filesystem.h b/src/core/file_sys/partition_filesystem.h index 573c90057f..9c5810cf15 100644 --- a/src/core/file_sys/partition_filesystem.h +++ b/src/core/file_sys/partition_filesystem.h @@ -27,9 +27,9 @@ public: Loader::ResultStatus Load(const std::vector<u8>& file_data, size_t offset = 0); u32 GetNumEntries() const; - u64 GetEntryOffset(int index) const; - u64 GetEntrySize(int index) const; - std::string GetEntryName(int index) const; + u64 GetEntryOffset(u32 index) const; + u64 GetEntrySize(u32 index) const; + std::string GetEntryName(u32 index) const; u64 GetFileOffset(const std::string& name) const; u64 GetFileSize(const std::string& name) const; @@ -37,7 +37,7 @@ public: private: struct Header { - std::array<char, 4> magic; + u32_le magic; u32_le num_entries; u32_le strtab_size; INSERT_PADDING_BYTES(0x4); diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index 25a822891a..2268111158 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp @@ -21,7 +21,7 @@ Loader::ResultStatus ProgramMetadata::Load(const std::string& file_path) { Loader::ResultStatus result = Load(file_data); if (result != Loader::ResultStatus::Success) - NGLOG_ERROR(Service_FS, "Failed to load NPDM from file {}!", file_path); + LOG_ERROR(Service_FS, "Failed to load NPDM from file {}!", file_path); return result; } @@ -76,14 +76,14 @@ u64 ProgramMetadata::GetFilesystemPermissions() const { } void ProgramMetadata::Print() const { - NGLOG_DEBUG(Service_FS, "Magic: {:.4}", npdm_header.magic.data()); - NGLOG_DEBUG(Service_FS, "Main thread priority: 0x{:02X}", npdm_header.main_thread_priority); - NGLOG_DEBUG(Service_FS, "Main thread core: {}", npdm_header.main_thread_cpu); - NGLOG_DEBUG(Service_FS, "Main thread stack size: 0x{:X} bytes", npdm_header.main_stack_size); - NGLOG_DEBUG(Service_FS, "Process category: {}", npdm_header.process_category); - NGLOG_DEBUG(Service_FS, "Flags: 0x{:02X}", npdm_header.flags); - NGLOG_DEBUG(Service_FS, " > 64-bit instructions: {}", - npdm_header.has_64_bit_instructions ? "YES" : "NO"); + LOG_DEBUG(Service_FS, "Magic: {:.4}", npdm_header.magic.data()); + LOG_DEBUG(Service_FS, "Main thread priority: 0x{:02X}", npdm_header.main_thread_priority); + LOG_DEBUG(Service_FS, "Main thread core: {}", npdm_header.main_thread_cpu); + LOG_DEBUG(Service_FS, "Main thread stack size: 0x{:X} bytes", npdm_header.main_stack_size); + LOG_DEBUG(Service_FS, "Process category: {}", npdm_header.process_category); + LOG_DEBUG(Service_FS, "Flags: 0x{:02X}", npdm_header.flags); + LOG_DEBUG(Service_FS, " > 64-bit instructions: {}", + npdm_header.has_64_bit_instructions ? "YES" : "NO"); auto address_space = "Unknown"; switch (npdm_header.address_space_type) { @@ -95,19 +95,19 @@ void ProgramMetadata::Print() const { break; } - NGLOG_DEBUG(Service_FS, " > Address space: {}\n", address_space); + LOG_DEBUG(Service_FS, " > Address space: {}\n", address_space); // Begin ACID printing (potential perms, signed) - NGLOG_DEBUG(Service_FS, "Magic: {:.4}", acid_header.magic.data()); - NGLOG_DEBUG(Service_FS, "Flags: 0x{:02X}", acid_header.flags); - NGLOG_DEBUG(Service_FS, " > Is Retail: {}", acid_header.is_retail ? "YES" : "NO"); - NGLOG_DEBUG(Service_FS, "Title ID Min: 0x{:016X}", acid_header.title_id_min); - NGLOG_DEBUG(Service_FS, "Title ID Max: 0x{:016X}", acid_header.title_id_max); - NGLOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", acid_file_access.permissions); + LOG_DEBUG(Service_FS, "Magic: {:.4}", acid_header.magic.data()); + LOG_DEBUG(Service_FS, "Flags: 0x{:02X}", acid_header.flags); + LOG_DEBUG(Service_FS, " > Is Retail: {}", acid_header.is_retail ? "YES" : "NO"); + LOG_DEBUG(Service_FS, "Title ID Min: 0x{:016X}", acid_header.title_id_min); + LOG_DEBUG(Service_FS, "Title ID Max: 0x{:016X}", acid_header.title_id_max); + LOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", acid_file_access.permissions); // Begin ACI0 printing (actual perms, unsigned) - NGLOG_DEBUG(Service_FS, "Magic: {:.4}", aci_header.magic.data()); - NGLOG_DEBUG(Service_FS, "Title ID: 0x{:016X}", aci_header.title_id); - NGLOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", aci_file_access.permissions); + LOG_DEBUG(Service_FS, "Magic: {:.4}", aci_header.magic.data()); + LOG_DEBUG(Service_FS, "Title ID: 0x{:016X}", aci_header.title_id); + LOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", aci_file_access.permissions); } } // namespace FileSys diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp index dc7591acab..84ae0d99b3 100644 --- a/src/core/file_sys/romfs_factory.cpp +++ b/src/core/file_sys/romfs_factory.cpp @@ -14,7 +14,7 @@ namespace FileSys { RomFS_Factory::RomFS_Factory(Loader::AppLoader& app_loader) { // Load the RomFS from the app if (Loader::ResultStatus::Success != app_loader.ReadRomFS(romfs_file, data_offset, data_size)) { - NGLOG_ERROR(Service_FS, "Unable to read RomFS!"); + LOG_ERROR(Service_FS, "Unable to read RomFS!"); } } @@ -24,13 +24,13 @@ ResultVal<std::unique_ptr<FileSystemBackend>> RomFS_Factory::Open(const Path& pa } ResultCode RomFS_Factory::Format(const Path& path) { - NGLOG_ERROR(Service_FS, "Unimplemented Format archive {}", GetName()); + LOG_ERROR(Service_FS, "Unimplemented Format archive {}", GetName()); // TODO(bunnei): Find the right error code for this return ResultCode(-1); } ResultVal<ArchiveFormatInfo> RomFS_Factory::GetFormatInfo(const Path& path) const { - NGLOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName()); + LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName()); // TODO(bunnei): Find the right error code for this return ResultCode(-1); } diff --git a/src/core/file_sys/romfs_filesystem.cpp b/src/core/file_sys/romfs_filesystem.cpp index 8e2bce6872..83162622b4 100644 --- a/src/core/file_sys/romfs_filesystem.cpp +++ b/src/core/file_sys/romfs_filesystem.cpp @@ -21,72 +21,70 @@ ResultVal<std::unique_ptr<StorageBackend>> RomFS_FileSystem::OpenFile(const std: } ResultCode RomFS_FileSystem::DeleteFile(const std::string& path) const { - NGLOG_CRITICAL(Service_FS, "Attempted to delete a file from an ROMFS archive ({}).", GetName()); + LOG_CRITICAL(Service_FS, "Attempted to delete a file from an ROMFS archive ({}).", GetName()); // TODO(bunnei): Use correct error code return ResultCode(-1); } ResultCode RomFS_FileSystem::RenameFile(const std::string& src_path, const std::string& dest_path) const { - NGLOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive ({}).", - GetName()); + LOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive ({}).", GetName()); // TODO(wwylele): Use correct error code return ResultCode(-1); } ResultCode RomFS_FileSystem::DeleteDirectory(const Path& path) const { - NGLOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive ({}).", - GetName()); + LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive ({}).", + GetName()); // TODO(wwylele): Use correct error code return ResultCode(-1); } ResultCode RomFS_FileSystem::DeleteDirectoryRecursively(const Path& path) const { - NGLOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive ({}).", - GetName()); + LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive ({}).", + GetName()); // TODO(wwylele): Use correct error code return ResultCode(-1); } ResultCode RomFS_FileSystem::CreateFile(const std::string& path, u64 size) const { - NGLOG_CRITICAL(Service_FS, "Attempted to create a file in an ROMFS archive ({}).", GetName()); + LOG_CRITICAL(Service_FS, "Attempted to create a file in an ROMFS archive ({}).", GetName()); // TODO(bunnei): Use correct error code return ResultCode(-1); } ResultCode RomFS_FileSystem::CreateDirectory(const std::string& path) const { - NGLOG_CRITICAL(Service_FS, "Attempted to create a directory in an ROMFS archive ({}).", - GetName()); + LOG_CRITICAL(Service_FS, "Attempted to create a directory in an ROMFS archive ({}).", + GetName()); // TODO(wwylele): Use correct error code return ResultCode(-1); } ResultCode RomFS_FileSystem::RenameDirectory(const Path& src_path, const Path& dest_path) const { - NGLOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive ({}).", - GetName()); + LOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive ({}).", GetName()); // TODO(wwylele): Use correct error code return ResultCode(-1); } ResultVal<std::unique_ptr<DirectoryBackend>> RomFS_FileSystem::OpenDirectory( const std::string& path) const { - NGLOG_WARNING(Service_FS, "Opening Directory in a ROMFS archive"); + LOG_WARNING(Service_FS, "Opening Directory in a ROMFS archive"); return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<ROMFSDirectory>()); } u64 RomFS_FileSystem::GetFreeSpaceSize() const { - NGLOG_WARNING(Service_FS, "Attempted to get the free space in an ROMFS archive"); + LOG_WARNING(Service_FS, "Attempted to get the free space in an ROMFS archive"); return 0; } ResultVal<FileSys::EntryType> RomFS_FileSystem::GetEntryType(const std::string& path) const { - NGLOG_CRITICAL(Service_FS, "Called within an ROMFS archive (path {}).", path); + LOG_CRITICAL(Service_FS, "Called within an ROMFS archive (path {}).", path); // TODO(wwylele): Use correct error code return ResultCode(-1); } ResultVal<size_t> RomFS_Storage::Read(const u64 offset, const size_t length, u8* buffer) const { - NGLOG_TRACE(Service_FS, "called offset={}, length={}", offset, length); + LOG_TRACE(Service_FS, "called offset={}, length={}", offset, length); romfs_file->Seek(data_offset + offset, SEEK_SET); size_t read_length = (size_t)std::min((u64)length, data_size - offset); @@ -95,7 +93,7 @@ ResultVal<size_t> RomFS_Storage::Read(const u64 offset, const size_t length, u8* ResultVal<size_t> RomFS_Storage::Write(const u64 offset, const size_t length, const bool flush, const u8* buffer) const { - NGLOG_ERROR(Service_FS, "Attempted to write to ROMFS file"); + LOG_ERROR(Service_FS, "Attempted to write to ROMFS file"); // TODO(Subv): Find error code return MakeResult<size_t>(0); } @@ -105,7 +103,7 @@ u64 RomFS_Storage::GetSize() const { } bool RomFS_Storage::SetSize(const u64 size) const { - NGLOG_ERROR(Service_FS, "Attempted to set the size of an ROMFS file"); + LOG_ERROR(Service_FS, "Attempted to set the size of an ROMFS file"); return false; } diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index c1be8fee4b..f3aa213af9 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp @@ -17,6 +17,15 @@ SaveData_Factory::SaveData_Factory(std::string nand_directory) ResultVal<std::unique_ptr<FileSystemBackend>> SaveData_Factory::Open(const Path& path) { std::string save_directory = GetFullPath(); + + if (!FileUtil::Exists(save_directory)) { + // TODO(bunnei): This is a work-around to always create a save data directory if it does not + // already exist. This is a hack, as we do not understand yet how this works on hardware. + // Without a save data directory, many games will assert on boot. This should not have any + // bad side-effects. + FileUtil::CreateFullPath(save_directory); + } + // Return an error if the save data doesn't actually exist. if (!FileUtil::IsDirectory(save_directory)) { // TODO(Subv): Find out correct error code. @@ -28,7 +37,7 @@ ResultVal<std::unique_ptr<FileSystemBackend>> SaveData_Factory::Open(const Path& } ResultCode SaveData_Factory::Format(const Path& path) { - NGLOG_WARNING(Service_FS, "Format archive {}", GetName()); + LOG_WARNING(Service_FS, "Format archive {}", GetName()); // Create the save data directory. if (!FileUtil::CreateFullPath(GetFullPath())) { // TODO(Subv): Find the correct error code. @@ -39,7 +48,7 @@ ResultCode SaveData_Factory::Format(const Path& path) { } ResultVal<ArchiveFormatInfo> SaveData_Factory::GetFormatInfo(const Path& path) const { - NGLOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName()); + LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName()); // TODO(bunnei): Find the right error code for this return ResultCode(-1); } diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp index 59ac3e0be8..2e5ffb764e 100644 --- a/src/core/file_sys/sdmc_factory.cpp +++ b/src/core/file_sys/sdmc_factory.cpp @@ -25,13 +25,13 @@ ResultVal<std::unique_ptr<FileSystemBackend>> SDMC_Factory::Open(const Path& pat } ResultCode SDMC_Factory::Format(const Path& path) { - NGLOG_ERROR(Service_FS, "Unimplemented Format archive {}", GetName()); + LOG_ERROR(Service_FS, "Unimplemented Format archive {}", GetName()); // TODO(Subv): Find the right error code for this return ResultCode(-1); } ResultVal<ArchiveFormatInfo> SDMC_Factory::GetFormatInfo(const Path& path) const { - NGLOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName()); + LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName()); // TODO(bunnei): Find the right error code for this return ResultCode(-1); } diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h index 79e52488f2..39bdf4e214 100644 --- a/src/core/frontend/input.h +++ b/src/core/frontend/input.h @@ -59,7 +59,7 @@ template <typename InputDeviceType> void RegisterFactory(const std::string& name, std::shared_ptr<Factory<InputDeviceType>> factory) { auto pair = std::make_pair(name, std::move(factory)); if (!Impl::FactoryList<InputDeviceType>::list.insert(std::move(pair)).second) { - NGLOG_ERROR(Input, "Factory '{}' already registered", name); + LOG_ERROR(Input, "Factory '{}' already registered", name); } } @@ -71,7 +71,7 @@ void RegisterFactory(const std::string& name, std::shared_ptr<Factory<InputDevic template <typename InputDeviceType> void UnregisterFactory(const std::string& name) { if (Impl::FactoryList<InputDeviceType>::list.erase(name) == 0) { - NGLOG_ERROR(Input, "Factory '{}' not registered", name); + LOG_ERROR(Input, "Factory '{}' not registered", name); } } @@ -88,7 +88,7 @@ std::unique_ptr<InputDeviceType> CreateDevice(const std::string& params) { const auto pair = factory_list.find(engine); if (pair == factory_list.end()) { if (engine != "null") { - NGLOG_ERROR(Input, "Unknown engine name: {}", engine); + LOG_ERROR(Input, "Unknown engine name: {}", engine); } return std::make_unique<InputDeviceType>(); } diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 2603192fea..938852a1a2 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -232,7 +232,7 @@ static u8 HexCharToValue(u8 hex) { return hex - 'A' + 0xA; } - NGLOG_ERROR(Debug_GDBStub, "Invalid nibble: {} ({:02X})", hex, hex); + LOG_ERROR(Debug_GDBStub, "Invalid nibble: {} ({:02X})", hex, hex); return 0; } @@ -372,7 +372,7 @@ static u8 ReadByte() { u8 c; size_t received_size = recv(gdbserver_socket, reinterpret_cast<char*>(&c), 1, MSG_WAITALL); if (received_size != 1) { - NGLOG_ERROR(Debug_GDBStub, "recv failed: {}", received_size); + LOG_ERROR(Debug_GDBStub, "recv failed: {}", received_size); Shutdown(); } @@ -413,8 +413,8 @@ static void RemoveBreakpoint(BreakpointType type, PAddr addr) { auto bp = p.find(static_cast<u64>(addr)); if (bp != p.end()) { - NGLOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: {:016X} bytes at {:016X} of type {}", - bp->second.len, bp->second.addr, static_cast<int>(type)); + LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: {:016X} bytes at {:016X} of type {}", + bp->second.len, bp->second.addr, static_cast<int>(type)); p.erase(static_cast<u64>(addr)); } } @@ -459,10 +459,10 @@ bool CheckBreakpoint(PAddr addr, BreakpointType type) { } if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) { - NGLOG_DEBUG(Debug_GDBStub, - "Found breakpoint type {} @ {:016X}, range: {:016X}" - " - {:016X} ({:X} bytes)", - static_cast<int>(type), addr, bp->second.addr, bp->second.addr + len, len); + LOG_DEBUG(Debug_GDBStub, + "Found breakpoint type {} @ {:016X}, range: {:016X}" + " - {:016X} ({:X} bytes)", + static_cast<int>(type), addr, bp->second.addr, bp->second.addr + len, len); return true; } } @@ -478,7 +478,7 @@ bool CheckBreakpoint(PAddr addr, BreakpointType type) { static void SendPacket(const char packet) { size_t sent_size = send(gdbserver_socket, &packet, 1, 0); if (sent_size != 1) { - NGLOG_ERROR(Debug_GDBStub, "send failed"); + LOG_ERROR(Debug_GDBStub, "send failed"); } } @@ -492,13 +492,13 @@ static void SendReply(const char* reply) { return; } - NGLOG_DEBUG(Debug_GDBStub, "Reply: {}", reply); + LOG_DEBUG(Debug_GDBStub, "Reply: {}", reply); memset(command_buffer, 0, sizeof(command_buffer)); command_length = static_cast<u32>(strlen(reply)); if (command_length + 4 > sizeof(command_buffer)) { - NGLOG_ERROR(Debug_GDBStub, "command_buffer overflow in SendReply"); + LOG_ERROR(Debug_GDBStub, "command_buffer overflow in SendReply"); return; } @@ -515,7 +515,7 @@ static void SendReply(const char* reply) { while (left > 0) { int sent_size = send(gdbserver_socket, reinterpret_cast<char*>(ptr), left, 0); if (sent_size < 0) { - NGLOG_ERROR(Debug_GDBStub, "gdb: send failed"); + LOG_ERROR(Debug_GDBStub, "gdb: send failed"); return Shutdown(); } @@ -526,7 +526,7 @@ static void SendReply(const char* reply) { /// Handle query command from gdb client. static void HandleQuery() { - NGLOG_DEBUG(Debug_GDBStub, "gdb: query '{}'", command_buffer + 1); + LOG_DEBUG(Debug_GDBStub, "gdb: query '{}'", command_buffer + 1); const char* query = reinterpret_cast<const char*>(command_buffer + 1); @@ -634,18 +634,18 @@ static void ReadCommand() { // ignore ack return; } else if (c == 0x03) { - NGLOG_INFO(Debug_GDBStub, "gdb: found break command"); + LOG_INFO(Debug_GDBStub, "gdb: found break command"); halt_loop = true; SendSignal(current_thread, SIGTRAP); return; } else if (c != GDB_STUB_START) { - NGLOG_DEBUG(Debug_GDBStub, "gdb: read invalid byte {:02X}", c); + LOG_DEBUG(Debug_GDBStub, "gdb: read invalid byte {:02X}", c); return; } while ((c = ReadByte()) != GDB_STUB_END) { if (command_length >= sizeof(command_buffer)) { - NGLOG_ERROR(Debug_GDBStub, "gdb: command_buffer overflow"); + LOG_ERROR(Debug_GDBStub, "gdb: command_buffer overflow"); SendPacket(GDB_STUB_NACK); return; } @@ -658,10 +658,9 @@ static void ReadCommand() { u8 checksum_calculated = CalculateChecksum(command_buffer, command_length); if (checksum_received != checksum_calculated) { - NGLOG_ERROR( - Debug_GDBStub, - "gdb: invalid checksum: calculated {:02X} and read {:02X} for ${}# (length: {})", - checksum_calculated, checksum_received, command_buffer, command_length); + LOG_ERROR(Debug_GDBStub, + "gdb: invalid checksum: calculated {:02X} and read {:02X} for ${}# (length: {})", + checksum_calculated, checksum_received, command_buffer, command_length); command_length = 0; @@ -688,7 +687,7 @@ static bool IsDataAvailable() { t.tv_usec = 0; if (select(gdbserver_socket + 1, &fd_socket, nullptr, nullptr, &t) < 0) { - NGLOG_ERROR(Debug_GDBStub, "select failed"); + LOG_ERROR(Debug_GDBStub, "select failed"); return false; } @@ -801,7 +800,7 @@ static void ReadMemory() { u64 len = HexToLong(start_offset, static_cast<u64>((command_buffer + command_length) - start_offset)); - NGLOG_DEBUG(Debug_GDBStub, "gdb: addr: {:016X} len: {:016X}", addr, len); + LOG_DEBUG(Debug_GDBStub, "gdb: addr: {:016X} len: {:016X}", addr, len); if (len * 2 > sizeof(reply)) { SendReply("E01"); @@ -888,8 +887,8 @@ static bool CommitBreakpoint(BreakpointType type, PAddr addr, u64 len) { breakpoint.len = len; p.insert({addr, breakpoint}); - NGLOG_DEBUG(Debug_GDBStub, "gdb: added {} breakpoint: {:016X} bytes at {:016X}", - static_cast<int>(type), breakpoint.len, breakpoint.addr); + LOG_DEBUG(Debug_GDBStub, "gdb: added {} breakpoint: {:016X} bytes at {:016X}", + static_cast<int>(type), breakpoint.len, breakpoint.addr); return true; } @@ -996,7 +995,7 @@ void HandlePacket() { return; } - NGLOG_DEBUG(Debug_GDBStub, "Packet: {}", command_buffer); + LOG_DEBUG(Debug_GDBStub, "Packet: {}", command_buffer); switch (command_buffer[0]) { case 'q': @@ -1010,7 +1009,7 @@ void HandlePacket() { break; case 'k': Shutdown(); - NGLOG_INFO(Debug_GDBStub, "killed by gdb"); + LOG_INFO(Debug_GDBStub, "killed by gdb"); return; case 'g': ReadRegisters(); @@ -1092,7 +1091,7 @@ static void Init(u16 port) { breakpoints_write.clear(); // Start gdb server - NGLOG_INFO(Debug_GDBStub, "Starting GDB server on port {}...", port); + LOG_INFO(Debug_GDBStub, "Starting GDB server on port {}...", port); sockaddr_in saddr_server = {}; saddr_server.sin_family = AF_INET; @@ -1105,28 +1104,28 @@ static void Init(u16 port) { int tmpsock = static_cast<int>(socket(PF_INET, SOCK_STREAM, 0)); if (tmpsock == -1) { - NGLOG_ERROR(Debug_GDBStub, "Failed to create gdb socket"); + LOG_ERROR(Debug_GDBStub, "Failed to create gdb socket"); } // Set socket to SO_REUSEADDR so it can always bind on the same port int reuse_enabled = 1; if (setsockopt(tmpsock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse_enabled, sizeof(reuse_enabled)) < 0) { - NGLOG_ERROR(Debug_GDBStub, "Failed to set gdb socket option"); + LOG_ERROR(Debug_GDBStub, "Failed to set gdb socket option"); } const sockaddr* server_addr = reinterpret_cast<const sockaddr*>(&saddr_server); socklen_t server_addrlen = sizeof(saddr_server); if (bind(tmpsock, server_addr, server_addrlen) < 0) { - NGLOG_ERROR(Debug_GDBStub, "Failed to bind gdb socket"); + LOG_ERROR(Debug_GDBStub, "Failed to bind gdb socket"); } if (listen(tmpsock, 1) < 0) { - NGLOG_ERROR(Debug_GDBStub, "Failed to listen to gdb socket"); + LOG_ERROR(Debug_GDBStub, "Failed to listen to gdb socket"); } // Wait for gdb to connect - NGLOG_INFO(Debug_GDBStub, "Waiting for gdb to connect..."); + LOG_INFO(Debug_GDBStub, "Waiting for gdb to connect..."); sockaddr_in saddr_client; sockaddr* client_addr = reinterpret_cast<sockaddr*>(&saddr_client); socklen_t client_addrlen = sizeof(saddr_client); @@ -1137,9 +1136,9 @@ static void Init(u16 port) { halt_loop = false; step_loop = false; - NGLOG_ERROR(Debug_GDBStub, "Failed to accept gdb client"); + LOG_ERROR(Debug_GDBStub, "Failed to accept gdb client"); } else { - NGLOG_INFO(Debug_GDBStub, "Client connected."); + LOG_INFO(Debug_GDBStub, "Client connected."); saddr_client.sin_addr.s_addr = ntohl(saddr_client.sin_addr.s_addr); } @@ -1158,7 +1157,7 @@ void Shutdown() { return; } - NGLOG_INFO(Debug_GDBStub, "Stopping GDB ..."); + LOG_INFO(Debug_GDBStub, "Stopping GDB ..."); if (gdbserver_socket != -1) { shutdown(gdbserver_socket, SHUT_RDWR); gdbserver_socket = -1; @@ -1168,7 +1167,7 @@ void Shutdown() { WSACleanup(); #endif - NGLOG_INFO(Debug_GDBStub, "GDB stopped."); + LOG_INFO(Debug_GDBStub, "GDB stopped."); } bool IsServerEnabled() { diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp new file mode 100644 index 0000000000..e9c8369d77 --- /dev/null +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -0,0 +1,173 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "core/core.h" +#include "core/hle/kernel/errors.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/process.h" +#include "core/hle/kernel/thread.h" +#include "core/hle/lock.h" +#include "core/memory.h" + +namespace Kernel { +namespace AddressArbiter { + +// Performs actual address waiting logic. +static ResultCode WaitForAddress(VAddr address, s64 timeout) { + SharedPtr<Thread> current_thread = GetCurrentThread(); + current_thread->arb_wait_address = address; + current_thread->status = THREADSTATUS_WAIT_ARB; + current_thread->wakeup_callback = nullptr; + + current_thread->WakeAfterDelay(timeout); + + Core::System::GetInstance().CpuCore(current_thread->processor_id).PrepareReschedule(); + return RESULT_TIMEOUT; +} + +// Gets the threads waiting on an address. +static void GetThreadsWaitingOnAddress(std::vector<SharedPtr<Thread>>& waiting_threads, + VAddr address) { + auto RetrieveWaitingThreads = + [](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr arb_addr) { + const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); + auto& thread_list = scheduler->GetThreadList(); + + for (auto& thread : thread_list) { + if (thread->arb_wait_address == arb_addr) + waiting_threads.push_back(thread); + } + }; + + // Retrieve a list of all threads that are waiting for this address. + RetrieveWaitingThreads(0, waiting_threads, address); + RetrieveWaitingThreads(1, waiting_threads, address); + RetrieveWaitingThreads(2, waiting_threads, address); + RetrieveWaitingThreads(3, waiting_threads, address); + // Sort them by priority, such that the highest priority ones come first. + std::sort(waiting_threads.begin(), waiting_threads.end(), + [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) { + return lhs->current_priority < rhs->current_priority; + }); +} + +// Wake up num_to_wake (or all) threads in a vector. +static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_to_wake) { + // Only process up to 'target' threads, unless 'target' is <= 0, in which case process + // them all. + size_t last = waiting_threads.size(); + if (num_to_wake > 0) + last = num_to_wake; + + // Signal the waiting threads. + for (size_t i = 0; i < last; i++) { + ASSERT(waiting_threads[i]->status = THREADSTATUS_WAIT_ARB); + waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS); + waiting_threads[i]->arb_wait_address = 0; + waiting_threads[i]->ResumeFromWait(); + } +} + +// Signals an address being waited on. +ResultCode SignalToAddress(VAddr address, s32 num_to_wake) { + // Get threads waiting on the address. + std::vector<SharedPtr<Thread>> waiting_threads; + GetThreadsWaitingOnAddress(waiting_threads, address); + + WakeThreads(waiting_threads, num_to_wake); + return RESULT_SUCCESS; +} + +// Signals an address being waited on and increments its value if equal to the value argument. +ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake) { + // Ensure that we can write to the address. + if (!Memory::IsValidVirtualAddress(address)) { + return ERR_INVALID_ADDRESS_STATE; + } + + if (static_cast<s32>(Memory::Read32(address)) == value) { + Memory::Write32(address, static_cast<u32>(value + 1)); + } else { + return ERR_INVALID_STATE; + } + + return SignalToAddress(address, num_to_wake); +} + +// Signals an address being waited on and modifies its value based on waiting thread count if equal +// to the value argument. +ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, + s32 num_to_wake) { + // Ensure that we can write to the address. + if (!Memory::IsValidVirtualAddress(address)) { + return ERR_INVALID_ADDRESS_STATE; + } + + // Get threads waiting on the address. + std::vector<SharedPtr<Thread>> waiting_threads; + GetThreadsWaitingOnAddress(waiting_threads, address); + + // Determine the modified value depending on the waiting count. + s32 updated_value; + if (waiting_threads.size() == 0) { + updated_value = value - 1; + } else if (num_to_wake <= 0 || waiting_threads.size() <= num_to_wake) { + updated_value = value + 1; + } else { + updated_value = value; + } + + if (static_cast<s32>(Memory::Read32(address)) == value) { + Memory::Write32(address, static_cast<u32>(updated_value)); + } else { + return ERR_INVALID_STATE; + } + + WakeThreads(waiting_threads, num_to_wake); + return RESULT_SUCCESS; +} + +// Waits on an address if the value passed is less than the argument value, optionally decrementing. +ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement) { + // Ensure that we can read the address. + if (!Memory::IsValidVirtualAddress(address)) { + return ERR_INVALID_ADDRESS_STATE; + } + + s32 cur_value = static_cast<s32>(Memory::Read32(address)); + if (cur_value < value) { + Memory::Write32(address, static_cast<u32>(cur_value - 1)); + } else { + return ERR_INVALID_STATE; + } + // Short-circuit without rescheduling, if timeout is zero. + if (timeout == 0) { + return RESULT_TIMEOUT; + } + + return WaitForAddress(address, timeout); +} + +// Waits on an address if the value passed is equal to the argument value. +ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) { + // Ensure that we can read the address. + if (!Memory::IsValidVirtualAddress(address)) { + return ERR_INVALID_ADDRESS_STATE; + } + // Only wait for the address if equal. + if (static_cast<s32>(Memory::Read32(address)) != value) { + return ERR_INVALID_STATE; + } + // Short-circuit without rescheduling, if timeout is zero. + if (timeout == 0) { + return RESULT_TIMEOUT; + } + + return WaitForAddress(address, timeout); +} +} // namespace AddressArbiter +} // namespace Kernel diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h new file mode 100644 index 0000000000..f20f3dbc0d --- /dev/null +++ b/src/core/hle/kernel/address_arbiter.h @@ -0,0 +1,32 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/result.h" + +namespace Kernel { + +namespace AddressArbiter { +enum class ArbitrationType { + WaitIfLessThan = 0, + DecrementAndWaitIfLessThan = 1, + WaitIfEqual = 2, +}; + +enum class SignalType { + Signal = 0, + IncrementAndSignalIfEqual = 1, + ModifyByWaitingCountAndSignalIfEqual = 2, +}; + +ResultCode SignalToAddress(VAddr address, s32 num_to_wake); +ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake); +ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake); + +ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement); +ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout); +} // namespace AddressArbiter + +} // namespace Kernel diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h index e1b5430bfd..221cb1bb53 100644 --- a/src/core/hle/kernel/errors.h +++ b/src/core/hle/kernel/errors.h @@ -20,13 +20,16 @@ enum { MaxConnectionsReached = 52, // Confirmed Switch OS error codes - MisalignedAddress = 102, + InvalidAddress = 102, + InvalidMemoryState = 106, InvalidProcessorId = 113, InvalidHandle = 114, InvalidCombination = 116, Timeout = 117, SynchronizationCanceled = 118, TooLarge = 119, + InvalidEnumValue = 120, + InvalidState = 125, }; } @@ -39,14 +42,15 @@ constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(-1); constexpr ResultCode ERR_PORT_NAME_TOO_LONG(-1); constexpr ResultCode ERR_WRONG_PERMISSION(-1); constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(-1); -constexpr ResultCode ERR_INVALID_ENUM_VALUE(-1); +constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue); constexpr ResultCode ERR_INVALID_ENUM_VALUE_FND(-1); constexpr ResultCode ERR_INVALID_COMBINATION(-1); constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(-1); constexpr ResultCode ERR_OUT_OF_MEMORY(-1); -constexpr ResultCode ERR_INVALID_ADDRESS(-1); -constexpr ResultCode ERR_INVALID_ADDRESS_STATE(-1); +constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress); +constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState); constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle); +constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState); constexpr ResultCode ERR_INVALID_POINTER(-1); constexpr ResultCode ERR_INVALID_OBJECT_ADDR(-1); constexpr ResultCode ERR_NOT_AUTHORIZED(-1); diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp index f7a9920d8d..7dd67f80f0 100644 --- a/src/core/hle/kernel/handle_table.cpp +++ b/src/core/hle/kernel/handle_table.cpp @@ -26,7 +26,7 @@ ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) { u16 slot = next_free_slot; if (slot >= generations.size()) { - NGLOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use."); + LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use."); return ERR_OUT_OF_HANDLES; } next_free_slot = generations[slot]; @@ -48,7 +48,7 @@ ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) { ResultVal<Handle> HandleTable::Duplicate(Handle handle) { SharedPtr<Object> object = GetGeneric(handle); if (object == nullptr) { - NGLOG_ERROR(Kernel, "Tried to duplicate invalid handle: {:08X}", handle); + LOG_ERROR(Kernel, "Tried to duplicate invalid handle: {:08X}", handle); return ERR_INVALID_HANDLE; } return Create(std::move(object)); diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 01904467ea..609cdbff26 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -120,7 +120,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { std::make_shared<IPC::DomainMessageHeader>(rp.PopRaw<IPC::DomainMessageHeader>()); } else { if (Session()->IsDomain()) - NGLOG_WARNING(IPC, "Domain request has no DomainMessageHeader!"); + LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!"); } } @@ -271,11 +271,16 @@ std::vector<u8> HLERequestContext::ReadBuffer(int buffer_index) const { } size_t HLERequestContext::WriteBuffer(const void* buffer, size_t size, int buffer_index) const { + if (size == 0) { + LOG_WARNING(Core, "skip empty buffer write"); + return 0; + } + const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()}; const size_t buffer_size{GetWriteBufferSize(buffer_index)}; if (size > buffer_size) { - NGLOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size, - buffer_size); + LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size, + buffer_size); size = buffer_size; // TODO(bunnei): This needs to be HW tested } diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index bc144f3dea..65560226d0 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -59,7 +59,7 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle, Handle requesting_thread_handle) { // The mutex address must be 4-byte aligned if ((address % sizeof(u32)) != 0) { - return ResultCode(ErrorModule::Kernel, ErrCodes::MisalignedAddress); + return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidAddress); } SharedPtr<Thread> holding_thread = g_handle_table.Get<Thread>(holding_thread_handle); @@ -97,7 +97,7 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle, ResultCode Mutex::Release(VAddr address) { // The mutex address must be 4-byte aligned if ((address % sizeof(u32)) != 0) { - return ResultCode(ErrorModule::Kernel, ErrCodes::MisalignedAddress); + return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidAddress); } auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(GetCurrentThread(), address); diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 651d932d35..0c05060854 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -54,7 +54,7 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) { continue; } else if ((type & 0xF00) == 0xE00) { // 0x0FFF // Allowed interrupts list - NGLOG_WARNING(Loader, "ExHeader allowed interrupts list ignored"); + LOG_WARNING(Loader, "ExHeader allowed interrupts list ignored"); } else if ((type & 0xF80) == 0xF00) { // 0x07FF // Allowed syscalls mask unsigned int index = ((descriptor >> 24) & 7) * 24; @@ -74,7 +74,7 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) { } else if ((type & 0xFFE) == 0xFF8) { // 0x001F // Mapped memory range if (i + 1 >= len || ((kernel_caps[i + 1] >> 20) & 0xFFE) != 0xFF8) { - NGLOG_WARNING(Loader, "Incomplete exheader memory range descriptor ignored."); + LOG_WARNING(Loader, "Incomplete exheader memory range descriptor ignored."); continue; } u32 end_desc = kernel_caps[i + 1]; @@ -109,9 +109,9 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) { int minor = kernel_version & 0xFF; int major = (kernel_version >> 8) & 0xFF; - NGLOG_INFO(Loader, "ExHeader kernel version: {}.{}", major, minor); + LOG_INFO(Loader, "ExHeader kernel version: {}.{}", major, minor); } else { - NGLOG_ERROR(Loader, "Unhandled kernel caps descriptor: 0x{:08X}", descriptor); + LOG_ERROR(Loader, "Unhandled kernel caps descriptor: 0x{:08X}", descriptor); } } } diff --git a/src/core/hle/kernel/resource_limit.cpp b/src/core/hle/kernel/resource_limit.cpp index 0ef5fc57d0..17a3e8a747 100644 --- a/src/core/hle/kernel/resource_limit.cpp +++ b/src/core/hle/kernel/resource_limit.cpp @@ -29,7 +29,7 @@ SharedPtr<ResourceLimit> ResourceLimit::GetForCategory(ResourceLimitCategory cat case ResourceLimitCategory::OTHER: return resource_limits[static_cast<u8>(category)]; default: - NGLOG_CRITICAL(Kernel, "Unknown resource limit category"); + LOG_CRITICAL(Kernel, "Unknown resource limit category"); UNREACHABLE(); } } @@ -55,7 +55,7 @@ s32 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const { case ResourceType::CPUTime: return current_cpu_time; default: - NGLOG_ERROR(Kernel, "Unknown resource type={:08X}", static_cast<u32>(resource)); + LOG_ERROR(Kernel, "Unknown resource type={:08X}", static_cast<u32>(resource)); UNIMPLEMENTED(); return 0; } @@ -84,7 +84,7 @@ u32 ResourceLimit::GetMaxResourceValue(ResourceType resource) const { case ResourceType::CPUTime: return max_cpu_time; default: - NGLOG_ERROR(Kernel, "Unknown resource type={:08X}", static_cast<u32>(resource)); + LOG_ERROR(Kernel, "Unknown resource type={:08X}", static_cast<u32>(resource)); UNIMPLEMENTED(); return 0; } diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 9cb9e0e5c9..11c2cb69ef 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -99,11 +99,11 @@ void Scheduler::Reschedule() { Thread* next = PopNextReadyThread(); if (cur && next) { - NGLOG_TRACE(Kernel, "context switch {} -> {}", cur->GetObjectId(), next->GetObjectId()); + LOG_TRACE(Kernel, "context switch {} -> {}", cur->GetObjectId(), next->GetObjectId()); } else if (cur) { - NGLOG_TRACE(Kernel, "context switch {} -> idle", cur->GetObjectId()); + LOG_TRACE(Kernel, "context switch {} -> idle", cur->GetObjectId()); } else if (next) { - NGLOG_TRACE(Kernel, "context switch idle -> {}", next->GetObjectId()); + LOG_TRACE(Kernel, "context switch idle -> {}", next->GetObjectId()); } SwitchContext(next); diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index bf812c5430..0d5cba1d91 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -71,7 +71,7 @@ ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& con return domain_request_handlers[object_id - 1]->HandleSyncRequest(context); case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: { - NGLOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id); + LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id); domain_request_handlers[object_id - 1] = nullptr; @@ -81,8 +81,8 @@ ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& con } } - NGLOG_CRITICAL(IPC, "Unknown domain command={}", - static_cast<int>(domain_message_header->command.Value())); + LOG_CRITICAL(IPC, "Unknown domain command={}", + static_cast<int>(domain_message_header->command.Value())); ASSERT(false); } diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index ac4921298a..93f7f2772d 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -107,16 +107,16 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi // Error out if the requested permissions don't match what the creator process allows. if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) { - NGLOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match", - GetObjectId(), address, name); + LOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match", + GetObjectId(), address, name); return ERR_INVALID_COMBINATION; } // Error out if the provided permissions are not compatible with what the creator process needs. if (other_permissions != MemoryPermission::DontCare && static_cast<u32>(this->permissions) & ~static_cast<u32>(other_permissions)) { - NGLOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match", - GetObjectId(), address, name); + LOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match", + GetObjectId(), address, name); return ERR_WRONG_PERMISSION; } @@ -131,7 +131,7 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi auto result = target_process->vm_manager.MapMemoryBlock( target_address, backing_block, backing_block_offset, size, MemoryState::Shared); if (result.Failed()) { - NGLOG_ERROR( + LOG_ERROR( Kernel, "cannot map id={}, target_address=0x{:X} name={}, error mapping to virtual memory", GetObjectId(), target_address, name); diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index ec3601e8b6..5ad923fe7c 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -11,6 +11,7 @@ #include "common/string_util.h" #include "core/core.h" #include "core/core_timing.h" +#include "core/hle/kernel/address_arbiter.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" #include "core/hle/kernel/event.h" @@ -31,7 +32,7 @@ namespace Kernel { /// Set the process heap to a given Size. It can both extend and shrink the heap. static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) { - NGLOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size); + LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size); auto& process = *Core::CurrentProcess(); CASCADE_RESULT(*heap_addr, process.HeapAllocate(Memory::HEAP_VADDR, heap_size, VMAPermission::ReadWrite)); @@ -39,21 +40,21 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) { } static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state1) { - NGLOG_WARNING(Kernel_SVC, "(STUBBED) called, addr=0x{:X}", addr); + LOG_WARNING(Kernel_SVC, "(STUBBED) called, addr=0x{:X}", addr); return RESULT_SUCCESS; } /// Maps a memory range into a different range. static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { - NGLOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, - src_addr, size); + LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, + src_addr, size); return Core::CurrentProcess()->MirrorMemory(dst_addr, src_addr, size); } /// Unmaps a region that was previously mapped with svcMapMemory static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { - NGLOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, - src_addr, size); + LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, + src_addr, size); return Core::CurrentProcess()->UnmapMemory(dst_addr, src_addr, size); } @@ -68,11 +69,11 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address if (port_name.size() > PortNameMaxLength) return ERR_PORT_NAME_TOO_LONG; - NGLOG_TRACE(Kernel_SVC, "called port_name={}", port_name); + LOG_TRACE(Kernel_SVC, "called port_name={}", port_name); auto it = Service::g_kernel_named_ports.find(port_name); if (it == Service::g_kernel_named_ports.end()) { - NGLOG_WARNING(Kernel_SVC, "tried to connect to unknown port: {}", port_name); + LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: {}", port_name); return ERR_NOT_FOUND; } @@ -90,11 +91,11 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address static ResultCode SendSyncRequest(Handle handle) { SharedPtr<ClientSession> session = g_handle_table.Get<ClientSession>(handle); if (!session) { - NGLOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle); + LOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle); return ERR_INVALID_HANDLE; } - NGLOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); + LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); Core::System::GetInstance().PrepareReschedule(); @@ -105,7 +106,7 @@ static ResultCode SendSyncRequest(Handle handle) { /// Get the ID for the specified thread. static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) { - NGLOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); + LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle); if (!thread) { @@ -118,7 +119,7 @@ static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) { /// Get the ID of the specified process static ResultCode GetProcessId(u32* process_id, Handle process_handle) { - NGLOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle); + LOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle); const SharedPtr<Process> process = g_handle_table.Get<Process>(process_handle); if (!process) { @@ -148,8 +149,8 @@ static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thr /// Wait for the given handles to synchronize, timeout after the specified nanoseconds static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64 handle_count, s64 nano_seconds) { - NGLOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}", - handles_address, handle_count, nano_seconds); + LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}", + handles_address, handle_count, nano_seconds); if (!Memory::IsValidVirtualAddress(handles_address)) return ERR_INVALID_POINTER; @@ -209,7 +210,7 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64 /// Resumes a thread waiting on WaitSynchronization static ResultCode CancelSynchronization(Handle thread_handle) { - NGLOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle); + LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle); const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle); if (!thread) { @@ -226,24 +227,24 @@ static ResultCode CancelSynchronization(Handle thread_handle) { /// Attempts to locks a mutex, creating it if it does not already exist static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr, Handle requesting_thread_handle) { - NGLOG_TRACE(Kernel_SVC, - "called holding_thread_handle=0x{:08X}, mutex_addr=0x{:X}, " - "requesting_current_thread_handle=0x{:08X}", - holding_thread_handle, mutex_addr, requesting_thread_handle); + LOG_TRACE(Kernel_SVC, + "called holding_thread_handle=0x{:08X}, mutex_addr=0x{:X}, " + "requesting_current_thread_handle=0x{:08X}", + holding_thread_handle, mutex_addr, requesting_thread_handle); return Mutex::TryAcquire(mutex_addr, holding_thread_handle, requesting_thread_handle); } /// Unlock a mutex static ResultCode ArbitrateUnlock(VAddr mutex_addr) { - NGLOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr); + LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr); return Mutex::Release(mutex_addr); } /// Break program execution static void Break(u64 unk_0, u64 unk_1, u64 unk_2) { - NGLOG_CRITICAL(Debug_Emulated, "Emulated program broke execution!"); + LOG_CRITICAL(Debug_Emulated, "Emulated program broke execution!"); ASSERT(false); } @@ -251,13 +252,13 @@ static void Break(u64 unk_0, u64 unk_1, u64 unk_2) { static void OutputDebugString(VAddr address, s32 len) { std::string str(len, '\0'); Memory::ReadBlock(address, str.data(), str.size()); - NGLOG_DEBUG(Debug_Emulated, "{}", str); + LOG_DEBUG(Debug_Emulated, "{}", str); } /// Gets system/memory information for the current process static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) { - NGLOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, - info_sub_id, handle); + LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, + info_sub_id, handle); auto& vm_manager = Core::CurrentProcess()->vm_manager; @@ -308,12 +309,17 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) *result = Core::CurrentProcess()->is_virtual_address_memory_enabled; break; case GetInfoType::TitleId: - NGLOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query titleid, returned 0"); + LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query titleid, returned 0"); *result = 0; break; case GetInfoType::PrivilegedProcessId: - NGLOG_WARNING(Kernel_SVC, - "(STUBBED) Attempted to query privileged process id bounds, returned 0"); + LOG_WARNING(Kernel_SVC, + "(STUBBED) Attempted to query privileged process id bounds, returned 0"); + *result = 0; + break; + case GetInfoType::UserExceptionContextAddr: + LOG_WARNING(Kernel_SVC, + "(STUBBED) Attempted to query user exception context address, returned 0"); *result = 0; break; default: @@ -325,14 +331,13 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) /// Sets the thread activity static ResultCode SetThreadActivity(Handle handle, u32 unknown) { - NGLOG_WARNING(Kernel_SVC, "(STUBBED) called, handle=0x{:08X}, unknown=0x{:08X}", handle, - unknown); + LOG_WARNING(Kernel_SVC, "(STUBBED) called, handle=0x{:08X}, unknown=0x{:08X}", handle, unknown); return RESULT_SUCCESS; } /// Gets the thread context static ResultCode GetThreadContext(Handle handle, VAddr addr) { - NGLOG_WARNING(Kernel_SVC, "(STUBBED) called, handle=0x{:08X}, addr=0x{:X}", handle, addr); + LOG_WARNING(Kernel_SVC, "(STUBBED) called, handle=0x{:08X}, addr=0x{:X}", handle, addr); return RESULT_SUCCESS; } @@ -371,16 +376,15 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) { /// Get which CPU core is executing the current thread static u32 GetCurrentProcessorNumber() { - NGLOG_TRACE(Kernel_SVC, "called"); + LOG_TRACE(Kernel_SVC, "called"); return GetCurrentThread()->processor_id; } static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size, u32 permissions) { - NGLOG_TRACE( - Kernel_SVC, - "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", - shared_memory_handle, addr, size, permissions); + LOG_TRACE(Kernel_SVC, + "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", + shared_memory_handle, addr, size, permissions); SharedPtr<SharedMemory> shared_memory = g_handle_table.Get<SharedMemory>(shared_memory_handle); if (!shared_memory) { @@ -400,15 +404,15 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s return shared_memory->Map(Core::CurrentProcess().get(), addr, permissions_type, MemoryPermission::DontCare); default: - NGLOG_ERROR(Kernel_SVC, "unknown permissions=0x{:08X}", permissions); + LOG_ERROR(Kernel_SVC, "unknown permissions=0x{:08X}", permissions); } return RESULT_SUCCESS; } static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) { - NGLOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}", - shared_memory_handle, addr, size); + LOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}", + shared_memory_handle, addr, size); SharedPtr<SharedMemory> shared_memory = g_handle_table.Get<SharedMemory>(shared_memory_handle); @@ -436,19 +440,19 @@ static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_i memory_info->type = static_cast<u32>(vma->second.meminfo_state); } - NGLOG_TRACE(Kernel_SVC, "called process=0x{:08X} addr={:X}", process_handle, addr); + LOG_TRACE(Kernel_SVC, "called process=0x{:08X} addr={:X}", process_handle, addr); return RESULT_SUCCESS; } /// Query memory static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, VAddr addr) { - NGLOG_TRACE(Kernel_SVC, "called, addr={:X}", addr); + LOG_TRACE(Kernel_SVC, "called, addr={:X}", addr); return QueryProcessMemory(memory_info, page_info, CurrentProcess, addr); } /// Exits the current process static void ExitProcess() { - NGLOG_INFO(Kernel_SVC, "Process {} exiting", Core::CurrentProcess()->process_id); + LOG_INFO(Kernel_SVC, "Process {} exiting", Core::CurrentProcess()->process_id); ASSERT_MSG(Core::CurrentProcess()->status == ProcessStatus::Running, "Process has already exited"); @@ -524,17 +528,17 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V Core::System::GetInstance().PrepareReschedule(); Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule(); - NGLOG_TRACE(Kernel_SVC, - "called entrypoint=0x{:08X} ({}), arg=0x{:08X}, stacktop=0x{:08X}, " - "threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}", - entry_point, name, arg, stack_top, priority, processor_id, *out_handle); + LOG_TRACE(Kernel_SVC, + "called entrypoint=0x{:08X} ({}), arg=0x{:08X}, stacktop=0x{:08X}, " + "threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}", + entry_point, name, arg, stack_top, priority, processor_id, *out_handle); return RESULT_SUCCESS; } /// Starts the thread for the provided handle static ResultCode StartThread(Handle thread_handle) { - NGLOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); + LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle); if (!thread) { @@ -551,7 +555,7 @@ static ResultCode StartThread(Handle thread_handle) { /// Called when a thread exits static void ExitThread() { - NGLOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", Core::CurrentArmInterface().GetPC()); + LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", Core::CurrentArmInterface().GetPC()); ExitCurrentThread(); Core::System::GetInstance().PrepareReschedule(); @@ -559,7 +563,7 @@ static void ExitThread() { /// Sleep the current thread static void SleepThread(s64 nanoseconds) { - NGLOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); + LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); // Don't attempt to yield execution if there are no available threads to run, // this way we avoid a useless reschedule to the idle thread. @@ -575,10 +579,10 @@ static void SleepThread(s64 nanoseconds) { Core::System::GetInstance().PrepareReschedule(); } -/// Signal process wide key atomic +/// Wait process wide key atomic static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_variable_addr, Handle thread_handle, s64 nano_seconds) { - NGLOG_TRACE( + LOG_TRACE( Kernel_SVC, "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}", mutex_addr, condition_variable_addr, thread_handle, nano_seconds); @@ -605,8 +609,8 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var /// Signal process wide key static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target) { - NGLOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}", - condition_variable_addr, target); + LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}", + condition_variable_addr, target); auto RetrieveWaitingThreads = [](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr condvar_addr) { @@ -684,6 +688,57 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target return RESULT_SUCCESS; } +// Wait for an address (via Address Arbiter) +static ResultCode WaitForAddress(VAddr address, u32 type, s32 value, s64 timeout) { + LOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}", + address, type, value, timeout); + // If the passed address is a kernel virtual address, return invalid memory state. + if (Memory::IsKernelVirtualAddress(address)) { + return ERR_INVALID_ADDRESS_STATE; + } + // If the address is not properly aligned to 4 bytes, return invalid address. + if (address % sizeof(u32) != 0) { + return ERR_INVALID_ADDRESS; + } + + switch (static_cast<AddressArbiter::ArbitrationType>(type)) { + case AddressArbiter::ArbitrationType::WaitIfLessThan: + return AddressArbiter::WaitForAddressIfLessThan(address, value, timeout, false); + case AddressArbiter::ArbitrationType::DecrementAndWaitIfLessThan: + return AddressArbiter::WaitForAddressIfLessThan(address, value, timeout, true); + case AddressArbiter::ArbitrationType::WaitIfEqual: + return AddressArbiter::WaitForAddressIfEqual(address, value, timeout); + default: + return ERR_INVALID_ENUM_VALUE; + } +} + +// Signals to an address (via Address Arbiter) +static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to_wake) { + LOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}", + address, type, value, num_to_wake); + // If the passed address is a kernel virtual address, return invalid memory state. + if (Memory::IsKernelVirtualAddress(address)) { + return ERR_INVALID_ADDRESS_STATE; + } + // If the address is not properly aligned to 4 bytes, return invalid address. + if (address % sizeof(u32) != 0) { + return ERR_INVALID_ADDRESS; + } + + switch (static_cast<AddressArbiter::SignalType>(type)) { + case AddressArbiter::SignalType::Signal: + return AddressArbiter::SignalToAddress(address, num_to_wake); + case AddressArbiter::SignalType::IncrementAndSignalIfEqual: + return AddressArbiter::IncrementAndSignalToAddressIfEqual(address, value, num_to_wake); + case AddressArbiter::SignalType::ModifyByWaitingCountAndSignalIfEqual: + return AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(address, value, + num_to_wake); + default: + return ERR_INVALID_ENUM_VALUE; + } +} + /// This returns the total CPU ticks elapsed since the CPU was powered-on static u64 GetSystemTick() { const u64 result{CoreTiming::GetTicks()}; @@ -696,13 +751,13 @@ static u64 GetSystemTick() { /// Close a handle static ResultCode CloseHandle(Handle handle) { - NGLOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); + LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); return g_handle_table.Close(handle); } /// Reset an event static ResultCode ResetSignal(Handle handle) { - NGLOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle); + LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle); auto event = g_handle_table.Get<Event>(handle); ASSERT(event != nullptr); event->Clear(); @@ -711,14 +766,14 @@ static ResultCode ResetSignal(Handle handle) { /// Creates a TransferMemory object static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 permissions) { - NGLOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, - size, permissions); + LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size, + permissions); *handle = 0; return RESULT_SUCCESS; } static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) { - NGLOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); + LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle); if (!thread) { @@ -732,8 +787,8 @@ static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) } static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) { - NGLOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:16X}, core=0x{:X}", thread_handle, - mask, core); + LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:16X}, core=0x{:X}", thread_handle, + mask, core); const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle); if (!thread) { @@ -744,7 +799,7 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) { ASSERT(thread->owner_process->ideal_processor != THREADPROCESSORID_DEFAULT); // Set the target CPU to the one specified in the process' exheader. core = thread->owner_process->ideal_processor; - mask = 1 << core; + mask = 1ull << core; } if (mask == 0) { @@ -761,7 +816,7 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) { } // Error out if the input core isn't enabled in the input mask. - if (core < Core::NUM_CPU_CORES && (mask & (1 << core)) == 0) { + if (core < Core::NUM_CPU_CORES && (mask & (1ull << core)) == 0) { return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination); } @@ -772,8 +827,8 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) { static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permissions, u32 remote_permissions) { - NGLOG_TRACE(Kernel_SVC, "called, size=0x{:X}, localPerms=0x{:08X}, remotePerms=0x{:08X}", size, - local_permissions, remote_permissions); + LOG_TRACE(Kernel_SVC, "called, size=0x{:X}, localPerms=0x{:08X}, remotePerms=0x{:08X}", size, + local_permissions, remote_permissions); auto sharedMemHandle = SharedMemory::Create(g_handle_table.Get<Process>(KernelHandle::CurrentProcess), size, static_cast<MemoryPermission>(local_permissions), @@ -784,7 +839,7 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss } static ResultCode ClearEvent(Handle handle) { - NGLOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle); + LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle); SharedPtr<Event> evt = g_handle_table.Get<Event>(handle); if (evt == nullptr) @@ -856,8 +911,8 @@ static const FunctionDef SVC_Table[] = { {0x31, nullptr, "GetResourceLimitCurrentValue"}, {0x32, SvcWrap<SetThreadActivity>, "SetThreadActivity"}, {0x33, SvcWrap<GetThreadContext>, "GetThreadContext"}, - {0x34, nullptr, "WaitForAddress"}, - {0x35, nullptr, "SignalToAddress"}, + {0x34, SvcWrap<WaitForAddress>, "WaitForAddress"}, + {0x35, SvcWrap<SignalToAddress>, "SignalToAddress"}, {0x36, nullptr, "Unknown"}, {0x37, nullptr, "Unknown"}, {0x38, nullptr, "Unknown"}, @@ -936,7 +991,7 @@ static const FunctionDef SVC_Table[] = { static const FunctionDef* GetSVCInfo(u32 func_num) { if (func_num >= std::size(SVC_Table)) { - NGLOG_ERROR(Kernel_SVC, "Unknown svc=0x{:02X}", func_num); + LOG_ERROR(Kernel_SVC, "Unknown svc=0x{:02X}", func_num); return nullptr; } return &SVC_Table[func_num]; @@ -955,10 +1010,10 @@ void CallSVC(u32 immediate) { if (info->func) { info->func(); } else { - NGLOG_CRITICAL(Kernel_SVC, "Unimplemented SVC function {}(..)", info->name); + LOG_CRITICAL(Kernel_SVC, "Unimplemented SVC function {}(..)", info->name); } } else { - NGLOG_CRITICAL(Kernel_SVC, "Unknown SVC function 0x{:X}", immediate); + LOG_CRITICAL(Kernel_SVC, "Unknown SVC function 0x{:X}", immediate); } } diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index 40aa88cc1f..79c3fe31b6 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -179,6 +179,20 @@ void SvcWrap() { FuncReturn(retval); } +template <ResultCode func(u64, u32, s32, s64)> +void SvcWrap() { + FuncReturn( + func(PARAM(0), (u32)(PARAM(1) & 0xFFFFFFFF), (s32)(PARAM(2) & 0xFFFFFFFF), (s64)PARAM(3)) + .raw); +} + +template <ResultCode func(u64, u32, s32, s32)> +void SvcWrap() { + FuncReturn(func(PARAM(0), (u32)(PARAM(1) & 0xFFFFFFFF), (s32)(PARAM(2) & 0xFFFFFFFF), + (s32)(PARAM(3) & 0xFFFFFFFF)) + .raw); +} + //////////////////////////////////////////////////////////////////////////////////////////////////// // Function wrappers that return type u32 diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index cffa7ca836..9a9746585b 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -104,7 +104,7 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { const auto proper_handle = static_cast<Handle>(thread_handle); SharedPtr<Thread> thread = wakeup_callback_handle_table.Get<Thread>(proper_handle); if (thread == nullptr) { - NGLOG_CRITICAL(Kernel, "Callback fired for invalid thread {:08X}", proper_handle); + LOG_CRITICAL(Kernel, "Callback fired for invalid thread {:08X}", proper_handle); return; } @@ -140,6 +140,11 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { } } + if (thread->arb_wait_address != 0) { + ASSERT(thread->status == THREADSTATUS_WAIT_ARB); + thread->arb_wait_address = 0; + } + if (resume) thread->ResumeFromWait(); } @@ -179,6 +184,7 @@ void Thread::ResumeFromWait() { case THREADSTATUS_WAIT_SLEEP: case THREADSTATUS_WAIT_IPC: case THREADSTATUS_WAIT_MUTEX: + case THREADSTATUS_WAIT_ARB: break; case THREADSTATUS_READY: @@ -284,19 +290,19 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, SharedPtr<Process> owner_process) { // Check if priority is in ranged. Lowest priority -> highest priority id. if (priority > THREADPRIO_LOWEST) { - NGLOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority); + LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority); return ERR_OUT_OF_RANGE; } if (processor_id > THREADPROCESSORID_MAX) { - NGLOG_ERROR(Kernel_SVC, "Invalid processor id: {}", processor_id); + LOG_ERROR(Kernel_SVC, "Invalid processor id: {}", processor_id); return ERR_OUT_OF_RANGE_KERNEL; } // TODO(yuriks): Other checks, returning 0xD9001BEA if (!Memory::IsValidVirtualAddress(*owner_process, entry_point)) { - NGLOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point); + LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point); // TODO (bunnei): Find the correct error code to use here return ResultCode(-1); } @@ -337,8 +343,8 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, auto& linheap_memory = memory_region->linear_heap_memory; if (linheap_memory->size() + Memory::PAGE_SIZE > memory_region->size) { - NGLOG_ERROR(Kernel_SVC, - "Not enough space in region to allocate a new TLS page for thread"); + LOG_ERROR(Kernel_SVC, + "Not enough space in region to allocate a new TLS page for thread"); return ERR_OUT_OF_MEMORY; } diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 1d2da6d50a..f1e7598026 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -45,6 +45,7 @@ enum ThreadStatus { THREADSTATUS_WAIT_SYNCH_ANY, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false THREADSTATUS_WAIT_SYNCH_ALL, ///< Waiting due to WaitSynchronizationN with wait_all = true THREADSTATUS_WAIT_MUTEX, ///< Waiting due to an ArbitrateLock/WaitProcessWideKey svc + THREADSTATUS_WAIT_ARB, ///< Waiting due to a SignalToAddress/WaitForAddress svc THREADSTATUS_DORMANT, ///< Created but not yet made ready THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated }; @@ -230,6 +231,9 @@ public: VAddr mutex_wait_address; ///< If waiting on a Mutex, this is the mutex address Handle wait_handle; ///< The handle used to wait for the mutex. + // If waiting for an AddressArbiter, this is the address being waited on. + VAddr arb_wait_address{0}; + std::string name; /// Handle used by guest emulated application to access this thread diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index 661356a976..0141125e4f 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp @@ -78,7 +78,7 @@ void Timer::WakeupAllWaitingThreads() { } void Timer::Signal(int cycles_late) { - NGLOG_TRACE(Kernel, "Timer {} fired", GetObjectId()); + LOG_TRACE(Kernel, "Timer {} fired", GetObjectId()); signaled = true; @@ -98,7 +98,7 @@ static void TimerCallback(u64 timer_handle, int cycles_late) { timer_callback_handle_table.Get<Timer>(static_cast<Handle>(timer_handle)); if (timer == nullptr) { - NGLOG_CRITICAL(Kernel, "Callback fired for invalid timer {:016X}", timer_handle); + LOG_CRITICAL(Kernel, "Callback fired for invalid timer {:016X}", timer_handle); return; } diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 676e5b2823..034dd490e3 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -242,12 +242,12 @@ void VMManager::RefreshMemoryBlockMappings(const std::vector<u8>* block) { void VMManager::LogLayout() const { for (const auto& p : vma_map) { const VirtualMemoryArea& vma = p.second; - NGLOG_DEBUG(Kernel, "{:016X} - {:016X} size: {:016X} {}{}{} {}", vma.base, - vma.base + vma.size, vma.size, - (u8)vma.permissions & (u8)VMAPermission::Read ? 'R' : '-', - (u8)vma.permissions & (u8)VMAPermission::Write ? 'W' : '-', - (u8)vma.permissions & (u8)VMAPermission::Execute ? 'X' : '-', - GetMemoryStateName(vma.meminfo_state)); + LOG_DEBUG(Kernel, "{:016X} - {:016X} size: {:016X} {}{}{} {}", vma.base, + vma.base + vma.size, vma.size, + (u8)vma.permissions & (u8)VMAPermission::Read ? 'R' : '-', + (u8)vma.permissions & (u8)VMAPermission::Write ? 'W' : '-', + (u8)vma.permissions & (u8)VMAPermission::Execute ? 'X' : '-', + GetMemoryStateName(vma.meminfo_state)); } } @@ -392,22 +392,22 @@ void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { } u64 VMManager::GetTotalMemoryUsage() { - NGLOG_WARNING(Kernel, "(STUBBED) called"); + LOG_WARNING(Kernel, "(STUBBED) called"); return 0xF8000000; } u64 VMManager::GetTotalHeapUsage() { - NGLOG_WARNING(Kernel, "(STUBBED) called"); + LOG_WARNING(Kernel, "(STUBBED) called"); return 0x0; } VAddr VMManager::GetAddressSpaceBaseAddr() { - NGLOG_WARNING(Kernel, "(STUBBED) called"); + LOG_WARNING(Kernel, "(STUBBED) called"); return 0x8000000; } u64 VMManager::GetAddressSpaceSize() { - NGLOG_WARNING(Kernel, "(STUBBED) called"); + LOG_WARNING(Kernel, "(STUBBED) called"); return MAX_ADDRESS; } diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index f2fffa7606..6bafb2dce5 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -47,7 +47,7 @@ public: private: void GetBase(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_ACC, "(STUBBED) called"); + LOG_WARNING(Service_ACC, "(STUBBED) called"); ProfileBase profile_base{}; IPC::ResponseBuilder rb{ctx, 16}; rb.Push(RESULT_SUCCESS); @@ -72,14 +72,14 @@ public: private: void CheckAvailability(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_ACC, "(STUBBED) called"); + LOG_WARNING(Service_ACC, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push(true); // TODO: Check when this is supposed to return true and when not } void GetAccountId(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_ACC, "(STUBBED) called"); + LOG_WARNING(Service_ACC, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); rb.Push<u64>(0x12345678ABCDEF); @@ -87,14 +87,14 @@ private: }; void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_ACC, "(STUBBED) called"); + LOG_WARNING(Service_ACC, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push(true); // TODO: Check when this is supposed to return true and when not } void Module::Interface::ListAllUsers(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_ACC, "(STUBBED) called"); + LOG_WARNING(Service_ACC, "(STUBBED) called"); constexpr std::array<u128, 10> user_ids{DEFAULT_USER_ID}; ctx.WriteBuffer(user_ids.data(), user_ids.size()); IPC::ResponseBuilder rb{ctx, 2}; @@ -102,7 +102,7 @@ void Module::Interface::ListAllUsers(Kernel::HLERequestContext& ctx) { } void Module::Interface::ListOpenUsers(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_ACC, "(STUBBED) called"); + LOG_WARNING(Service_ACC, "(STUBBED) called"); constexpr std::array<u128, 10> user_ids{DEFAULT_USER_ID}; ctx.WriteBuffer(user_ids.data(), user_ids.size()); IPC::ResponseBuilder rb{ctx, 2}; @@ -113,11 +113,11 @@ void Module::Interface::GetProfile(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IProfile>(); - NGLOG_DEBUG(Service_ACC, "called"); + LOG_DEBUG(Service_ACC, "called"); } void Module::Interface::InitializeApplicationInfo(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_ACC, "(STUBBED) called"); + LOG_WARNING(Service_ACC, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -126,11 +126,11 @@ void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestCo IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IManagerForApplication>(); - NGLOG_DEBUG(Service_ACC, "called"); + LOG_DEBUG(Service_ACC, "called"); } void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_ACC, "(STUBBED) called"); + LOG_WARNING(Service_ACC, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 6}; rb.Push(RESULT_SUCCESS); rb.PushRaw(DEFAULT_USER_ID); diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index b8d6b8d4d0..a871b3eaa0 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -30,14 +30,14 @@ IWindowController::IWindowController() : ServiceFramework("IWindowController") { } void IWindowController::GetAppletResourceUserId(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_WARNING(Service_AM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); rb.Push<u64>(0); } void IWindowController::AcquireForegroundRights(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_WARNING(Service_AM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -56,20 +56,20 @@ IAudioController::IAudioController() : ServiceFramework("IAudioController") { } void IAudioController::SetExpectedMasterVolume(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_WARNING(Service_AM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void IAudioController::GetMainAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_WARNING(Service_AM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push(volume); } void IAudioController::GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_WARNING(Service_AM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push(volume); @@ -174,14 +174,14 @@ void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_WARNING(Service_AM, "(STUBBED) called"); } void ISelfController::SetRestartMessageEnabled(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_WARNING(Service_AM, "(STUBBED) called"); } void ISelfController::SetPerformanceModeChangedNotification(Kernel::HLERequestContext& ctx) { @@ -192,14 +192,14 @@ void ISelfController::SetPerformanceModeChangedNotification(Kernel::HLERequestCo IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_AM, "(STUBBED) called flag={}", flag); + LOG_WARNING(Service_AM, "(STUBBED) called flag={}", flag); } void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_WARNING(Service_AM, "(STUBBED) called"); } void ISelfController::SetOperationModeChangedNotification(Kernel::HLERequestContext& ctx) { @@ -210,7 +210,7 @@ void ISelfController::SetOperationModeChangedNotification(Kernel::HLERequestCont IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_AM, "(STUBBED) called flag={}", flag); + LOG_WARNING(Service_AM, "(STUBBED) called flag={}", flag); } void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) { @@ -223,21 +223,21 @@ void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_AM, "(STUBBED) called enabled={}", enabled); + LOG_WARNING(Service_AM, "(STUBBED) called enabled={}", enabled); } void ISelfController::LockExit(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_WARNING(Service_AM, "(STUBBED) called"); } void ISelfController::UnlockExit(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_WARNING(Service_AM, "(STUBBED) called"); } void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx) { @@ -247,7 +247,7 @@ void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(launchable_event); - NGLOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_WARNING(Service_AM, "(STUBBED) called"); } void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) { @@ -260,14 +260,14 @@ void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) rb.Push(RESULT_SUCCESS); rb.Push(layer_id); - NGLOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_WARNING(Service_AM, "(STUBBED) called"); } void ISelfController::SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_WARNING(Service_AM, "(STUBBED) called"); } ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter") { @@ -311,7 +311,7 @@ void ICommonStateGetter::GetEventHandle(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(event); - NGLOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_WARNING(Service_AM, "(STUBBED) called"); } void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) { @@ -319,7 +319,7 @@ void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.Push<u32>(15); - NGLOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_WARNING(Service_AM, "(STUBBED) called"); } void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) { @@ -327,7 +327,7 @@ void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.Push(static_cast<u8>(FocusState::InFocus)); - NGLOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_WARNING(Service_AM, "(STUBBED) called"); } void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) { @@ -336,7 +336,7 @@ void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.Push(static_cast<u8>(use_docked_mode ? OperationMode::Docked : OperationMode::Handheld)); - NGLOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_WARNING(Service_AM, "(STUBBED) called"); } void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) { @@ -346,7 +346,7 @@ void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) { rb.Push(static_cast<u32>(use_docked_mode ? APM::PerformanceMode::Docked : APM::PerformanceMode::Handheld)); - NGLOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_WARNING(Service_AM, "(STUBBED) called"); } class IStorageAccessor final : public ServiceFramework<IStorageAccessor> { @@ -370,7 +370,7 @@ private: rb.Push(RESULT_SUCCESS); rb.Push(static_cast<u64>(buffer.size())); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } void Write(Kernel::HLERequestContext& ctx) { @@ -386,7 +386,7 @@ private: IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)}; rb.Push(RESULT_SUCCESS); - NGLOG_DEBUG(Service_AM, "called, offset={}", offset); + LOG_DEBUG(Service_AM, "called, offset={}", offset); } void Read(Kernel::HLERequestContext& ctx) { @@ -402,7 +402,7 @@ private: IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)}; rb.Push(RESULT_SUCCESS); - NGLOG_DEBUG(Service_AM, "called, offset={}", offset); + LOG_DEBUG(Service_AM, "called, offset={}", offset); } }; @@ -426,7 +426,7 @@ private: rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<AM::IStorageAccessor>(buffer); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } }; @@ -467,21 +467,21 @@ private: rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(state_changed_event); - NGLOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_WARNING(Service_AM, "(STUBBED) called"); } void GetResult(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_WARNING(Service_AM, "(STUBBED) called"); } void Start(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_WARNING(Service_AM, "(STUBBED) called"); } void PushInData(Kernel::HLERequestContext& ctx) { @@ -491,7 +491,7 @@ private: IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)}; rb.Push(RESULT_SUCCESS); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } void PopOutData(Kernel::HLERequestContext& ctx) { @@ -501,7 +501,7 @@ private: storage_stack.pop(); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } std::stack<std::shared_ptr<AM::IStorage>> storage_stack; @@ -526,7 +526,7 @@ void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<AM::ILibraryAppletAccessor>(); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) { @@ -538,7 +538,7 @@ void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<AM::IStorage>(std::move(buffer)); - NGLOG_DEBUG(Service_AM, "called, size={}", size); + LOG_DEBUG(Service_AM, "called, size={}", size); } IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") { @@ -602,21 +602,21 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<AM::IStorage>(buffer); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest( Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_WARNING(Service_AM, "(STUBBED) called"); } void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; u128 uid = rp.PopRaw<u128>(); - NGLOG_WARNING(Service, "(STUBBED) called uid = {:016X}{:016X}", uid[1], uid[0]); + LOG_WARNING(Service, "(STUBBED) called uid = {:016X}{:016X}", uid[1], uid[0]); IPC::ResponseBuilder rb{ctx, 4}; @@ -644,7 +644,7 @@ void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_AM, "(STUBBED) called, result=0x{:08X}", result); + LOG_WARNING(Service_AM, "(STUBBED) called, result=0x{:08X}", result); } void IApplicationFunctions::GetDisplayVersion(Kernel::HLERequestContext& ctx) { @@ -652,7 +652,7 @@ void IApplicationFunctions::GetDisplayVersion(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.Push<u64>(1); rb.Push<u64>(0); - NGLOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_WARNING(Service_AM, "(STUBBED) called"); } void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) { @@ -660,20 +660,20 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); rb.Push(static_cast<u64>(Service::Set::LanguageCode::EN_US)); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } void IApplicationFunctions::InitializeGamePlayRecording(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_WARNING(Service_AM, "(STUBBED) called"); } void IApplicationFunctions::SetGamePlayRecordingState(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_WARNING(Service_AM, "(STUBBED) called"); } void IApplicationFunctions::NotifyRunning(Kernel::HLERequestContext& ctx) { @@ -681,7 +681,7 @@ void IApplicationFunctions::NotifyRunning(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.Push<u8>(0); // Unknown, seems to be ignored by official processes - NGLOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_WARNING(Service_AM, "(STUBBED) called"); } void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) { @@ -692,7 +692,7 @@ void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) { rb.Push<u64>(0); rb.Push<u64>(0); - NGLOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_WARNING(Service_AM, "(STUBBED) called"); } void InstallInterfaces(SM::ServiceManager& service_manager, @@ -717,7 +717,7 @@ IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions" void IHomeMenuFunctions::RequestToGetForeground(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_WARNING(Service_AM, "(STUBBED) called"); } IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStateController") { diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp index 7ce551de37..180057ec2a 100644 --- a/src/core/hle/service/am/applet_ae.cpp +++ b/src/core/hle/service/am/applet_ae.cpp @@ -33,63 +33,63 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ICommonStateGetter>(); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetSelfController(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ISelfController>(nvflinger); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetWindowController(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IWindowController>(); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetAudioController(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IAudioController>(); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetDisplayController(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IDisplayController>(); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetProcessWindingController(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IProcessWindingController>(); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetDebugFunctions(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IDebugFunctions>(); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetLibraryAppletCreator(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ILibraryAppletCreator>(); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetApplicationFunctions(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IApplicationFunctions>(); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } std::shared_ptr<NVFlinger::NVFlinger> nvflinger; @@ -120,70 +120,70 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ICommonStateGetter>(); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetSelfController(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ISelfController>(nvflinger); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetWindowController(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IWindowController>(); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetAudioController(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IAudioController>(); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetDisplayController(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IDisplayController>(); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetDebugFunctions(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IDebugFunctions>(); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetLibraryAppletCreator(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ILibraryAppletCreator>(); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetHomeMenuFunctions(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IHomeMenuFunctions>(); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetGlobalStateController(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IGlobalStateController>(); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetApplicationCreator(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IApplicationCreator>(); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } std::shared_ptr<NVFlinger::NVFlinger> nvflinger; }; @@ -192,21 +192,21 @@ void AppletAE::OpenSystemAppletProxy(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ISystemAppletProxy>(nvflinger); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } void AppletAE::OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp index 587a922fe6..278259edad 100644 --- a/src/core/hle/service/am/applet_oe.cpp +++ b/src/core/hle/service/am/applet_oe.cpp @@ -33,56 +33,56 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IAudioController>(); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetDisplayController(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IDisplayController>(); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetDebugFunctions(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IDebugFunctions>(); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetWindowController(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IWindowController>(); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetSelfController(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ISelfController>(nvflinger); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetCommonStateGetter(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ICommonStateGetter>(); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetLibraryAppletCreator(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ILibraryAppletCreator>(); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetApplicationFunctions(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IApplicationFunctions>(); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } std::shared_ptr<NVFlinger::NVFlinger> nvflinger; @@ -92,7 +92,7 @@ void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IApplicationProxy>(nvflinger); - NGLOG_DEBUG(Service_AM, "called"); + LOG_DEBUG(Service_AM, "called"); } AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index 5b6dfb48f9..6e7438580c 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp @@ -27,14 +27,14 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); rb.Push<u64>(0); - NGLOG_WARNING(Service_AOC, "(STUBBED) called"); + LOG_WARNING(Service_AOC, "(STUBBED) called"); } void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); rb.Push<u64>(0); - NGLOG_WARNING(Service_AOC, "(STUBBED) called"); + LOG_WARNING(Service_AOC, "(STUBBED) called"); } void InstallInterfaces(SM::ServiceManager& service_manager) { diff --git a/src/core/hle/service/apm/interface.cpp b/src/core/hle/service/apm/interface.cpp index 3a03188ce1..751d73f8df 100644 --- a/src/core/hle/service/apm/interface.cpp +++ b/src/core/hle/service/apm/interface.cpp @@ -29,8 +29,8 @@ private: IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_APM, "(STUBBED) called mode={} config={}", static_cast<u32>(mode), - config); + LOG_WARNING(Service_APM, "(STUBBED) called mode={} config={}", static_cast<u32>(mode), + config); } void GetPerformanceConfiguration(Kernel::HLERequestContext& ctx) { @@ -42,7 +42,7 @@ private: rb.Push(RESULT_SUCCESS); rb.Push<u32>(0); // Performance configuration - NGLOG_WARNING(Service_APM, "(STUBBED) called mode={}", static_cast<u32>(mode)); + LOG_WARNING(Service_APM, "(STUBBED) called mode={}", static_cast<u32>(mode)); } }; diff --git a/src/core/hle/service/audio/audio.cpp b/src/core/hle/service/audio/audio.cpp index 92f910b5f4..d231e91e1d 100644 --- a/src/core/hle/service/audio/audio.cpp +++ b/src/core/hle/service/audio/audio.cpp @@ -8,6 +8,7 @@ #include "core/hle/service/audio/audrec_u.h" #include "core/hle/service/audio/audren_u.h" #include "core/hle/service/audio/codecctl.h" +#include "core/hle/service/audio/hwopus.h" namespace Service::Audio { @@ -17,6 +18,7 @@ void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared<AudRecU>()->InstallAsService(service_manager); std::make_shared<AudRenU>()->InstallAsService(service_manager); std::make_shared<CodecCtl>()->InstallAsService(service_manager); + std::make_shared<HwOpus>()->InstallAsService(service_manager); } } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 402eaa3066..1b4b649d84 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -60,14 +60,14 @@ public: private: void GetAudioOutState(Kernel::HLERequestContext& ctx) { - NGLOG_DEBUG(Service_Audio, "called"); + LOG_DEBUG(Service_Audio, "called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push(static_cast<u32>(audio_out_state)); } void StartAudioOut(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_WARNING(Service_Audio, "(STUBBED) called"); // Start audio audio_out_state = AudioState::Started; @@ -77,7 +77,7 @@ private: } void StopAudioOut(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_WARNING(Service_Audio, "(STUBBED) called"); // Stop audio audio_out_state = AudioState::Stopped; @@ -89,7 +89,7 @@ private: } void RegisterBufferEvent(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_WARNING(Service_Audio, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); @@ -97,7 +97,7 @@ private: } void AppendAudioOutBuffer(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_WARNING(Service_Audio, "(STUBBED) called"); IPC::RequestParser rp{ctx}; const u64 key{rp.Pop<u64>()}; @@ -108,7 +108,7 @@ private: } void GetReleasedAudioOutBuffer(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_WARNING(Service_Audio, "(STUBBED) called"); // TODO(st4rk): This is how libtransistor currently implements the // GetReleasedAudioOutBuffer, it should return the key (a VAddr) to the app and this address @@ -164,7 +164,7 @@ private: }; void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_WARNING(Service_Audio, "(STUBBED) called"); IPC::RequestParser rp{ctx}; const std::string audio_interface = "AudioInterface"; @@ -180,7 +180,7 @@ void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) { } void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_WARNING(Service_Audio, "(STUBBED) called"); if (!audio_out_interface) { audio_out_interface = std::make_shared<IAudioOut>(); diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 6e8002bc9d..2da936b278 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -17,7 +17,8 @@ constexpr u64 audio_ticks{static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / 200)}; class IAudioRenderer final : public ServiceFramework<IAudioRenderer> { public: - IAudioRenderer() : ServiceFramework("IAudioRenderer") { + IAudioRenderer(AudioRendererParameter audren_params) + : ServiceFramework("IAudioRenderer"), worker_params(audren_params) { static const FunctionInfo functions[] = { {0, nullptr, "GetAudioRendererSampleRate"}, {1, nullptr, "GetAudioRendererSampleCount"}, @@ -46,6 +47,7 @@ public: // Start the audio event CoreTiming::ScheduleEvent(audio_ticks, audio_event); + voice_status_list.reserve(worker_params.voice_count); } ~IAudioRenderer() { CoreTiming::UnscheduleEvent(audio_event, 0); @@ -57,30 +59,63 @@ private: } void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) { - NGLOG_DEBUG(Service_Audio, "{}", ctx.Description()); - AudioRendererResponseData response_data{}; - - response_data.section_0_size = - static_cast<u32>(response_data.state_entries.size() * sizeof(AudioRendererStateEntry)); - response_data.section_1_size = static_cast<u32>(response_data.section_1.size()); - response_data.section_2_size = static_cast<u32>(response_data.section_2.size()); - response_data.section_3_size = static_cast<u32>(response_data.section_3.size()); - response_data.section_4_size = static_cast<u32>(response_data.section_4.size()); - response_data.section_5_size = static_cast<u32>(response_data.section_5.size()); - response_data.total_size = sizeof(AudioRendererResponseData); - - for (unsigned i = 0; i < response_data.state_entries.size(); i++) { - // 4 = Busy and 5 = Ready? - response_data.state_entries[i].state = 5; + UpdateDataHeader config{}; + auto buf = ctx.ReadBuffer(); + std::memcpy(&config, buf.data(), sizeof(UpdateDataHeader)); + u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4); + + std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count); + std::memcpy(mem_pool_info.data(), + buf.data() + sizeof(UpdateDataHeader) + config.behavior_size, + memory_pool_count * sizeof(MemoryPoolInfo)); + + std::vector<VoiceInfo> voice_info(worker_params.voice_count); + std::memcpy(voice_info.data(), + buf.data() + sizeof(UpdateDataHeader) + config.behavior_size + + config.memory_pools_size + config.voice_resource_size, + worker_params.voice_count * sizeof(VoiceInfo)); + + UpdateDataHeader response_data{worker_params}; + + ASSERT(ctx.GetWriteBufferSize() == response_data.total_size); + + std::vector<u8> output(response_data.total_size); + std::memcpy(output.data(), &response_data, sizeof(UpdateDataHeader)); + std::vector<MemoryPoolEntry> memory_pool(memory_pool_count); + for (unsigned i = 0; i < memory_pool.size(); i++) { + if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestAttach) + memory_pool[i].state = MemoryPoolStates::Attached; + else if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestDetach) + memory_pool[i].state = MemoryPoolStates::Detached; + else + memory_pool[i].state = mem_pool_info[i].pool_state; } + std::memcpy(output.data() + sizeof(UpdateDataHeader), memory_pool.data(), + response_data.memory_pools_size); + + for (unsigned i = 0; i < voice_info.size(); i++) { + if (voice_info[i].is_new) { + voice_status_list[i].played_sample_count = 0; + voice_status_list[i].wave_buffer_consumed = 0; + } else if (voice_info[i].play_state == (u8)PlayStates::Started) { + for (u32 buff_idx = 0; buff_idx < voice_info[i].wave_buffer_count; buff_idx++) { + voice_status_list[i].played_sample_count += + (voice_info[i].wave_buffer[buff_idx].end_sample_offset - + voice_info[i].wave_buffer[buff_idx].start_sample_offset) / + 2; + voice_status_list[i].wave_buffer_consumed++; + } + } + } + std::memcpy(output.data() + sizeof(UpdateDataHeader) + response_data.memory_pools_size, + voice_status_list.data(), response_data.voices_size); - ctx.WriteBuffer(&response_data, response_data.total_size); + ctx.WriteBuffer(output); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_WARNING(Service_Audio, "(STUBBED) called"); } void StartAudioRenderer(Kernel::HLERequestContext& ctx) { @@ -88,7 +123,7 @@ private: rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_WARNING(Service_Audio, "(STUBBED) called"); } void StopAudioRenderer(Kernel::HLERequestContext& ctx) { @@ -96,7 +131,7 @@ private: rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_WARNING(Service_Audio, "(STUBBED) called"); } void QuerySystemEvent(Kernel::HLERequestContext& ctx) { @@ -106,51 +141,132 @@ private: rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(system_event); - NGLOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_WARNING(Service_Audio, "(STUBBED) called"); } - struct AudioRendererStateEntry { - u32_le state; + enum class MemoryPoolStates : u32 { // Should be LE + Invalid = 0x0, + Unknown = 0x1, + RequestDetach = 0x2, + Detached = 0x3, + RequestAttach = 0x4, + Attached = 0x5, + Released = 0x6, + }; + + enum class PlayStates : u8 { + Started = 0, + Stopped = 1, + }; + + struct MemoryPoolEntry { + MemoryPoolStates state; u32_le unknown_4; u32_le unknown_8; u32_le unknown_c; }; - static_assert(sizeof(AudioRendererStateEntry) == 0x10, - "AudioRendererStateEntry has wrong size"); - - struct AudioRendererResponseData { - u32_le unknown_0; - u32_le section_5_size; - u32_le section_0_size; - u32_le section_1_size; - u32_le unknown_10; - u32_le section_2_size; - u32_le unknown_18; - u32_le section_3_size; - u32_le section_4_size; - u32_le unknown_24; - u32_le unknown_28; - u32_le unknown_2c; - u32_le unknown_30; - u32_le unknown_34; - u32_le unknown_38; + static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size"); + + struct MemoryPoolInfo { + u64_le pool_address; + u64_le pool_size; + MemoryPoolStates pool_state; + INSERT_PADDING_WORDS(3); // Unknown + }; + static_assert(sizeof(MemoryPoolInfo) == 0x20, "MemoryPoolInfo has wrong size"); + + struct UpdateDataHeader { + UpdateDataHeader() {} + + UpdateDataHeader(const AudioRendererParameter& config) { + revision = Common::MakeMagic('R', 'E', 'V', '4'); // 5.1.0 Revision + behavior_size = 0xb0; + memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10; + voices_size = config.voice_count * 0x10; + effects_size = config.effect_count * 0x10; + sinks_size = config.sink_count * 0x20; + performance_manager_size = 0x10; + total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + + voices_size + effects_size + sinks_size + performance_manager_size; + } + + u32_le revision; + u32_le behavior_size; + u32_le memory_pools_size; + u32_le voices_size; + u32_le voice_resource_size; + u32_le effects_size; + u32_le mixes_size; + u32_le sinks_size; + u32_le performance_manager_size; + INSERT_PADDING_WORDS(6); u32_le total_size; + }; + static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size"); - std::array<AudioRendererStateEntry, 0x18e> state_entries; + struct BiquadFilter { + u8 enable; + INSERT_PADDING_BYTES(1); + s16_le numerator[3]; + s16_le denominator[2]; + }; + static_assert(sizeof(BiquadFilter) == 0xc, "BiquadFilter has wrong size"); + + struct WaveBuffer { + u64_le buffer_addr; + u64_le buffer_sz; + s32_le start_sample_offset; + s32_le end_sample_offset; + u8 loop; + u8 end_of_stream; + u8 sent_to_server; + INSERT_PADDING_BYTES(5); + u64 context_addr; + u64 context_sz; + INSERT_PADDING_BYTES(8); + }; + static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size"); + + struct VoiceInfo { + u32_le id; + u32_le node_id; + u8 is_new; + u8 is_in_use; + u8 play_state; + u8 sample_format; + u32_le sample_rate; + u32_le priority; + u32_le sorting_order; + u32_le channel_count; + float_le pitch; + float_le volume; + BiquadFilter biquad_filter[2]; + u32_le wave_buffer_count; + u16_le wave_buffer_head; + INSERT_PADDING_BYTES(6); + u64_le additional_params_addr; + u64_le additional_params_sz; + u32_le mix_id; + u32_le splitter_info_id; + WaveBuffer wave_buffer[4]; + u32_le voice_channel_resource_ids[6]; + INSERT_PADDING_BYTES(24); + }; + static_assert(sizeof(VoiceInfo) == 0x170, "VoiceInfo is wrong size"); - std::array<u8, 0x600> section_1; - std::array<u8, 0xe0> section_2; - std::array<u8, 0x20> section_3; - std::array<u8, 0x10> section_4; - std::array<u8, 0xb0> section_5; + struct VoiceOutStatus { + u64_le played_sample_count; + u32_le wave_buffer_consumed; + INSERT_PADDING_WORDS(1); }; - static_assert(sizeof(AudioRendererResponseData) == 0x20e0, - "AudioRendererResponseData has wrong size"); + static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size"); /// This is used to trigger the audio event callback. CoreTiming::EventType* audio_event; Kernel::SharedPtr<Kernel::Event> system_event; + AudioRendererParameter worker_params; + std::vector<VoiceOutStatus> voice_status_list; }; class IAudioDevice final : public ServiceFramework<IAudioDevice> { @@ -179,7 +295,7 @@ public: private: void ListAudioDeviceName(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_WARNING(Service_Audio, "(STUBBED) called"); IPC::RequestParser rp{ctx}; const std::string audio_interface = "AudioInterface"; @@ -191,7 +307,7 @@ private: } void SetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_WARNING(Service_Audio, "(STUBBED) called"); IPC::RequestParser rp{ctx}; f32 volume = static_cast<f32>(rp.Pop<u32>()); @@ -204,7 +320,7 @@ private: } void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_WARNING(Service_Audio, "(STUBBED) called"); IPC::RequestParser rp{ctx}; const std::string audio_interface = "AudioDevice"; @@ -216,7 +332,7 @@ private: } void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_WARNING(Service_Audio, "(STUBBED) called"); buffer_event->Signal(); @@ -226,7 +342,7 @@ private: } void GetActiveChannelCount(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_WARNING(Service_Audio, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(1); @@ -248,31 +364,33 @@ AudRenU::AudRenU() : ServiceFramework("audren:u") { } void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto params = rp.PopRaw<AudioRendererParameter>(); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<Audio::IAudioRenderer>(); + rb.PushIpcInterface<Audio::IAudioRenderer>(std::move(params)); - NGLOG_DEBUG(Service_Audio, "called"); + LOG_DEBUG(Service_Audio, "called"); } void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - auto params = rp.PopRaw<WorkerBufferParameters>(); + auto params = rp.PopRaw<AudioRendererParameter>(); - u64 buffer_sz = Common::AlignUp(4 * params.unknown8, 0x40); - buffer_sz += params.unknownC * 1024; - buffer_sz += 0x940 * (params.unknownC + 1); + u64 buffer_sz = Common::AlignUp(4 * params.unknown_8, 0x40); + buffer_sz += params.unknown_c * 1024; + buffer_sz += 0x940 * (params.unknown_c + 1); buffer_sz += 0x3F0 * params.voice_count; - buffer_sz += Common::AlignUp(8 * (params.unknownC + 1), 0x10); + buffer_sz += Common::AlignUp(8 * (params.unknown_c + 1), 0x10); buffer_sz += Common::AlignUp(8 * params.voice_count, 0x10); buffer_sz += - Common::AlignUp((0x3C0 * (params.sink_count + params.unknownC) + 4 * params.sample_count) * - (params.unknown8 + 6), + Common::AlignUp((0x3C0 * (params.sink_count + params.unknown_c) + 4 * params.sample_count) * + (params.unknown_8 + 6), 0x40); - if (IsFeatureSupported(AudioFeatures::Splitter, params.magic)) { - u32 count = params.unknownC + 1; + if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) { + u32 count = params.unknown_c + 1; u64 node_count = Common::AlignUp(count, 0x40); u64 node_state_buffer_sz = 4 * (node_count * node_count) + 0xC * node_count + 2 * (node_count / 8); @@ -287,20 +405,20 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { } buffer_sz += 0x20 * (params.effect_count + 4 * params.voice_count) + 0x50; - if (IsFeatureSupported(AudioFeatures::Splitter, params.magic)) { - buffer_sz += 0xE0 * params.unknown2c; + if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) { + buffer_sz += 0xE0 * params.unknown_2c; buffer_sz += 0x20 * params.splitter_count; - buffer_sz += Common::AlignUp(4 * params.unknown2c, 0x10); + buffer_sz += Common::AlignUp(4 * params.unknown_2c, 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); - if (params.unknown1c >= 1) { + if (params.unknown_1c >= 1) { output_sz = Common::AlignUp(((16 * params.sink_count + 16 * params.effect_count + 16 * params.voice_count + 16) + 0x658) * - (params.unknown1c + 1) + + (params.unknown_1c + 1) + 0xc0, 0x40) + output_sz; @@ -312,7 +430,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.Push<u64>(output_sz); - NGLOG_DEBUG(Service_Audio, "called, buffer_size=0x{:X}", output_sz); + LOG_DEBUG(Service_Audio, "called, buffer_size=0x{:X}", output_sz); } void AudRenU::GetAudioDevice(Kernel::HLERequestContext& ctx) { @@ -321,14 +439,14 @@ void AudRenU::GetAudioDevice(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<Audio::IAudioDevice>(); - NGLOG_DEBUG(Service_Audio, "called"); + LOG_DEBUG(Service_Audio, "called"); } bool AudRenU::IsFeatureSupported(AudioFeatures feature, u32_le revision) const { u32_be version_num = (revision - Common::MakeMagic('R', 'E', 'V', '0')); // Byte swap switch (feature) { case AudioFeatures::Splitter: - return version_num >= 2; + return version_num >= 2u; default: return false; } diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h index fe53de4cef..b9b81db4f8 100644 --- a/src/core/hle/service/audio/audren_u.h +++ b/src/core/hle/service/audio/audren_u.h @@ -12,6 +12,24 @@ class HLERequestContext; namespace Service::Audio { +struct AudioRendererParameter { + u32_le sample_rate; + u32_le sample_count; + u32_le unknown_8; + u32_le unknown_c; + u32_le voice_count; + u32_le sink_count; + u32_le effect_count; + u32_le unknown_1c; + u8 unknown_20; + INSERT_PADDING_BYTES(3); + u32_le splitter_count; + u32_le unknown_2c; + INSERT_PADDING_WORDS(1); + u32_le revision; +}; +static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size"); + class AudRenU final : public ServiceFramework<AudRenU> { public: explicit AudRenU(); @@ -22,25 +40,6 @@ private: void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx); void GetAudioDevice(Kernel::HLERequestContext& ctx); - struct WorkerBufferParameters { - u32_le sample_rate; - u32_le sample_count; - u32_le unknown8; - u32_le unknownC; - u32_le voice_count; - u32_le sink_count; - u32_le effect_count; - u32_le unknown1c; - u8 unknown20; - u8 padding1[3]; - u32_le splitter_count; - u32_le unknown2c; - u8 padding2[4]; - u32_le magic; - }; - static_assert(sizeof(WorkerBufferParameters) == 52, - "WorkerBufferParameters is an invalid size"); - enum class AudioFeatures : u32 { Splitter, }; diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp new file mode 100644 index 0000000000..844df382ca --- /dev/null +++ b/src/core/hle/service/audio/hwopus.cpp @@ -0,0 +1,29 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/service/audio/hwopus.h" + +namespace Service::Audio { + +void HwOpus::GetWorkBufferSize(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Audio, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0x4000); +} + +HwOpus::HwOpus() : ServiceFramework("hwopus") { + static const FunctionInfo functions[] = { + {0, nullptr, "Initialize"}, + {1, &HwOpus::GetWorkBufferSize, "GetWorkBufferSize"}, + {2, nullptr, "InitializeMultiStream"}, + {3, nullptr, "GetWorkBufferSizeMultiStream"}, + }; + RegisterHandlers(functions); +} + +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/hwopus.h b/src/core/hle/service/audio/hwopus.h new file mode 100644 index 0000000000..090b8c8257 --- /dev/null +++ b/src/core/hle/service/audio/hwopus.h @@ -0,0 +1,20 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::Audio { + +class HwOpus final : public ServiceFramework<HwOpus> { +public: + explicit HwOpus(); + ~HwOpus() = default; + +private: + void GetWorkBufferSize(Kernel::HLERequestContext& ctx); +}; + +} // namespace Service::Audio diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp index 52be9db22f..35e024c3d9 100644 --- a/src/core/hle/service/bcat/module.cpp +++ b/src/core/hle/service/bcat/module.cpp @@ -36,7 +36,7 @@ void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IBcatService>(); - NGLOG_DEBUG(Service_BCAT, "called"); + LOG_DEBUG(Service_BCAT, "called"); } Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp index 2d42822093..299b9474f0 100644 --- a/src/core/hle/service/fatal/fatal.cpp +++ b/src/core/hle/service/fatal/fatal.cpp @@ -16,13 +16,13 @@ Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); u32 error_code = rp.Pop<u32>(); - NGLOG_WARNING(Service_Fatal, "(STUBBED) called, error_code=0x{:X}", error_code); + LOG_WARNING(Service_Fatal, "(STUBBED) called, error_code=0x{:X}", error_code); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void Module::Interface::ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_Fatal, "(STUBBED) called"); + LOG_WARNING(Service_Fatal, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index 68d1c90a5f..f58b518b62 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -25,14 +25,14 @@ ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& fact ASSERT_MSG(inserted, "Tried to register more than one system with same id code"); auto& filesystem = result.first->second; - NGLOG_DEBUG(Service_FS, "Registered file system {} with id code 0x{:08X}", - filesystem->GetName(), static_cast<u32>(type)); + LOG_DEBUG(Service_FS, "Registered file system {} with id code 0x{:08X}", filesystem->GetName(), + static_cast<u32>(type)); return RESULT_SUCCESS; } ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type, FileSys::Path& path) { - NGLOG_TRACE(Service_FS, "Opening FileSystem with type={}", static_cast<u32>(type)); + LOG_TRACE(Service_FS, "Opening FileSystem with type={}", static_cast<u32>(type)); auto itr = filesystem_map.find(type); if (itr == filesystem_map.end()) { @@ -44,7 +44,7 @@ ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type, } ResultCode FormatFileSystem(Type type) { - NGLOG_TRACE(Service_FS, "Formatting FileSystem with type={}", static_cast<u32>(type)); + LOG_TRACE(Service_FS, "Formatting FileSystem with type={}", static_cast<u32>(type)); auto itr = filesystem_map.find(type); if (itr == filesystem_map.end()) { diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index eb5748cf88..82efe7f7db 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -37,7 +37,7 @@ private: const s64 offset = rp.Pop<s64>(); const s64 length = rp.Pop<s64>(); - NGLOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length); + LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length); // Error checking if (length < 0) { @@ -89,7 +89,7 @@ private: const s64 offset = rp.Pop<s64>(); const s64 length = rp.Pop<s64>(); - NGLOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length); + LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length); // Error checking if (length < 0) { @@ -126,7 +126,7 @@ private: const s64 offset = rp.Pop<s64>(); const s64 length = rp.Pop<s64>(); - NGLOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length); + LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length); // Error checking if (length < 0) { @@ -154,7 +154,7 @@ private: } void Flush(Kernel::HLERequestContext& ctx) { - NGLOG_DEBUG(Service_FS, "called"); + LOG_DEBUG(Service_FS, "called"); backend->Flush(); IPC::ResponseBuilder rb{ctx, 2}; @@ -165,7 +165,7 @@ private: IPC::RequestParser rp{ctx}; const u64 size = rp.Pop<u64>(); backend->SetSize(size); - NGLOG_DEBUG(Service_FS, "called, size={}", size); + LOG_DEBUG(Service_FS, "called, size={}", size); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -173,7 +173,7 @@ private: void GetSize(Kernel::HLERequestContext& ctx) { const u64 size = backend->GetSize(); - NGLOG_DEBUG(Service_FS, "called, size={}", size); + LOG_DEBUG(Service_FS, "called, size={}", size); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); @@ -199,7 +199,7 @@ private: IPC::RequestParser rp{ctx}; const u64 unk = rp.Pop<u64>(); - NGLOG_DEBUG(Service_FS, "called, unk=0x{:X}", unk); + LOG_DEBUG(Service_FS, "called, unk=0x{:X}", unk); // Calculate how many entries we can fit in the output buffer u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry); @@ -221,7 +221,7 @@ private: } void GetEntryCount(Kernel::HLERequestContext& ctx) { - NGLOG_DEBUG(Service_FS, "called"); + LOG_DEBUG(Service_FS, "called"); u64 count = backend->GetEntryCount(); @@ -265,7 +265,7 @@ public: u64 mode = rp.Pop<u64>(); u32 size = rp.Pop<u32>(); - NGLOG_DEBUG(Service_FS, "called file {} mode 0x{:X} size 0x{:08X}", name, mode, size); + LOG_DEBUG(Service_FS, "called file {} mode 0x{:X} size 0x{:08X}", name, mode, size); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(backend->CreateFile(name, size)); @@ -277,7 +277,7 @@ public: auto file_buffer = ctx.ReadBuffer(); std::string name = Common::StringFromBuffer(file_buffer); - NGLOG_DEBUG(Service_FS, "called file {}", name); + LOG_DEBUG(Service_FS, "called file {}", name); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(backend->DeleteFile(name)); @@ -289,7 +289,7 @@ public: auto file_buffer = ctx.ReadBuffer(); std::string name = Common::StringFromBuffer(file_buffer); - NGLOG_DEBUG(Service_FS, "called directory {}", name); + LOG_DEBUG(Service_FS, "called directory {}", name); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(backend->CreateDirectory(name)); @@ -307,7 +307,7 @@ public: Memory::ReadBlock(ctx.BufferDescriptorX()[1].Address(), buffer.data(), buffer.size()); std::string dst_name = Common::StringFromBuffer(buffer); - NGLOG_DEBUG(Service_FS, "called file '{}' to file '{}'", src_name, dst_name); + LOG_DEBUG(Service_FS, "called file '{}' to file '{}'", src_name, dst_name); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(backend->RenameFile(src_name, dst_name)); @@ -321,7 +321,7 @@ public: auto mode = static_cast<FileSys::Mode>(rp.Pop<u32>()); - NGLOG_DEBUG(Service_FS, "called file {} mode {}", name, static_cast<u32>(mode)); + LOG_DEBUG(Service_FS, "called file {} mode {}", name, static_cast<u32>(mode)); auto result = backend->OpenFile(name, mode); if (result.Failed()) { @@ -346,7 +346,7 @@ public: // TODO(Subv): Implement this filter. u32 filter_flags = rp.Pop<u32>(); - NGLOG_DEBUG(Service_FS, "called directory {} filter {}", name, filter_flags); + LOG_DEBUG(Service_FS, "called directory {} filter {}", name, filter_flags); auto result = backend->OpenDirectory(name); if (result.Failed()) { @@ -368,7 +368,7 @@ public: auto file_buffer = ctx.ReadBuffer(); std::string name = Common::StringFromBuffer(file_buffer); - NGLOG_DEBUG(Service_FS, "called file {}", name); + LOG_DEBUG(Service_FS, "called file {}", name); auto result = backend->GetEntryType(name); if (result.Failed()) { @@ -383,7 +383,7 @@ public: } void Commit(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_FS, "(STUBBED) called"); + LOG_WARNING(Service_FS, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -499,14 +499,14 @@ void FSP_SRV::TryLoadRomFS() { } void FSP_SRV::Initialize(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_FS, "(STUBBED) called"); + LOG_WARNING(Service_FS, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) { - NGLOG_DEBUG(Service_FS, "called"); + LOG_DEBUG(Service_FS, "called"); FileSys::Path unused; auto filesystem = OpenFileSystem(Type::SDMC, unused).Unwrap(); @@ -523,14 +523,14 @@ void FSP_SRV::CreateSaveData(Kernel::HLERequestContext& ctx) { auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>(); u128 uid = rp.PopRaw<u128>(); - NGLOG_WARNING(Service_FS, "(STUBBED) called uid = {:016X}{:016X}", uid[1], uid[0]); + LOG_WARNING(Service_FS, "(STUBBED) called uid = {:016X}{:016X}", uid[1], uid[0]); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_FS, "(STUBBED) called"); + LOG_WARNING(Service_FS, "(STUBBED) called"); // TODO(Subv): Read the input parameters and mount the requested savedata instead of always // mounting the current process' savedata. @@ -549,7 +549,7 @@ void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) { } void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_FS, "(STUBBED) called"); + LOG_WARNING(Service_FS, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); @@ -557,12 +557,12 @@ void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { } void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { - NGLOG_DEBUG(Service_FS, "called"); + LOG_DEBUG(Service_FS, "called"); TryLoadRomFS(); if (!romfs) { // TODO (bunnei): Find the right error code to use here - NGLOG_CRITICAL(Service_FS, "no file system interface available!"); + LOG_CRITICAL(Service_FS, "no file system interface available!"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultCode(-1)); return; @@ -571,7 +571,7 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { // Attempt to open a StorageBackend interface to the RomFS auto storage = romfs->OpenFile({}, {}); if (storage.Failed()) { - NGLOG_CRITICAL(Service_FS, "no storage interface available!"); + LOG_CRITICAL(Service_FS, "no storage interface available!"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(storage.Code()); return; @@ -583,7 +583,7 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { } void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_FS, "(STUBBED) called, using OpenDataStorageByCurrentProcess"); + LOG_WARNING(Service_FS, "(STUBBED) called, using OpenDataStorageByCurrentProcess"); OpenDataStorageByCurrentProcess(ctx); } diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index 94d9fbf253..c98a46e053 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp @@ -13,7 +13,7 @@ namespace Service::Friend { void Module::Interface::CreateFriendService(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_Friend, "(STUBBED) called"); + LOG_WARNING(Service_Friend, "(STUBBED) called"); } Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 00c5308bad..b0f4a384ed 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -53,7 +53,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(shared_mem); - NGLOG_DEBUG(Service_HID, "called"); + LOG_DEBUG(Service_HID, "called"); } void LoadInputDevices() { @@ -75,7 +75,7 @@ private: // Set up controllers as neon red+blue Joy-Con attached to console ControllerHeader& controller_header = mem.controllers[Controller_Handheld].header; - controller_header.type = ControllerType_Handheld | ControllerType_JoyconPair; + controller_header.type = ControllerType_Handheld; controller_header.single_colors_descriptor = ColorDesc_ColorsNonexistent; controller_header.right_color_body = JOYCON_BODY_NEON_RED; controller_header.right_color_buttons = JOYCON_BUTTONS_NEON_RED; @@ -90,19 +90,22 @@ private: // HID shared memory stores the state of the past 17 samples in a circlular buffer, // each with a timestamp in number of samples since boot. + const ControllerInputEntry& last_entry = layout.entries[layout.header.latest_entry]; + layout.header.timestamp_ticks = CoreTiming::GetTicks(); layout.header.latest_entry = (layout.header.latest_entry + 1) % HID_NUM_ENTRIES; ControllerInputEntry& entry = layout.entries[layout.header.latest_entry]; - entry.connection_state = ConnectionState_Connected | ConnectionState_Wired; - entry.timestamp++; + entry.timestamp = last_entry.timestamp + 1; // TODO(shinyquagsire23): Is this always identical to timestamp? - entry.timestamp_2++; + entry.timestamp_2 = entry.timestamp; // TODO(shinyquagsire23): More than just handheld input if (controller != Controller_Handheld) continue; + entry.connection_state = ConnectionState_Connected | ConnectionState_Wired; + // TODO(shinyquagsire23): Set up some LUTs for each layout mapping in the future? // For now everything is just the default handheld layout, but split Joy-Con will // rotate the face buttons and directions for certain layouts. @@ -262,7 +265,7 @@ private: void ActivateVibrationDevice(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_WARNING(Service_HID, "(STUBBED) called"); } }; @@ -394,144 +397,144 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IAppletResource>(applet_resource); - NGLOG_DEBUG(Service_HID, "called"); + LOG_DEBUG(Service_HID, "called"); } void ActivateDebugPad(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_WARNING(Service_HID, "(STUBBED) called"); } void ActivateTouchScreen(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_WARNING(Service_HID, "(STUBBED) called"); } void ActivateMouse(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_WARNING(Service_HID, "(STUBBED) called"); } void ActivateKeyboard(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_WARNING(Service_HID, "(STUBBED) called"); } void StartSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_WARNING(Service_HID, "(STUBBED) called"); } void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_WARNING(Service_HID, "(STUBBED) called"); } void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_WARNING(Service_HID, "(STUBBED) called"); } void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(0); - NGLOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_WARNING(Service_HID, "(STUBBED) called"); } void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_WARNING(Service_HID, "(STUBBED) called"); } void ActivateNpad(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_WARNING(Service_HID, "(STUBBED) called"); } void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(event); - NGLOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_WARNING(Service_HID, "(STUBBED) called"); } void GetPlayerLedPattern(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_WARNING(Service_HID, "(STUBBED) called"); } void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_WARNING(Service_HID, "(STUBBED) called"); } void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push(joy_hold_type); - NGLOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_WARNING(Service_HID, "(STUBBED) called"); } void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_WARNING(Service_HID, "(STUBBED) called"); } void SendVibrationValue(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_WARNING(Service_HID, "(STUBBED) called"); } void GetActualVibrationValue(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_WARNING(Service_HID, "(STUBBED) called"); } void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_WARNING(Service_HID, "(STUBBED) called"); } void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_WARNING(Service_HID, "(STUBBED) called"); } void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); rb.Push<u64>(0); - NGLOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_WARNING(Service_HID, "(STUBBED) called"); } void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IActiveVibrationDeviceList>(); - NGLOG_DEBUG(Service_HID, "called"); + LOG_DEBUG(Service_HID, "called"); } void SendVibrationValues(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_WARNING(Service_HID, "(STUBBED) called"); } }; diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 15eee8f013..b499308d63 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -12,7 +12,7 @@ namespace Service::HID { // Begin enums and output structs constexpr u32 HID_NUM_ENTRIES = 17; -constexpr u32 HID_NUM_LAYOUTS = 2; +constexpr u32 HID_NUM_LAYOUTS = 7; constexpr s32 HID_JOYSTICK_MAX = 0x8000; constexpr s32 HID_JOYSTICK_MIN = -0x8000; diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp index 46194643e8..e85a8bdb9e 100644 --- a/src/core/hle/service/lm/lm.cpp +++ b/src/core/hle/service/lm/lm.cpp @@ -141,19 +141,19 @@ private: if (header.IsTailLog()) { switch (header.severity) { case MessageHeader::Severity::Trace: - NGLOG_TRACE(Debug_Emulated, "{}", log_stream.str()); + LOG_TRACE(Debug_Emulated, "{}", log_stream.str()); break; case MessageHeader::Severity::Info: - NGLOG_INFO(Debug_Emulated, "{}", log_stream.str()); + LOG_INFO(Debug_Emulated, "{}", log_stream.str()); break; case MessageHeader::Severity::Warning: - NGLOG_WARNING(Debug_Emulated, "{}", log_stream.str()); + LOG_WARNING(Debug_Emulated, "{}", log_stream.str()); break; case MessageHeader::Severity::Error: - NGLOG_ERROR(Debug_Emulated, "{}", log_stream.str()); + LOG_ERROR(Debug_Emulated, "{}", log_stream.str()); break; case MessageHeader::Severity::Critical: - NGLOG_CRITICAL(Debug_Emulated, "{}", log_stream.str()); + LOG_CRITICAL(Debug_Emulated, "{}", log_stream.str()); break; } } @@ -178,7 +178,7 @@ void LM::Initialize(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<Logger>(); - NGLOG_DEBUG(Service_LM, "called"); + LOG_DEBUG(Service_LM, "called"); } LM::LM() : ServiceFramework("lm") { diff --git a/src/core/hle/service/mm/mm_u.cpp b/src/core/hle/service/mm/mm_u.cpp index b3a85b8180..08f45b78af 100644 --- a/src/core/hle/service/mm/mm_u.cpp +++ b/src/core/hle/service/mm/mm_u.cpp @@ -14,7 +14,7 @@ void InstallInterfaces(SM::ServiceManager& service_manager) { } void MM_U::Initialize(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_MM, "(STUBBED) called"); + LOG_WARNING(Service_MM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -25,13 +25,13 @@ void MM_U::SetAndWait(Kernel::HLERequestContext& ctx) { max = rp.Pop<u32>(); current = min; - NGLOG_WARNING(Service_MM, "(STUBBED) called, min=0x{:X}, max=0x{:X}", min, max); + LOG_WARNING(Service_MM, "(STUBBED) called, min=0x{:X}, max=0x{:X}", min, max); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void MM_U::Get(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_MM, "(STUBBED) called"); + LOG_WARNING(Service_MM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push(current); diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 2a9f840376..56b05e9e80 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp @@ -64,7 +64,7 @@ private: }; void Initialize(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_NFP, "(STUBBED) called"); + LOG_WARNING(Service_NFP, "(STUBBED) called"); state = State::Initialized; @@ -78,7 +78,7 @@ private: ctx.WriteBuffer(&device_handle, sizeof(device_handle)); - NGLOG_WARNING(Service_NFP, "(STUBBED) called, array_size={}", array_size); + LOG_WARNING(Service_NFP, "(STUBBED) called, array_size={}", array_size); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); @@ -88,7 +88,7 @@ private: void AttachActivateEvent(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u64 dev_handle = rp.Pop<u64>(); - NGLOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); + LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); @@ -98,7 +98,7 @@ private: void AttachDeactivateEvent(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u64 dev_handle = rp.Pop<u64>(); - NGLOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); + LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); @@ -106,14 +106,14 @@ private: } void GetState(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_NFP, "(STUBBED) called"); + LOG_WARNING(Service_NFP, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(static_cast<u32>(state)); } void GetDeviceState(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_NFP, "(STUBBED) called"); + LOG_WARNING(Service_NFP, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(static_cast<u32>(device_state)); @@ -122,7 +122,7 @@ private: void GetNpadId(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u64 dev_handle = rp.Pop<u64>(); - NGLOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); + LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(npad_id); @@ -131,7 +131,7 @@ private: void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u64 dev_handle = rp.Pop<u64>(); - NGLOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); + LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); @@ -148,7 +148,7 @@ private: }; void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { - NGLOG_DEBUG(Service_NFP, "called"); + LOG_DEBUG(Service_NFP, "called"); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IUser>(); diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index 62489c7fee..54a151c265 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -62,33 +62,33 @@ public: private: void GetRequestState(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_NIFM, "(STUBBED) called"); + LOG_WARNING(Service_NIFM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(0); } void GetResult(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_NIFM, "(STUBBED) called"); + LOG_WARNING(Service_NIFM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void GetSystemEventReadableHandles(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_NIFM, "(STUBBED) called"); + LOG_WARNING(Service_NIFM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2, 2}; rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(event1, event2); } void Cancel(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_NIFM, "(STUBBED) called"); + LOG_WARNING(Service_NIFM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void SetConnectionConfirmationOption(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_NIFM, "(STUBBED) called"); + LOG_WARNING(Service_NIFM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -114,7 +114,7 @@ public: private: void GetClientId(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_NIFM, "(STUBBED) called"); + LOG_WARNING(Service_NIFM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); rb.Push<u64>(0); @@ -125,7 +125,7 @@ private: rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IScanRequest>(); - NGLOG_DEBUG(Service_NIFM, "called"); + LOG_DEBUG(Service_NIFM, "called"); } void CreateRequest(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; @@ -133,10 +133,10 @@ private: rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IRequest>(); - NGLOG_DEBUG(Service_NIFM, "called"); + LOG_DEBUG(Service_NIFM, "called"); } void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_NIFM, "(STUBBED) called"); + LOG_WARNING(Service_NIFM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -146,7 +146,7 @@ private: rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<INetworkProfile>(); - NGLOG_DEBUG(Service_NIFM, "called"); + LOG_DEBUG(Service_NIFM, "called"); } }; @@ -196,14 +196,14 @@ void Module::Interface::CreateGeneralServiceOld(Kernel::HLERequestContext& ctx) IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IGeneralService>(); - NGLOG_DEBUG(Service_NIFM, "called"); + LOG_DEBUG(Service_NIFM, "called"); } void Module::Interface::CreateGeneralService(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IGeneralService>(); - NGLOG_DEBUG(Service_NIFM, "called"); + LOG_DEBUG(Service_NIFM, "called"); } Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp index 636af9a1e3..d6a12ede51 100644 --- a/src/core/hle/service/ns/pl_u.cpp +++ b/src/core/hle/service/ns/pl_u.cpp @@ -52,7 +52,7 @@ PL_U::PL_U() : ServiceFramework("pl:u") { ASSERT(file.GetSize() == SHARED_FONT_MEM_SIZE); file.ReadBytes(shared_font->data(), shared_font->size()); } else { - NGLOG_WARNING(Service_NS, "Unable to load shared font: {}", filepath); + LOG_WARNING(Service_NS, "Unable to load shared font: {}", filepath); } } @@ -60,7 +60,7 @@ void PL_U::RequestLoad(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u32 shared_font_type{rp.Pop<u32>()}; - NGLOG_DEBUG(Service_NS, "called, shared_font_type={}", shared_font_type); + LOG_DEBUG(Service_NS, "called, shared_font_type={}", shared_font_type); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -69,7 +69,7 @@ void PL_U::GetLoadState(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u32 font_id{rp.Pop<u32>()}; - NGLOG_DEBUG(Service_NS, "called, font_id={}", font_id); + LOG_DEBUG(Service_NS, "called, font_id={}", font_id); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(static_cast<u32>(LoadState::Done)); @@ -79,7 +79,7 @@ void PL_U::GetSize(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u32 font_id{rp.Pop<u32>()}; - NGLOG_DEBUG(Service_NS, "called, font_id={}", font_id); + LOG_DEBUG(Service_NS, "called, font_id={}", font_id); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(SHARED_FONT_REGIONS[font_id].size); @@ -89,7 +89,7 @@ void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u32 font_id{rp.Pop<u32>()}; - NGLOG_DEBUG(Service_NS, "called, font_id={}", font_id); + LOG_DEBUG(Service_NS, "called, font_id={}", font_id); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(SHARED_FONT_REGIONS[font_id].offset); @@ -110,7 +110,7 @@ void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE, "PL_U:shared_font_mem"); - NGLOG_DEBUG(Service_NS, "called"); + LOG_DEBUG(Service_NS, "called"); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(shared_font_mem); @@ -119,7 +119,7 @@ void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u64 language_code{rp.Pop<u64>()}; // TODO(ogniK): Find out what this is used for - NGLOG_DEBUG(Service_NS, "called, language_code=%lx", language_code); + LOG_DEBUG(Service_NS, "called, language_code=%lx", language_code); IPC::ResponseBuilder rb{ctx, 4}; std::vector<u32> font_codes; std::vector<u32> font_offsets; diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index 103e66d0c3..c39d5a1645 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -20,9 +20,9 @@ u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform) { VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle); - NGLOG_WARNING(Service, - "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}", - addr, offset, width, height, stride, format); + LOG_WARNING(Service, + "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}", + addr, offset, width, height, stride, format); using PixelFormat = Tegra::FramebufferConfig::PixelFormat; const Tegra::FramebufferConfig framebuffer{ diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index c1eea861df..57b128b40a 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -8,12 +8,14 @@ #include "core/core.h" #include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" #include "core/hle/service/nvdrv/devices/nvmap.h" +#include "video_core/renderer_base.h" +#include "video_core/video_core.h" namespace Service::Nvidia::Devices { u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { - NGLOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", - command.raw, input.size(), output.size()); + LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", + command.raw, input.size(), output.size()); switch (static_cast<IoctlCommand>(command.raw)) { case IoctlCommand::IocInitalizeExCommand: @@ -40,15 +42,15 @@ u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vecto u32 nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output) { IoctlInitalizeEx params{}; std::memcpy(¶ms, input.data(), input.size()); - NGLOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x{:X}", params.big_page_size); + LOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x{:X}", params.big_page_size); return 0; } u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output) { IoctlAllocSpace params{}; std::memcpy(¶ms, input.data(), input.size()); - NGLOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages, - params.page_size, params.flags); + LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages, + params.page_size, params.flags); auto& gpu = Core::System::GetInstance().GPU(); const u64 size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)}; @@ -65,7 +67,7 @@ u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) { size_t num_entries = input.size() / sizeof(IoctlRemapEntry); - NGLOG_WARNING(Service_NVDRV, "(STUBBED) called, num_entries=0x{:X}", num_entries); + LOG_WARNING(Service_NVDRV, "(STUBBED) called, num_entries=0x{:X}", num_entries); std::vector<IoctlRemapEntry> entries(num_entries); std::memcpy(entries.data(), input.data(), input.size()); @@ -73,8 +75,8 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) auto& gpu = Core::System::GetInstance().GPU(); for (const auto& entry : entries) { - NGLOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", - entry.offset, entry.nvmap_handle, entry.pages); + LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", + entry.offset, entry.nvmap_handle, entry.pages); Tegra::GPUVAddr offset = static_cast<Tegra::GPUVAddr>(entry.offset) << 0x10; auto object = nvmap_dev->GetObject(entry.nvmap_handle); @@ -96,11 +98,11 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou IoctlMapBufferEx params{}; std::memcpy(¶ms, input.data(), input.size()); - NGLOG_DEBUG(Service_NVDRV, - "called, flags={:X}, nvmap_handle={:X}, buffer_offset={}, mapping_size={}" - ", offset={}", - params.flags, params.nvmap_handle, params.buffer_offset, params.mapping_size, - params.offset); + LOG_DEBUG(Service_NVDRV, + "called, flags={:X}, nvmap_handle={:X}, buffer_offset={}, mapping_size={}" + ", offset={}", + params.flags, params.nvmap_handle, params.buffer_offset, params.mapping_size, + params.offset); if (!params.nvmap_handle) { return 0; @@ -146,7 +148,7 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou IoctlUnmapBuffer params{}; std::memcpy(¶ms, input.data(), input.size()); - NGLOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset); + LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset); auto& gpu = Core::System::GetInstance().GPU(); @@ -154,6 +156,9 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou ASSERT_MSG(itr != buffer_mappings.end(), "Tried to unmap invalid mapping"); + // Remove this memory region from the rasterizer cache. + VideoCore::g_renderer->Rasterizer()->FlushAndInvalidateRegion(params.offset, itr->second.size); + params.offset = gpu.memory_manager->UnmapBuffer(params.offset, itr->second.size); buffer_mappings.erase(itr->second.offset); @@ -165,7 +170,7 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) { IoctlBindChannel params{}; std::memcpy(¶ms, input.data(), input.size()); - NGLOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd); + LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd); channel = params.fd; return 0; } @@ -173,8 +178,8 @@ u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& ou u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) { IoctlGetVaRegions params{}; std::memcpy(¶ms, input.data(), input.size()); - NGLOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr, - params.buf_size); + LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr, + params.buf_size); params.buf_size = 0x30; params.regions[0].offset = 0x04000000; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index 7872d1e094..671b092e17 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -9,8 +9,8 @@ namespace Service::Nvidia::Devices { u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { - NGLOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", - command.raw, input.size(), output.size()); + LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", + command.raw, input.size(), output.size()); switch (static_cast<IoctlCommand>(command.raw)) { case IoctlCommand::IocGetConfigCommand: @@ -29,33 +29,18 @@ u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector< u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) { IocGetConfigParams params{}; std::memcpy(¶ms, input.data(), sizeof(params)); - NGLOG_DEBUG(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(), - params.param_str.data()); - - if (!strcmp(params.domain_str.data(), "nv")) { - if (!strcmp(params.param_str.data(), "NV_MEMORY_PROFILER")) { - params.config_str[0] = '0'; - } else if (!strcmp(params.param_str.data(), "NVN_THROUGH_OPENGL")) { - params.config_str[0] = '0'; - } else if (!strcmp(params.param_str.data(), "NVRM_GPU_PREVENT_USE")) { - params.config_str[0] = '0'; - } else { - params.config_str[0] = '\0'; - } - } else { - UNIMPLEMENTED(); // unknown domain? Only nv has been seen so far on hardware - } - std::memcpy(output.data(), ¶ms, sizeof(params)); - return 0; + LOG_TRACE(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(), + params.param_str.data()); + return 0x30006; // Returns error on production mode } u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async) { IocCtrlEventWaitParams params{}; std::memcpy(¶ms, input.data(), sizeof(params)); - NGLOG_WARNING(Service_NVDRV, - "(STUBBED) called, syncpt_id={}, threshold={}, timeout={}, is_async={}", - params.syncpt_id, params.threshold, params.timeout, is_async); + LOG_WARNING(Service_NVDRV, + "(STUBBED) called, syncpt_id={}, threshold={}, timeout={}, is_async={}", + params.syncpt_id, params.threshold, params.timeout, is_async); // TODO(Subv): Implement actual syncpt waiting. params.value = 0; @@ -64,7 +49,7 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& } u32 nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) { - NGLOG_WARNING(Service_NVDRV, "(STUBBED) called"); + LOG_WARNING(Service_NVDRV, "(STUBBED) called"); // TODO(bunnei): Implement this. return 0; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index 0abc0de834..44e062f50d 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -10,8 +10,8 @@ namespace Service::Nvidia::Devices { u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { - NGLOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", - command.raw, input.size(), output.size()); + LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", + command.raw, input.size(), output.size()); switch (static_cast<IoctlCommand>(command.raw)) { case IoctlCommand::IocGetCharacteristicsCommand: @@ -36,7 +36,7 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vec } u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output) { - NGLOG_DEBUG(Service_NVDRV, "called"); + LOG_DEBUG(Service_NVDRV, "called"); IoctlCharacteristics params{}; std::memcpy(¶ms, input.data(), input.size()); params.gc.arch = 0x120; @@ -83,8 +83,8 @@ u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vecto u32 nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output) { IoctlGpuGetTpcMasksArgs params{}; std::memcpy(¶ms, input.data(), input.size()); - NGLOG_INFO(Service_NVDRV, "called, mask=0x{:X}, mask_buf_addr=0x{:X}", params.mask_buf_size, - params.mask_buf_addr); + LOG_INFO(Service_NVDRV, "called, mask=0x{:X}, mask_buf_addr=0x{:X}", params.mask_buf_size, + params.mask_buf_addr); // TODO(ogniK): Confirm value on hardware if (params.mask_buf_size) params.tpc_mask_size = 4 * 1; // 4 * num_gpc @@ -95,7 +95,7 @@ u32 nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& } u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output) { - NGLOG_DEBUG(Service_NVDRV, "called"); + LOG_DEBUG(Service_NVDRV, "called"); IoctlActiveSlotMask params{}; std::memcpy(¶ms, input.data(), input.size()); params.slot = 0x07; @@ -105,7 +105,7 @@ u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector } u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output) { - NGLOG_DEBUG(Service_NVDRV, "called"); + LOG_DEBUG(Service_NVDRV, "called"); IoctlZcullGetCtxSize params{}; std::memcpy(¶ms, input.data(), input.size()); params.size = 0x1; @@ -114,7 +114,7 @@ u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u } u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output) { - NGLOG_DEBUG(Service_NVDRV, "called"); + LOG_DEBUG(Service_NVDRV, "called"); IoctlNvgpuGpuZcullGetInfoArgs params{}; std::memcpy(¶ms, input.data(), input.size()); params.width_align_pixels = 0x20; @@ -132,7 +132,7 @@ u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& } u32 nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output) { - NGLOG_WARNING(Service_NVDRV, "(STUBBED) called"); + LOG_WARNING(Service_NVDRV, "(STUBBED) called"); IoctlZbcSetTable params{}; std::memcpy(¶ms, input.data(), input.size()); // TODO(ogniK): What does this even actually do? @@ -141,7 +141,7 @@ u32 nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& } u32 nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output) { - NGLOG_WARNING(Service_NVDRV, "(STUBBED) called"); + LOG_WARNING(Service_NVDRV, "(STUBBED) called"); IoctlZbcQueryTable params{}; std::memcpy(¶ms, input.data(), input.size()); // TODO : To implement properly @@ -150,7 +150,7 @@ u32 nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vector<u8> } u32 nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& output) { - NGLOG_WARNING(Service_NVDRV, "(STUBBED) called"); + LOG_WARNING(Service_NVDRV, "(STUBBED) called"); IoctlFlushL2 params{}; std::memcpy(¶ms, input.data(), input.size()); // TODO : To implement properly diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 79aab87f96..8de8705966 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -12,8 +12,8 @@ namespace Service::Nvidia::Devices { u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { - NGLOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", - command.raw, input.size(), output.size()); + LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", + command.raw, input.size(), output.size()); switch (static_cast<IoctlCommand>(command.raw)) { case IoctlCommand::IocSetNVMAPfdCommand: @@ -51,13 +51,13 @@ u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u u32 nvhost_gpu::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) { IoctlSetNvmapFD params{}; std::memcpy(¶ms, input.data(), input.size()); - NGLOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); + LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); nvmap_fd = params.nvmap_fd; return 0; } u32 nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>& output) { - NGLOG_DEBUG(Service_NVDRV, "called"); + LOG_DEBUG(Service_NVDRV, "called"); IoctlClientData params{}; std::memcpy(¶ms, input.data(), input.size()); user_data = params.data; @@ -65,7 +65,7 @@ u32 nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>& out } u32 nvhost_gpu::GetClientData(const std::vector<u8>& input, std::vector<u8>& output) { - NGLOG_DEBUG(Service_NVDRV, "called"); + LOG_DEBUG(Service_NVDRV, "called"); IoctlClientData params{}; std::memcpy(¶ms, input.data(), input.size()); params.data = user_data; @@ -75,8 +75,8 @@ u32 nvhost_gpu::GetClientData(const std::vector<u8>& input, std::vector<u8>& out u32 nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& output) { std::memcpy(&zcull_params, input.data(), input.size()); - NGLOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va, - zcull_params.mode); + LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va, + zcull_params.mode); std::memcpy(output.data(), &zcull_params, output.size()); return 0; } @@ -84,26 +84,26 @@ u32 nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& output) u32 nvhost_gpu::SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output) { IoctlSetErrorNotifier params{}; std::memcpy(¶ms, input.data(), input.size()); - NGLOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", - params.offset, params.size, params.mem); + LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset, + params.size, params.mem); std::memcpy(output.data(), ¶ms, output.size()); return 0; } u32 nvhost_gpu::SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output) { std::memcpy(&channel_priority, input.data(), input.size()); - NGLOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority); + LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority); return 0; } u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output) { IoctlAllocGpfifoEx2 params{}; std::memcpy(¶ms, input.data(), input.size()); - NGLOG_WARNING(Service_NVDRV, - "(STUBBED) called, num_entries={:X}, flags={:X}, unk0={:X}, " - "unk1={:X}, unk2={:X}, unk3={:X}", - params.num_entries, params.flags, params.unk0, params.unk1, params.unk2, - params.unk3); + LOG_WARNING(Service_NVDRV, + "(STUBBED) called, num_entries={:X}, flags={:X}, unk0={:X}, " + "unk1={:X}, unk2={:X}, unk3={:X}", + params.num_entries, params.flags, params.unk0, params.unk1, params.unk2, + params.unk3); params.fence_out.id = 0; params.fence_out.value = 0; std::memcpy(output.data(), ¶ms, output.size()); @@ -113,20 +113,21 @@ u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& ou u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output) { IoctlAllocObjCtx params{}; std::memcpy(¶ms, input.data(), input.size()); - NGLOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num, - params.flags); + LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num, + params.flags); params.obj_id = 0x0; std::memcpy(output.data(), ¶ms, output.size()); return 0; } u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) { - if (input.size() < sizeof(IoctlSubmitGpfifo)) + if (input.size() < sizeof(IoctlSubmitGpfifo)) { UNIMPLEMENTED(); + } IoctlSubmitGpfifo params{}; std::memcpy(¶ms, input.data(), sizeof(IoctlSubmitGpfifo)); - NGLOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}", - params.gpfifo, params.num_entries, params.flags); + LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}", + params.gpfifo, params.num_entries, params.flags); auto entries = std::vector<IoctlGpfifoEntry>(); entries.resize(params.num_entries); @@ -145,7 +146,7 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp u32 nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { IoctlGetWaitbase params{}; std::memcpy(¶ms, input.data(), sizeof(IoctlGetWaitbase)); - NGLOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown); + LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown); params.value = 0; // Seems to be hard coded at 0 std::memcpy(output.data(), ¶ms, output.size()); return 0; @@ -154,7 +155,7 @@ u32 nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& outpu u32 nvhost_gpu::ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output) { IoctlChannelSetTimeout params{}; std::memcpy(¶ms, input.data(), sizeof(IoctlChannelSetTimeout)); - NGLOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout); + LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout); return 0; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index 0b6c228981..b51c73ee89 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp @@ -9,8 +9,8 @@ namespace Service::Nvidia::Devices { u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { - NGLOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", - command.raw, input.size(), output.size()); + LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", + command.raw, input.size(), output.size()); switch (static_cast<IoctlCommand>(command.raw)) { case IoctlCommand::IocSetNVMAPfdCommand: @@ -24,7 +24,7 @@ u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, std::vector u32 nvhost_nvdec::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) { IoctlSetNvmapFD params{}; std::memcpy(¶ms, input.data(), input.size()); - NGLOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); + LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); nvmap_fd = params.nvmap_fd; return 0; } diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index 23fe981901..724eeb1392 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -52,7 +52,7 @@ u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { u32 handle = next_handle++; handles[handle] = std::move(object); - NGLOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size); + LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size); params.handle = handle; @@ -73,7 +73,7 @@ u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) { object->addr = params.addr; object->status = Object::Status::Allocated; - NGLOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr); + LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr); std::memcpy(output.data(), ¶ms, sizeof(params)); return 0; @@ -83,7 +83,7 @@ u32 nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) { IocGetIdParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - NGLOG_WARNING(Service_NVDRV, "called"); + LOG_WARNING(Service_NVDRV, "called"); auto object = GetObject(params.handle); ASSERT(object); @@ -98,7 +98,7 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) { IocFromIdParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - NGLOG_WARNING(Service_NVDRV, "(STUBBED) called"); + LOG_WARNING(Service_NVDRV, "(STUBBED) called"); auto itr = std::find_if(handles.begin(), handles.end(), [&](const auto& entry) { return entry.second->id == params.id; }); @@ -119,7 +119,7 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) { IocParamParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - NGLOG_WARNING(Service_NVDRV, "(STUBBED) called type={}", params.param); + LOG_WARNING(Service_NVDRV, "(STUBBED) called type={}", params.param); auto object = GetObject(params.handle); ASSERT(object); @@ -148,6 +148,7 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) { } u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) { + // TODO(Subv): These flags are unconfirmed. enum FreeFlags { Freed = 0, NotFreedYet = 1, @@ -156,20 +157,26 @@ u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) { IocFreeParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - NGLOG_WARNING(Service_NVDRV, "(STUBBED) called"); + LOG_WARNING(Service_NVDRV, "(STUBBED) called"); auto itr = handles.find(params.handle); ASSERT(itr != handles.end()); + ASSERT(itr->second->refcount > 0); + itr->second->refcount--; - params.refcount = itr->second->refcount; params.size = itr->second->size; - if (itr->second->refcount == 0) + if (itr->second->refcount == 0) { params.flags = Freed; - else + // The address of the nvmap is written to the output if we're finally freeing it, otherwise + // 0 is written. + params.address = itr->second->addr; + } else { params.flags = NotFreedYet; + params.address = 0; + } handles.erase(params.handle); diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h index 39fafaa7cf..f2eec64098 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.h +++ b/src/core/hle/service/nvdrv/devices/nvmap.h @@ -94,7 +94,7 @@ private: struct IocFreeParams { u32_le handle; INSERT_PADDING_BYTES(4); - u64_le refcount; + u64_le address; u32_le size; u32_le flags; }; diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp index 45d2862ef7..b10efd5c94 100644 --- a/src/core/hle/service/nvdrv/interface.cpp +++ b/src/core/hle/service/nvdrv/interface.cpp @@ -12,7 +12,7 @@ namespace Service::Nvidia { void NVDRV::Open(Kernel::HLERequestContext& ctx) { - NGLOG_DEBUG(Service_NVDRV, "called"); + LOG_DEBUG(Service_NVDRV, "called"); const auto& buffer = ctx.ReadBuffer(); std::string device_name(buffer.begin(), buffer.end()); @@ -25,7 +25,7 @@ void NVDRV::Open(Kernel::HLERequestContext& ctx) { } void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) { - NGLOG_DEBUG(Service_NVDRV, "called"); + LOG_DEBUG(Service_NVDRV, "called"); IPC::RequestParser rp{ctx}; u32 fd = rp.Pop<u32>(); @@ -41,7 +41,7 @@ void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) { } void NVDRV::Close(Kernel::HLERequestContext& ctx) { - NGLOG_DEBUG(Service_NVDRV, "called"); + LOG_DEBUG(Service_NVDRV, "called"); IPC::RequestParser rp{ctx}; u32 fd = rp.Pop<u32>(); @@ -53,7 +53,7 @@ void NVDRV::Close(Kernel::HLERequestContext& ctx) { } void NVDRV::Initialize(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_NVDRV, "(STUBBED) called"); + LOG_WARNING(Service_NVDRV, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(0); @@ -63,7 +63,7 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; u32 fd = rp.Pop<u32>(); u32 event_id = rp.Pop<u32>(); - NGLOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id); + LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id); IPC::ResponseBuilder rb{ctx, 3, 1}; rb.Push(RESULT_SUCCESS); @@ -75,14 +75,14 @@ void NVDRV::SetClientPID(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; pid = rp.Pop<u64>(); - NGLOG_WARNING(Service_NVDRV, "(STUBBED) called, pid=0x{:X}", pid); + LOG_WARNING(Service_NVDRV, "(STUBBED) called, pid=0x{:X}", pid); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(0); } void NVDRV::FinishInitialize(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_NVDRV, "(STUBBED) called"); + LOG_WARNING(Service_NVDRV, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp index 49e88b3948..f7f2fe1b2e 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue.cpp @@ -23,7 +23,7 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) { buffer.igbp_buffer = igbp_buffer; buffer.status = Buffer::Status::Free; - NGLOG_WARNING(Service, "Adding graphics buffer {}", slot); + LOG_WARNING(Service, "Adding graphics buffer {}", slot); queue.emplace_back(buffer); @@ -94,7 +94,7 @@ void BufferQueue::ReleaseBuffer(u32 slot) { } u32 BufferQueue::Query(QueryType type) { - NGLOG_WARNING(Service, "(STUBBED) called type={}", static_cast<u32>(type)); + LOG_WARNING(Service, "(STUBBED) called type={}", static_cast<u32>(type)); switch (type) { case QueryType::NativeWindowFormat: // TODO(Subv): Use an enum for this diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 5c50ed6015..ef3c2cc985 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -48,7 +48,7 @@ NVFlinger::~NVFlinger() { } u64 NVFlinger::OpenDisplay(const std::string& name) { - NGLOG_WARNING(Service, "Opening display {}", name); + LOG_WARNING(Service, "Opening display {}", name); // TODO(Subv): Currently we only support the Default display. ASSERT(name == "Default"); diff --git a/src/core/hle/service/pctl/module.cpp b/src/core/hle/service/pctl/module.cpp index dd20d5ae79..fcf1f3da3b 100644 --- a/src/core/hle/service/pctl/module.cpp +++ b/src/core/hle/service/pctl/module.cpp @@ -112,7 +112,7 @@ public: private: void Initialize(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_PCTL, "(STUBBED) called"); + LOG_WARNING(Service_PCTL, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2, 0, 0}; rb.Push(RESULT_SUCCESS); } @@ -122,14 +122,14 @@ void Module::Interface::CreateService(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IParentalControlService>(); - NGLOG_DEBUG(Service_PCTL, "called"); + LOG_DEBUG(Service_PCTL, "called"); } void Module::Interface::CreateServiceWithoutInitialize(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IParentalControlService>(); - NGLOG_DEBUG(Service_PCTL, "called"); + LOG_DEBUG(Service_PCTL, "called"); } Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp index eaf30ee6b7..3c43b8d8cc 100644 --- a/src/core/hle/service/prepo/prepo.cpp +++ b/src/core/hle/service/prepo/prepo.cpp @@ -27,7 +27,7 @@ PlayReport::PlayReport(const char* name) : ServiceFramework(name) { void PlayReport::SaveReportWithUser(Kernel::HLERequestContext& ctx) { // TODO(ogniK): Do we want to add play report? - NGLOG_WARNING(Service_PREPO, "(STUBBED) called"); + LOG_WARNING(Service_PREPO, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index bdd9eb5a5e..0d036bfaae 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -122,7 +122,7 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(Kernel::HLERequestContext } buf.push_back('}'); - NGLOG_ERROR(Service, "unknown / unimplemented {}", fmt::to_string(buf)); + LOG_ERROR(Service, "unknown / unimplemented {}", fmt::to_string(buf)); UNIMPLEMENTED(); } @@ -133,7 +133,7 @@ void ServiceFrameworkBase::InvokeRequest(Kernel::HLERequestContext& ctx) { return ReportUnimplementedFunction(ctx, info); } - NGLOG_TRACE( + LOG_TRACE( Service, "{}", MakeFunctionString(info->name, GetServiceName().c_str(), ctx.CommandBuffer()).c_str()); handler_invoker(this, info->handler_callback, ctx); @@ -206,12 +206,12 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) { VI::InstallInterfaces(*sm, nv_flinger); Set::InstallInterfaces(*sm); - NGLOG_DEBUG(Service, "initialized OK"); + LOG_DEBUG(Service, "initialized OK"); } /// Shutdown ServiceManager void Shutdown() { g_kernel_named_ports.clear(); - NGLOG_DEBUG(Service, "shutdown OK"); + LOG_DEBUG(Service, "shutdown OK"); } } // namespace Service diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp index f0572bed6b..bd295cdf63 100644 --- a/src/core/hle/service/set/set.cpp +++ b/src/core/hle/service/set/set.cpp @@ -12,9 +12,6 @@ namespace Service::Set { void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - u32 id = rp.Pop<u32>(); - static constexpr std::array<LanguageCode, 17> available_language_codes = {{ LanguageCode::JA, LanguageCode::EN_US, @@ -40,7 +37,7 @@ void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.Push(static_cast<u64>(available_language_codes.size())); - NGLOG_DEBUG(Service_SET, "called"); + LOG_DEBUG(Service_SET, "called"); } SET::SET() : ServiceFramework("set") { @@ -50,7 +47,7 @@ SET::SET() : ServiceFramework("set") { {2, nullptr, "MakeLanguageCode"}, {3, nullptr, "GetAvailableLanguageCodeCount"}, {4, nullptr, "GetRegionCode"}, - {5, nullptr, "GetAvailableLanguageCodes2"}, + {5, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes2"}, {6, nullptr, "GetAvailableLanguageCodeCount2"}, {7, nullptr, "GetKeyCodeMap"}, {8, nullptr, "GetQuestFlag"}, diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp index 762a664c52..fa85277fe9 100644 --- a/src/core/hle/service/set/set_sys.cpp +++ b/src/core/hle/service/set/set_sys.cpp @@ -16,7 +16,7 @@ void SET_SYS::GetColorSetId(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.Push<u32>(0); - NGLOG_WARNING(Service_SET, "(STUBBED) called"); + LOG_WARNING(Service_SET, "(STUBBED) called"); } SET_SYS::SET_SYS() : ServiceFramework("set:sys") { diff --git a/src/core/hle/service/sm/controller.cpp b/src/core/hle/service/sm/controller.cpp index fe5097cdcd..518a0cc466 100644 --- a/src/core/hle/service/sm/controller.cpp +++ b/src/core/hle/service/sm/controller.cpp @@ -17,7 +17,7 @@ void Controller::ConvertSessionToDomain(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.Push<u32>(1); // Converted sessions start with 1 request handler - NGLOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetObjectId()); + LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetObjectId()); } void Controller::DuplicateSession(Kernel::HLERequestContext& ctx) { @@ -29,11 +29,11 @@ void Controller::DuplicateSession(Kernel::HLERequestContext& ctx) { Kernel::SharedPtr<Kernel::ClientSession> session{ctx.Session()->parent->client}; rb.PushMoveObjects(session); - NGLOG_DEBUG(Service, "called, session={}", session->GetObjectId()); + LOG_DEBUG(Service, "called, session={}", session->GetObjectId()); } void Controller::DuplicateSessionEx(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service, "(STUBBED) called, using DuplicateSession"); + LOG_WARNING(Service, "(STUBBED) called, using DuplicateSession"); DuplicateSession(ctx); } @@ -43,7 +43,7 @@ void Controller::QueryPointerBufferSize(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.Push<u32>(0x500); - NGLOG_WARNING(Service, "(STUBBED) called"); + LOG_WARNING(Service, "(STUBBED) called"); } Controller::Controller() : ServiceFramework("IpcController") { diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index bded8421f1..f22a2a79f6 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -86,7 +86,7 @@ SM::~SM() = default; void SM::Initialize(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_DEBUG(Service_SM, "called"); + LOG_DEBUG(Service_SM, "called"); } void SM::GetService(Kernel::HLERequestContext& ctx) { @@ -102,8 +102,7 @@ void SM::GetService(Kernel::HLERequestContext& ctx) { if (client_port.Failed()) { IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); rb.Push(client_port.Code()); - NGLOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, - client_port.Code().raw); + LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, client_port.Code().raw); if (name.length() == 0) return; // LibNX Fix UNIMPLEMENTED(); @@ -113,7 +112,7 @@ void SM::GetService(Kernel::HLERequestContext& ctx) { auto session = client_port.Unwrap()->Connect(); ASSERT(session.Succeeded()); if (session.Succeeded()) { - NGLOG_DEBUG(Service_SM, "called service={} -> session={}", name, (*session)->GetObjectId()); + LOG_DEBUG(Service_SM, "called service={} -> session={}", name, (*session)->GetObjectId()); IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles); rb.Push(session.Code()); diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index ab909fdaa3..32648bdd97 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp @@ -8,7 +8,7 @@ namespace Service::Sockets { void BSD::RegisterClient(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service, "(STUBBED) called"); + LOG_WARNING(Service, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; @@ -17,7 +17,7 @@ void BSD::RegisterClient(Kernel::HLERequestContext& ctx) { } void BSD::StartMonitoring(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service, "(STUBBED) called"); + LOG_WARNING(Service, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; @@ -32,8 +32,7 @@ void BSD::Socket(Kernel::HLERequestContext& ctx) { u32 type = rp.Pop<u32>(); u32 protocol = rp.Pop<u32>(); - NGLOG_WARNING(Service, "(STUBBED) called domain={} type={} protocol={}", domain, type, - protocol); + LOG_WARNING(Service, "(STUBBED) called domain={} type={} protocol={}", domain, type, protocol); u32 fd = next_fd++; @@ -45,7 +44,7 @@ void BSD::Socket(Kernel::HLERequestContext& ctx) { } void BSD::Connect(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service, "(STUBBED) called"); + LOG_WARNING(Service, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 4}; @@ -55,7 +54,7 @@ void BSD::Connect(Kernel::HLERequestContext& ctx) { } void BSD::SendTo(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service, "(STUBBED) called"); + LOG_WARNING(Service, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 4}; @@ -65,7 +64,7 @@ void BSD::SendTo(Kernel::HLERequestContext& ctx) { } void BSD::Close(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service, "(STUBBED) called"); + LOG_WARNING(Service, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 4}; diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp index f377e59f2c..d235c4cfd0 100644 --- a/src/core/hle/service/sockets/sfdnsres.cpp +++ b/src/core/hle/service/sockets/sfdnsres.cpp @@ -10,7 +10,7 @@ namespace Service::Sockets { void SFDNSRES::GetAddrInfo(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - NGLOG_WARNING(Service, "(STUBBED) called"); + LOG_WARNING(Service, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; diff --git a/src/core/hle/service/spl/module.cpp b/src/core/hle/service/spl/module.cpp index 76ba97156b..3f5a342a7b 100644 --- a/src/core/hle/service/spl/module.cpp +++ b/src/core/hle/service/spl/module.cpp @@ -28,7 +28,7 @@ void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - NGLOG_DEBUG(Service_SPL, "called"); + LOG_DEBUG(Service_SPL, "called"); } void InstallInterfaces(SM::ServiceManager& service_manager) { diff --git a/src/core/hle/service/ssl/ssl.cpp b/src/core/hle/service/ssl/ssl.cpp index b3dad8b068..40aea6090d 100644 --- a/src/core/hle/service/ssl/ssl.cpp +++ b/src/core/hle/service/ssl/ssl.cpp @@ -65,7 +65,7 @@ public: private: void SetOption(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_SSL, "(STUBBED) called"); + LOG_WARNING(Service_SSL, "(STUBBED) called"); IPC::RequestParser rp{ctx}; IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); @@ -73,7 +73,7 @@ private: } void CreateConnection(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_SSL, "(STUBBED) called"); + LOG_WARNING(Service_SSL, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); @@ -82,7 +82,7 @@ private: }; void SSL::CreateContext(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_SSL, "(STUBBED) called"); + LOG_WARNING(Service_SSL, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); @@ -103,7 +103,7 @@ SSL::SSL() : ServiceFramework("ssl") { } void SSL::SetInterfaceVersion(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_SSL, "(STUBBED) called"); + LOG_WARNING(Service_SSL, "(STUBBED) called"); IPC::RequestParser rp{ctx}; u32 unk1 = rp.Pop<u32>(); // Probably minor/major? u32 unk2 = rp.Pop<u32>(); // TODO(ogniK): Figure out what this does diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index 654012189e..507ae95f4e 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp @@ -33,14 +33,14 @@ private: const s64 time_since_epoch{std::chrono::duration_cast<std::chrono::seconds>( std::chrono::system_clock::now().time_since_epoch()) .count()}; - NGLOG_DEBUG(Service_Time, "called"); + LOG_DEBUG(Service_Time, "called"); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); rb.Push<u64>(time_since_epoch); } void GetSystemClockContext(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_Time, "(STUBBED) called"); + LOG_WARNING(Service_Time, "(STUBBED) called"); SystemClockContext system_clock_ontext{}; IPC::ResponseBuilder rb{ctx, (sizeof(SystemClockContext) / 4) + 2}; rb.Push(RESULT_SUCCESS); @@ -59,7 +59,7 @@ public: private: void GetCurrentTimePoint(Kernel::HLERequestContext& ctx) { - NGLOG_DEBUG(Service_Time, "called"); + LOG_DEBUG(Service_Time, "called"); SteadyClockTimePoint steady_clock_time_point{ CoreTiming::cyclesToMs(CoreTiming::GetTicks()) / 1000}; IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2}; @@ -91,21 +91,21 @@ private: TimeZoneRule my_time_zone_rule{}; void GetDeviceLocationName(Kernel::HLERequestContext& ctx) { - NGLOG_DEBUG(Service_Time, "called"); + LOG_DEBUG(Service_Time, "called"); IPC::ResponseBuilder rb{ctx, (sizeof(LocationName) / 4) + 2}; rb.Push(RESULT_SUCCESS); rb.PushRaw(location_name); } void GetTotalLocationNameCount(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_Time, "(STUBBED) called"); + LOG_WARNING(Service_Time, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(0); } void LoadTimeZoneRule(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_Time, "(STUBBED) called"); + LOG_WARNING(Service_Time, "(STUBBED) called"); ctx.WriteBuffer(&my_time_zone_rule, sizeof(TimeZoneRule)); @@ -117,7 +117,7 @@ private: IPC::RequestParser rp{ctx}; const u64 posix_time = rp.Pop<u64>(); - NGLOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x{:016X}", posix_time); + LOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x{:016X}", posix_time); TimeZoneRule time_zone_rule{}; auto buffer = ctx.ReadBuffer(); @@ -138,7 +138,7 @@ private: IPC::RequestParser rp{ctx}; const u64 posix_time = rp.Pop<u64>(); - NGLOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x{:016X}", posix_time); + LOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x{:016X}", posix_time); CalendarTime calendar_time{2018, 1, 1, 0, 0, 0}; CalendarAdditionalInfo additional_info{}; @@ -176,35 +176,35 @@ void Module::Interface::GetStandardUserSystemClock(Kernel::HLERequestContext& ct IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ISystemClock>(); - NGLOG_DEBUG(Service_Time, "called"); + LOG_DEBUG(Service_Time, "called"); } void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ISystemClock>(); - NGLOG_DEBUG(Service_Time, "called"); + LOG_DEBUG(Service_Time, "called"); } void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ISteadyClock>(); - NGLOG_DEBUG(Service_Time, "called"); + LOG_DEBUG(Service_Time, "called"); } void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ITimeZoneService>(); - NGLOG_DEBUG(Service_Time, "called"); + LOG_DEBUG(Service_Time, "called"); } void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ISystemClock>(); - NGLOG_DEBUG(Service_Time, "called"); + LOG_DEBUG(Service_Time, "called"); } Module::Interface::Interface(std::shared_ptr<Module> time, const char* name) diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index e86556671e..f3765b555c 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -470,7 +470,7 @@ private: u32 flags = rp.Pop<u32>(); auto buffer_queue = nv_flinger->GetBufferQueue(id); - NGLOG_DEBUG(Service_VI, "called, transaction={:X}", static_cast<u32>(transaction)); + LOG_DEBUG(Service_VI, "called, transaction={:X}", static_cast<u32>(transaction)); if (transaction == TransactionId::Connect) { IGBPConnectRequestParcel request{ctx.ReadBuffer()}; @@ -532,7 +532,7 @@ private: IGBPQueryResponseParcel response{value}; ctx.WriteBuffer(response.Serialize()); } else if (transaction == TransactionId::CancelBuffer) { - NGLOG_WARNING(Service_VI, "(STUBBED) called, transaction=CancelBuffer"); + LOG_WARNING(Service_VI, "(STUBBED) called, transaction=CancelBuffer"); } else { ASSERT_MSG(false, "Unimplemented"); } @@ -547,8 +547,8 @@ private: s32 addval = rp.PopRaw<s32>(); u32 type = rp.Pop<u32>(); - NGLOG_WARNING(Service_VI, "(STUBBED) called id={}, addval={:08X}, type={:08X}", id, addval, - type); + LOG_WARNING(Service_VI, "(STUBBED) called id={}, addval={:08X}, type={:08X}", id, addval, + type); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -562,7 +562,7 @@ private: // TODO(Subv): Find out what this actually is. - NGLOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown); + LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(buffer_queue->GetNativeHandle()); @@ -625,7 +625,7 @@ public: private: void SetLayerZ(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_VI, "(STUBBED) called"); + LOG_WARNING(Service_VI, "(STUBBED) called"); IPC::RequestParser rp{ctx}; u64 layer_id = rp.Pop<u64>(); u64 z_value = rp.Pop<u64>(); @@ -640,8 +640,8 @@ private: bool visibility = rp.Pop<bool>(); IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:08X}, visibility={}", layer_id, - visibility); + LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:08X}, visibility={}", layer_id, + visibility); } }; @@ -723,7 +723,7 @@ public: private: void CloseDisplay(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_VI, "(STUBBED) called"); + LOG_WARNING(Service_VI, "(STUBBED) called"); IPC::RequestParser rp{ctx}; u64 display = rp.Pop<u64>(); @@ -732,7 +732,7 @@ private: } void CreateManagedLayer(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_VI, "(STUBBED) called"); + LOG_WARNING(Service_VI, "(STUBBED) called"); IPC::RequestParser rp{ctx}; u32 unknown = rp.Pop<u32>(); rp.Skip(1, false); @@ -747,7 +747,7 @@ private: } void AddToLayerStack(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_VI, "(STUBBED) called"); + LOG_WARNING(Service_VI, "(STUBBED) called"); IPC::RequestParser rp{ctx}; u32 stack = rp.Pop<u32>(); u64 layer_id = rp.Pop<u64>(); @@ -762,8 +762,8 @@ private: bool visibility = rp.Pop<bool>(); IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); rb.Push(RESULT_SUCCESS); - NGLOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:X}, visibility={}", layer_id, - visibility); + LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:X}, visibility={}", layer_id, + visibility); } std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; @@ -776,7 +776,7 @@ public: private: void GetRelayService(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_VI, "(STUBBED) called"); + LOG_WARNING(Service_VI, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); @@ -784,7 +784,7 @@ private: } void GetSystemDisplayService(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_VI, "(STUBBED) called"); + LOG_WARNING(Service_VI, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); @@ -792,7 +792,7 @@ private: } void GetManagerDisplayService(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_VI, "(STUBBED) called"); + LOG_WARNING(Service_VI, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); @@ -800,7 +800,7 @@ private: } void GetIndirectDisplayTransactionService(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_VI, "(STUBBED) called"); + LOG_WARNING(Service_VI, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); @@ -808,7 +808,7 @@ private: } void OpenDisplay(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_VI, "(STUBBED) called"); + LOG_WARNING(Service_VI, "(STUBBED) called"); IPC::RequestParser rp{ctx}; auto name_buf = rp.PopRaw<std::array<u8, 0x40>>(); auto end = std::find(name_buf.begin(), name_buf.end(), '\0'); @@ -823,7 +823,7 @@ private: } void CloseDisplay(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_VI, "(STUBBED) called"); + LOG_WARNING(Service_VI, "(STUBBED) called"); IPC::RequestParser rp{ctx}; u64 display_id = rp.Pop<u64>(); @@ -832,7 +832,7 @@ private: } void GetDisplayResolution(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_VI, "(STUBBED) called"); + LOG_WARNING(Service_VI, "(STUBBED) called"); IPC::RequestParser rp{ctx}; u64 display_id = rp.Pop<u64>(); @@ -849,7 +849,7 @@ private: } void SetLayerScalingMode(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_VI, "(STUBBED) called"); + LOG_WARNING(Service_VI, "(STUBBED) called"); IPC::RequestParser rp{ctx}; u32 scaling_mode = rp.Pop<u32>(); u64 unknown = rp.Pop<u64>(); @@ -865,11 +865,11 @@ private: IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0); rb.Push(RESULT_SUCCESS); rb.Push<u64>(1); - NGLOG_WARNING(Service_VI, "(STUBBED) called"); + LOG_WARNING(Service_VI, "(STUBBED) called"); } void OpenLayer(Kernel::HLERequestContext& ctx) { - NGLOG_DEBUG(Service_VI, "called"); + LOG_DEBUG(Service_VI, "called"); IPC::RequestParser rp{ctx}; auto name_buf = rp.PopRaw<std::array<u8, 0x40>>(); auto end = std::find(name_buf.begin(), name_buf.end(), '\0'); @@ -889,7 +889,7 @@ private: } void CreateStrayLayer(Kernel::HLERequestContext& ctx) { - NGLOG_DEBUG(Service_VI, "called"); + LOG_DEBUG(Service_VI, "called"); IPC::RequestParser rp{ctx}; u32 flags = rp.Pop<u32>(); @@ -909,7 +909,7 @@ private: } void DestroyStrayLayer(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_VI, "(STUBBED) called"); + LOG_WARNING(Service_VI, "(STUBBED) called"); IPC::RequestParser rp{ctx}; u64 layer_id = rp.Pop<u64>(); @@ -919,7 +919,7 @@ private: } void GetDisplayVsyncEvent(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_VI, "(STUBBED) called"); + LOG_WARNING(Service_VI, "(STUBBED) called"); IPC::RequestParser rp{ctx}; u64 display_id = rp.Pop<u64>(); @@ -968,7 +968,7 @@ Module::Interface::Interface(std::shared_ptr<Module> module, const char* name, : ServiceFramework(name), module(std::move(module)), nv_flinger(std::move(nv_flinger)) {} void Module::Interface::GetDisplayService(Kernel::HLERequestContext& ctx) { - NGLOG_WARNING(Service_VI, "(STUBBED) called"); + LOG_WARNING(Service_VI, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); diff --git a/src/core/hw/hw.cpp b/src/core/hw/hw.cpp index 8fc91dc9ca..2f48068c1e 100644 --- a/src/core/hw/hw.cpp +++ b/src/core/hw/hw.cpp @@ -33,7 +33,7 @@ inline void Read(T& var, const u32 addr) { LCD::Read(var, addr); break; default: - NGLOG_ERROR(HW_Memory, "Unknown Read{} @ 0x{:08X}", sizeof(var) * 8, addr); + LOG_ERROR(HW_Memory, "Unknown Read{} @ 0x{:08X}", sizeof(var) * 8, addr); break; } } @@ -62,7 +62,7 @@ inline void Write(u32 addr, const T data) { LCD::Write(addr, data); break; default: - NGLOG_ERROR(HW_Memory, "Unknown Write{} 0x{:08X} @ 0x{:08X}", sizeof(data) * 8, data, addr); + LOG_ERROR(HW_Memory, "Unknown Write{} 0x{:08X} @ 0x{:08X}", sizeof(data) * 8, data, addr); break; } } @@ -85,12 +85,12 @@ void Update() {} /// Initialize hardware void Init() { LCD::Init(); - NGLOG_DEBUG(HW, "Initialized OK"); + LOG_DEBUG(HW, "Initialized OK"); } /// Shutdown hardware void Shutdown() { LCD::Shutdown(); - NGLOG_DEBUG(HW, "Shutdown OK"); + LOG_DEBUG(HW, "Shutdown OK"); } } // namespace HW diff --git a/src/core/hw/lcd.cpp b/src/core/hw/lcd.cpp index e8525efdec..0b62174d58 100644 --- a/src/core/hw/lcd.cpp +++ b/src/core/hw/lcd.cpp @@ -20,7 +20,7 @@ inline void Read(T& var, const u32 raw_addr) { // Reads other than u32 are untested, so I'd rather have them abort than silently fail if (index >= 0x400 || !std::is_same<T, u32>::value) { - NGLOG_ERROR(HW_LCD, "Unknown Read{} @ 0x{:08X}", sizeof(var) * 8, addr); + LOG_ERROR(HW_LCD, "Unknown Read{} @ 0x{:08X}", sizeof(var) * 8, addr); return; } @@ -34,7 +34,7 @@ inline void Write(u32 addr, const T data) { // Writes other than u32 are untested, so I'd rather have them abort than silently fail if (index >= 0x400 || !std::is_same<T, u32>::value) { - NGLOG_ERROR(HW_LCD, "Unknown Write{} 0x{:08X} @ 0x{:08X}", sizeof(data) * 8, data, addr); + LOG_ERROR(HW_LCD, "Unknown Write{} 0x{:08X} @ 0x{:08X}", sizeof(data) * 8, data, addr); return; } @@ -56,12 +56,12 @@ template void Write<u8>(u32 addr, const u8 data); /// Initialize hardware void Init() { memset(&g_regs, 0, sizeof(g_regs)); - NGLOG_DEBUG(HW_LCD, "Initialized OK"); + LOG_DEBUG(HW_LCD, "Initialized OK"); } /// Shutdown hardware void Shutdown() { - NGLOG_DEBUG(HW_LCD, "Shutdown OK"); + LOG_DEBUG(HW_LCD, "Shutdown OK"); } } // namespace LCD diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index b01b2caf63..eb7feb617e 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -132,7 +132,7 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( const VAddr load_addr = next_load_addr; next_load_addr = AppLoader_NSO::LoadModule(path, load_addr); if (next_load_addr) { - NGLOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); + LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); } else { next_load_addr = load_addr; } @@ -163,7 +163,7 @@ ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS( std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, u64& size) { if (filepath_romfs.empty()) { - NGLOG_DEBUG(Loader, "No RomFS available"); + LOG_DEBUG(Loader, "No RomFS available"); return ResultStatus::ErrorNotUsed; } @@ -176,8 +176,8 @@ ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS( offset = 0; size = romfs_file->GetSize(); - NGLOG_DEBUG(Loader, "RomFS offset: 0x{:016X}", offset); - NGLOG_DEBUG(Loader, "RomFS size: 0x{:016X}", size); + LOG_DEBUG(Loader, "RomFS offset: 0x{:016X}", offset); + LOG_DEBUG(Loader, "RomFS size: 0x{:016X}", size); // Reset read pointer file.Seek(0, SEEK_SET); diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index e42d3a8703..b69e5c6efb 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp @@ -273,18 +273,18 @@ const char* ElfReader::GetSectionName(int section) const { } SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) { - NGLOG_DEBUG(Loader, "String section: {}", header->e_shstrndx); + LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx); // Should we relocate? relocate = (header->e_type != ET_EXEC); if (relocate) { - NGLOG_DEBUG(Loader, "Relocatable module"); + LOG_DEBUG(Loader, "Relocatable module"); entryPoint += vaddr; } else { - NGLOG_DEBUG(Loader, "Prerelocated executable"); + LOG_DEBUG(Loader, "Prerelocated executable"); } - NGLOG_DEBUG(Loader, "{} segments:", header->e_phnum); + LOG_DEBUG(Loader, "{} segments:", header->e_phnum); // First pass : Get the bits into RAM u32 base_addr = relocate ? vaddr : 0; @@ -304,8 +304,8 @@ SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) { for (unsigned int i = 0; i < header->e_phnum; ++i) { Elf32_Phdr* p = &segments[i]; - NGLOG_DEBUG(Loader, "Type: {} Vaddr: {:08X} Filesz: {:08X} Memsz: {:08X} ", p->p_type, - p->p_vaddr, p->p_filesz, p->p_memsz); + LOG_DEBUG(Loader, "Type: {} Vaddr: {:08X} Filesz: {:08X} Memsz: {:08X} ", p->p_type, + p->p_vaddr, p->p_filesz, p->p_memsz); if (p->p_type == PT_LOAD) { CodeSet::Segment* codeset_segment; @@ -317,16 +317,16 @@ SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) { } else if (permission_flags == (PF_R | PF_W)) { codeset_segment = &codeset->data; } else { - NGLOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i, - p->p_flags); + LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i, + p->p_flags); continue; } if (codeset_segment->size != 0) { - NGLOG_ERROR(Loader, - "ELF has more than one segment of the same type. Skipping extra " - "segment (id {})", - i); + LOG_ERROR(Loader, + "ELF has more than one segment of the same type. Skipping extra " + "segment (id {})", + i); continue; } @@ -345,7 +345,7 @@ SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) { codeset->entrypoint = base_addr + header->e_entry; codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); - NGLOG_DEBUG(Loader, "Done loading."); + LOG_DEBUG(Loader, "Done loading."); return codeset; } diff --git a/src/core/loader/linker.cpp b/src/core/loader/linker.cpp index c7be5f2657..769516b6f3 100644 --- a/src/core/loader/linker.cpp +++ b/src/core/loader/linker.cpp @@ -84,7 +84,7 @@ void Linker::WriteRelocations(std::vector<u8>& program_image, const std::vector< } break; default: - NGLOG_CRITICAL(Loader, "Unknown relocation type: {}", static_cast<int>(rela.type)); + LOG_CRITICAL(Loader, "Unknown relocation type: {}", static_cast<int>(rela.type)); break; } } @@ -141,7 +141,7 @@ void Linker::ResolveImports() { if (search != exports.end()) { Memory::Write64(import.second.ea, search->second + import.second.addend); } else { - NGLOG_ERROR(Loader, "Unresolved import: {}", import.first); + LOG_ERROR(Loader, "Unresolved import: {}", import.first); } } } diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 6a4fd38cbe..8831d8e832 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -9,6 +9,7 @@ #include "core/hle/kernel/process.h" #include "core/loader/deconstructed_rom_directory.h" #include "core/loader/elf.h" +#include "core/loader/nca.h" #include "core/loader/nro.h" #include "core/loader/nso.h" @@ -32,6 +33,7 @@ FileType IdentifyFile(FileUtil::IOFile& file, const std::string& filepath) { CHECK_TYPE(ELF) CHECK_TYPE(NSO) CHECK_TYPE(NRO) + CHECK_TYPE(NCA) #undef CHECK_TYPE @@ -41,7 +43,7 @@ FileType IdentifyFile(FileUtil::IOFile& file, const std::string& filepath) { FileType IdentifyFile(const std::string& file_name) { FileUtil::IOFile file(file_name, "rb"); if (!file.IsOpen()) { - NGLOG_ERROR(Loader, "Failed to load file {}", file_name); + LOG_ERROR(Loader, "Failed to load file {}", file_name); return FileType::Unknown; } @@ -57,6 +59,8 @@ FileType GuessFromExtension(const std::string& extension_) { return FileType::NRO; else if (extension == ".nso") return FileType::NSO; + else if (extension == ".nca") + return FileType::NCA; return FileType::Unknown; } @@ -69,6 +73,8 @@ const char* GetFileTypeString(FileType type) { return "NRO"; case FileType::NSO: return "NSO"; + case FileType::NCA: + return "NCA"; case FileType::DeconstructedRomDirectory: return "Directory"; case FileType::Error: @@ -104,6 +110,10 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileUtil::IOFile&& file, FileTyp case FileType::NRO: return std::make_unique<AppLoader_NRO>(std::move(file), filepath); + // NX NCA file format. + case FileType::NCA: + return std::make_unique<AppLoader_NCA>(std::move(file), filepath); + // NX deconstructed ROM directory. case FileType::DeconstructedRomDirectory: return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file), filepath); @@ -116,7 +126,7 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileUtil::IOFile&& file, FileTyp std::unique_ptr<AppLoader> GetLoader(const std::string& filename) { FileUtil::IOFile file(filename, "rb"); if (!file.IsOpen()) { - NGLOG_ERROR(Loader, "Failed to load file {}", filename); + LOG_ERROR(Loader, "Failed to load file {}", filename); return nullptr; } @@ -127,12 +137,12 @@ std::unique_ptr<AppLoader> GetLoader(const std::string& filename) { FileType filename_type = GuessFromExtension(filename_extension); if (type != filename_type) { - NGLOG_WARNING(Loader, "File {} has a different type than its extension.", filename); + LOG_WARNING(Loader, "File {} has a different type than its extension.", filename); if (FileType::Unknown == type) type = filename_type; } - NGLOG_DEBUG(Loader, "Loading file {} as {}...", filename, GetFileTypeString(type)); + LOG_DEBUG(Loader, "Loading file {} as {}...", filename, GetFileTypeString(type)); return GetFileLoader(std::move(file), type, filename_filename, filename); } diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index b1aabb1cb4..b76f7b13d0 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -29,6 +29,7 @@ enum class FileType { ELF, NSO, NRO, + NCA, DeconstructedRomDirectory, }; diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp new file mode 100644 index 0000000000..da064f8e39 --- /dev/null +++ b/src/core/loader/nca.cpp @@ -0,0 +1,303 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <vector> + +#include "common/common_funcs.h" +#include "common/file_util.h" +#include "common/logging/log.h" +#include "common/swap.h" +#include "core/core.h" +#include "core/file_sys/program_metadata.h" +#include "core/file_sys/romfs_factory.h" +#include "core/hle/kernel/process.h" +#include "core/hle/kernel/resource_limit.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/loader/nca.h" +#include "core/loader/nso.h" +#include "core/memory.h" + +namespace Loader { + +// Media offsets in headers are stored divided by 512. Mult. by this to get real offset. +constexpr u64 MEDIA_OFFSET_MULTIPLIER = 0x200; + +constexpr u64 SECTION_HEADER_SIZE = 0x200; +constexpr u64 SECTION_HEADER_OFFSET = 0x400; + +enum class NcaContentType : u8 { Program = 0, Meta = 1, Control = 2, Manual = 3, Data = 4 }; + +enum class NcaSectionFilesystemType : u8 { PFS0 = 0x2, ROMFS = 0x3 }; + +struct NcaSectionTableEntry { + u32_le media_offset; + u32_le media_end_offset; + INSERT_PADDING_BYTES(0x8); +}; +static_assert(sizeof(NcaSectionTableEntry) == 0x10, "NcaSectionTableEntry has incorrect size."); + +struct NcaHeader { + std::array<u8, 0x100> rsa_signature_1; + std::array<u8, 0x100> rsa_signature_2; + u32_le magic; + u8 is_system; + NcaContentType content_type; + u8 crypto_type; + u8 key_index; + u64_le size; + u64_le title_id; + INSERT_PADDING_BYTES(0x4); + u32_le sdk_version; + u8 crypto_type_2; + INSERT_PADDING_BYTES(15); + std::array<u8, 0x10> rights_id; + std::array<NcaSectionTableEntry, 0x4> section_tables; + std::array<std::array<u8, 0x20>, 0x4> hash_tables; + std::array<std::array<u8, 0x10>, 0x4> key_area; + INSERT_PADDING_BYTES(0xC0); +}; +static_assert(sizeof(NcaHeader) == 0x400, "NcaHeader has incorrect size."); + +struct NcaSectionHeaderBlock { + INSERT_PADDING_BYTES(3); + NcaSectionFilesystemType filesystem_type; + u8 crypto_type; + INSERT_PADDING_BYTES(3); +}; +static_assert(sizeof(NcaSectionHeaderBlock) == 0x8, "NcaSectionHeaderBlock has incorrect size."); + +struct Pfs0Superblock { + NcaSectionHeaderBlock header_block; + std::array<u8, 0x20> hash; + u32_le size; + INSERT_PADDING_BYTES(4); + u64_le hash_table_offset; + u64_le hash_table_size; + u64_le pfs0_header_offset; + u64_le pfs0_size; + INSERT_PADDING_BYTES(432); +}; +static_assert(sizeof(Pfs0Superblock) == 0x200, "Pfs0Superblock has incorrect size."); + +static bool IsValidNca(const NcaHeader& header) { + return header.magic == Common::MakeMagic('N', 'C', 'A', '2') || + header.magic == Common::MakeMagic('N', 'C', 'A', '3'); +} + +// TODO(DarkLordZach): Add support for encrypted. +class Nca final { + std::vector<FileSys::PartitionFilesystem> pfs; + std::vector<u64> pfs_offset; + + u64 romfs_offset = 0; + u64 romfs_size = 0; + + boost::optional<u8> exefs_id = boost::none; + + FileUtil::IOFile file; + std::string path; + + u64 GetExeFsFileOffset(const std::string& file_name) const; + u64 GetExeFsFileSize(const std::string& file_name) const; + +public: + ResultStatus Load(FileUtil::IOFile&& file, std::string path); + + FileSys::PartitionFilesystem GetPfs(u8 id) const; + + u64 GetRomFsOffset() const; + u64 GetRomFsSize() const; + + std::vector<u8> GetExeFsFile(const std::string& file_name); +}; + +static bool IsPfsExeFs(const FileSys::PartitionFilesystem& pfs) { + // According to switchbrew, an exefs must only contain these two files: + return pfs.GetFileSize("main") > 0 && pfs.GetFileSize("main.npdm") > 0; +} + +ResultStatus Nca::Load(FileUtil::IOFile&& in_file, std::string in_path) { + file = std::move(in_file); + path = in_path; + file.Seek(0, SEEK_SET); + std::array<u8, sizeof(NcaHeader)> header_array{}; + if (sizeof(NcaHeader) != file.ReadBytes(header_array.data(), sizeof(NcaHeader))) + LOG_CRITICAL(Loader, "File reader errored out during header read."); + + NcaHeader header{}; + std::memcpy(&header, header_array.data(), sizeof(NcaHeader)); + if (!IsValidNca(header)) + return ResultStatus::ErrorInvalidFormat; + + int number_sections = + std::count_if(std::begin(header.section_tables), std::end(header.section_tables), + [](NcaSectionTableEntry entry) { return entry.media_offset > 0; }); + + for (int i = 0; i < number_sections; ++i) { + // Seek to beginning of this section. + file.Seek(SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE, SEEK_SET); + std::array<u8, sizeof(NcaSectionHeaderBlock)> array{}; + if (sizeof(NcaSectionHeaderBlock) != + file.ReadBytes(array.data(), sizeof(NcaSectionHeaderBlock))) + LOG_CRITICAL(Loader, "File reader errored out during header read."); + + NcaSectionHeaderBlock block{}; + std::memcpy(&block, array.data(), sizeof(NcaSectionHeaderBlock)); + + if (block.filesystem_type == NcaSectionFilesystemType::ROMFS) { + romfs_offset = header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER; + romfs_size = + header.section_tables[i].media_end_offset * MEDIA_OFFSET_MULTIPLIER - romfs_offset; + } else if (block.filesystem_type == NcaSectionFilesystemType::PFS0) { + Pfs0Superblock sb{}; + // Seek back to beginning of this section. + file.Seek(SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE, SEEK_SET); + if (sizeof(Pfs0Superblock) != file.ReadBytes(&sb, sizeof(Pfs0Superblock))) + LOG_CRITICAL(Loader, "File reader errored out during header read."); + + u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) * + MEDIA_OFFSET_MULTIPLIER) + + sb.pfs0_header_offset; + FileSys::PartitionFilesystem npfs{}; + ResultStatus status = npfs.Load(path, offset); + + if (status == ResultStatus::Success) { + pfs.emplace_back(std::move(npfs)); + pfs_offset.emplace_back(offset); + } + } + } + + for (size_t i = 0; i < pfs.size(); ++i) { + if (IsPfsExeFs(pfs[i])) + exefs_id = i; + } + + return ResultStatus::Success; +} + +FileSys::PartitionFilesystem Nca::GetPfs(u8 id) const { + return pfs[id]; +} + +u64 Nca::GetExeFsFileOffset(const std::string& file_name) const { + if (exefs_id == boost::none) + return 0; + return pfs[*exefs_id].GetFileOffset(file_name) + pfs_offset[*exefs_id]; +} + +u64 Nca::GetExeFsFileSize(const std::string& file_name) const { + if (exefs_id == boost::none) + return 0; + return pfs[*exefs_id].GetFileSize(file_name); +} + +u64 Nca::GetRomFsOffset() const { + return romfs_offset; +} + +u64 Nca::GetRomFsSize() const { + return romfs_size; +} + +std::vector<u8> Nca::GetExeFsFile(const std::string& file_name) { + std::vector<u8> out(GetExeFsFileSize(file_name)); + file.Seek(GetExeFsFileOffset(file_name), SEEK_SET); + file.ReadBytes(out.data(), GetExeFsFileSize(file_name)); + return out; +} + +AppLoader_NCA::AppLoader_NCA(FileUtil::IOFile&& file, std::string filepath) + : AppLoader(std::move(file)), filepath(std::move(filepath)) {} + +FileType AppLoader_NCA::IdentifyType(FileUtil::IOFile& file, const std::string&) { + file.Seek(0, SEEK_SET); + std::array<u8, 0x400> header_enc_array{}; + if (0x400 != file.ReadBytes(header_enc_array.data(), 0x400)) + return FileType::Error; + + // TODO(DarkLordZach): Assuming everything is decrypted. Add crypto support. + NcaHeader header{}; + std::memcpy(&header, header_enc_array.data(), sizeof(NcaHeader)); + + if (IsValidNca(header) && header.content_type == NcaContentType::Program) + return FileType::NCA; + + return FileType::Error; +} + +ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) { + if (is_loaded) { + return ResultStatus::ErrorAlreadyLoaded; + } + if (!file.IsOpen()) { + return ResultStatus::Error; + } + + nca = std::make_unique<Nca>(); + ResultStatus result = nca->Load(std::move(file), filepath); + if (result != ResultStatus::Success) { + return result; + } + + result = metadata.Load(nca->GetExeFsFile("main.npdm")); + if (result != ResultStatus::Success) { + return result; + } + metadata.Print(); + + const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()}; + if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit) { + return ResultStatus::ErrorUnsupportedArch; + } + + VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR}; + for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", + "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { + const VAddr load_addr = next_load_addr; + next_load_addr = AppLoader_NSO::LoadModule(module, nca->GetExeFsFile(module), load_addr); + if (next_load_addr) { + LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); + } else { + next_load_addr = load_addr; + } + } + + process->program_id = metadata.GetTitleID(); + process->svc_access_mask.set(); + process->address_mappings = default_address_mappings; + process->resource_limit = + Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); + process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(), + metadata.GetMainThreadStackSize()); + + if (nca->GetRomFsSize() > 0) + Service::FileSystem::RegisterFileSystem(std::make_unique<FileSys::RomFS_Factory>(*this), + Service::FileSystem::Type::RomFS); + + is_loaded = true; + return ResultStatus::Success; +} + +ResultStatus AppLoader_NCA::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, + u64& size) { + if (nca->GetRomFsSize() == 0) { + LOG_DEBUG(Loader, "No RomFS available"); + return ResultStatus::ErrorNotUsed; + } + + romfs_file = std::make_shared<FileUtil::IOFile>(filepath, "rb"); + + offset = nca->GetRomFsOffset(); + size = nca->GetRomFsSize(); + + LOG_DEBUG(Loader, "RomFS offset: 0x{:016X}", offset); + LOG_DEBUG(Loader, "RomFS size: 0x{:016X}", size); + + return ResultStatus::Success; +} + +AppLoader_NCA::~AppLoader_NCA() = default; + +} // namespace Loader diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h new file mode 100644 index 0000000000..3b6c451d02 --- /dev/null +++ b/src/core/loader/nca.h @@ -0,0 +1,49 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <string> +#include "common/common_types.h" +#include "core/file_sys/partition_filesystem.h" +#include "core/file_sys/program_metadata.h" +#include "core/hle/kernel/kernel.h" +#include "core/loader/loader.h" + +namespace Loader { + +class Nca; + +/// Loads an NCA file +class AppLoader_NCA final : public AppLoader { +public: + AppLoader_NCA(FileUtil::IOFile&& file, std::string filepath); + + /** + * Returns the type of the file + * @param file FileUtil::IOFile open file + * @param filepath Path of the file that we are opening. + * @return FileType found, or FileType::Error if this loader doesn't know it + */ + static FileType IdentifyType(FileUtil::IOFile& file, const std::string& filepath); + + FileType GetFileType() override { + return IdentifyType(file, filepath); + } + + ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; + + ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, + u64& size) override; + + ~AppLoader_NCA(); + +private: + std::string filepath; + FileSys::ProgramMetadata metadata; + + std::unique_ptr<Nca> nca; +}; + +} // namespace Loader diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 01be9e2170..7f84e4b1be 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -66,22 +66,13 @@ FileType AppLoader_NSO::IdentifyType(FileUtil::IOFile& file, const std::string&) return FileType::Error; } -static std::vector<u8> ReadSegment(FileUtil::IOFile& file, const NsoSegmentHeader& header, - int compressed_size) { - std::vector<u8> compressed_data; - compressed_data.resize(compressed_size); - - file.Seek(header.offset, SEEK_SET); - if (compressed_size != file.ReadBytes(compressed_data.data(), compressed_size)) { - NGLOG_CRITICAL(Loader, "Failed to read {} NSO LZ4 compressed bytes", compressed_size); - return {}; - } - +static std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data, + const NsoSegmentHeader& header) { std::vector<u8> uncompressed_data; uncompressed_data.resize(header.size); const int bytes_uncompressed = LZ4_decompress_safe( reinterpret_cast<const char*>(compressed_data.data()), - reinterpret_cast<char*>(uncompressed_data.data()), compressed_size, header.size); + reinterpret_cast<char*>(uncompressed_data.data()), compressed_data.size(), header.size); ASSERT_MSG(bytes_uncompressed == header.size && bytes_uncompressed == uncompressed_data.size(), "{} != {} != {}", bytes_uncompressed, header.size, uncompressed_data.size()); @@ -89,10 +80,76 @@ static std::vector<u8> ReadSegment(FileUtil::IOFile& file, const NsoSegmentHeade return uncompressed_data; } +static std::vector<u8> ReadSegment(FileUtil::IOFile& file, const NsoSegmentHeader& header, + size_t compressed_size) { + std::vector<u8> compressed_data; + compressed_data.resize(compressed_size); + + file.Seek(header.offset, SEEK_SET); + if (compressed_size != file.ReadBytes(compressed_data.data(), compressed_size)) { + LOG_CRITICAL(Loader, "Failed to read {} NSO LZ4 compressed bytes", compressed_size); + return {}; + } + + return DecompressSegment(compressed_data, header); +} + static constexpr u32 PageAlignSize(u32 size) { return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; } +VAddr AppLoader_NSO::LoadModule(const std::string& name, const std::vector<u8>& file_data, + VAddr load_base) { + if (file_data.size() < sizeof(NsoHeader)) + return {}; + + NsoHeader nso_header; + std::memcpy(&nso_header, file_data.data(), sizeof(NsoHeader)); + + if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) + return {}; + + // Build program image + Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(""); + std::vector<u8> program_image; + for (int i = 0; i < nso_header.segments.size(); ++i) { + std::vector<u8> compressed_data(nso_header.segments_compressed_size[i]); + for (int j = 0; j < nso_header.segments_compressed_size[i]; ++j) + compressed_data[j] = file_data[nso_header.segments[i].offset + j]; + std::vector<u8> data = DecompressSegment(compressed_data, nso_header.segments[i]); + program_image.resize(nso_header.segments[i].location); + program_image.insert(program_image.end(), data.begin(), data.end()); + codeset->segments[i].addr = nso_header.segments[i].location; + codeset->segments[i].offset = nso_header.segments[i].location; + codeset->segments[i].size = PageAlignSize(static_cast<u32>(data.size())); + } + + // MOD header pointer is at .text offset + 4 + u32 module_offset; + std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32)); + + // Read MOD header + ModHeader mod_header{}; + // Default .bss to size in segment header if MOD0 section doesn't exist + u32 bss_size{PageAlignSize(nso_header.segments[2].bss_size)}; + std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(ModHeader)); + const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')}; + if (has_mod_header) { + // Resize program image to include .bss section and page align each section + bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); + } + codeset->data.size += bss_size; + const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)}; + program_image.resize(image_size); + + // Load codeset for current process + codeset->name = name; + codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); + Core::CurrentProcess()->LoadModule(codeset, load_base); + + return load_base + image_size; +} + VAddr AppLoader_NSO::LoadModule(const std::string& path, VAddr load_base) { FileUtil::IOFile file(path, "rb"); if (!file.IsOpen()) { @@ -158,7 +215,7 @@ ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) { // Load module LoadModule(filepath, Memory::PROCESS_IMAGE_VADDR); - NGLOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", filepath, Memory::PROCESS_IMAGE_VADDR); + LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", filepath, Memory::PROCESS_IMAGE_VADDR); process->svc_access_mask.set(); process->address_mappings = default_address_mappings; diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h index 1ae30a824d..386f4d39a4 100644 --- a/src/core/loader/nso.h +++ b/src/core/loader/nso.h @@ -29,6 +29,9 @@ public: return IdentifyType(file, filepath); } + static VAddr LoadModule(const std::string& name, const std::vector<u8>& file_data, + VAddr load_base); + static VAddr LoadModule(const std::string& path, VAddr load_base); ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 3b81acd631..190ccc25c0 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -43,8 +43,8 @@ PageTable* GetCurrentPageTable() { } static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, PageType type) { - NGLOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE, - (base + size) * PAGE_SIZE); + LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE, + (base + size) * PAGE_SIZE); RasterizerFlushVirtualRegion(base << PAGE_BITS, size * PAGE_SIZE, FlushMode::FlushAndInvalidate); @@ -173,7 +173,7 @@ T Read(const VAddr vaddr) { PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; switch (type) { case PageType::Unmapped: - NGLOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr); + LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr); return 0; case PageType::Memory: ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); @@ -205,8 +205,8 @@ void Write(const VAddr vaddr, const T data) { PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; switch (type) { case PageType::Unmapped: - NGLOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, - static_cast<u32>(data), vaddr); + LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, + static_cast<u32>(data), vaddr); return; case PageType::Memory: ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); @@ -241,6 +241,10 @@ bool IsValidVirtualAddress(const VAddr vaddr) { return IsValidVirtualAddress(*Core::CurrentProcess(), vaddr); } +bool IsKernelVirtualAddress(const VAddr vaddr) { + return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END; +} + bool IsValidPhysicalAddress(const PAddr paddr) { return GetPhysicalPointer(paddr) != nullptr; } @@ -255,7 +259,7 @@ u8* GetPointer(const VAddr vaddr) { return GetPointerFromVMA(vaddr); } - NGLOG_ERROR(HW_Memory, "Unknown GetPointer @ 0x{:016X}", vaddr); + LOG_ERROR(HW_Memory, "Unknown GetPointer @ 0x{:016X}", vaddr); return nullptr; } @@ -292,12 +296,12 @@ u8* GetPhysicalPointer(PAddr address) { }); if (area == std::end(memory_areas)) { - NGLOG_ERROR(HW_Memory, "Unknown GetPhysicalPointer @ 0x{:016X}", address); + LOG_ERROR(HW_Memory, "Unknown GetPhysicalPointer @ 0x{:016X}", address); return nullptr; } if (area->paddr_base == IO_AREA_PADDR) { - NGLOG_ERROR(HW_Memory, "MMIO mappings are not supported yet. phys_addr={:016X}", address); + LOG_ERROR(HW_Memory, "MMIO mappings are not supported yet. phys_addr={:016X}", address); return nullptr; } @@ -344,9 +348,9 @@ void RasterizerMarkRegionCached(Tegra::GPUVAddr gpu_addr, u64 size, bool cached) Core::System::GetInstance().GPU().memory_manager->GpuToCpuAddress(gpu_addr); // The GPU <-> CPU virtual memory mapping is not 1:1 if (!maybe_vaddr) { - NGLOG_ERROR(HW_Memory, - "Trying to flush a cached region to an invalid physical address {:016X}", - gpu_addr); + LOG_ERROR(HW_Memory, + "Trying to flush a cached region to an invalid physical address {:016X}", + gpu_addr); continue; } VAddr vaddr = *maybe_vaddr; @@ -480,9 +484,9 @@ void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_ switch (page_table.attributes[page_index]) { case PageType::Unmapped: { - NGLOG_ERROR(HW_Memory, - "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", - current_vaddr, src_addr, size); + LOG_ERROR(HW_Memory, + "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", + current_vaddr, src_addr, size); std::memset(dest_buffer, 0, copy_amount); break; } @@ -544,9 +548,9 @@ void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const voi switch (page_table.attributes[page_index]) { case PageType::Unmapped: { - NGLOG_ERROR(HW_Memory, - "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", - current_vaddr, dest_addr, size); + LOG_ERROR(HW_Memory, + "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", + current_vaddr, dest_addr, size); break; } case PageType::Memory: { @@ -592,9 +596,9 @@ void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const size switch (page_table.attributes[page_index]) { case PageType::Unmapped: { - NGLOG_ERROR(HW_Memory, - "Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", - current_vaddr, dest_addr, size); + LOG_ERROR(HW_Memory, + "Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", + current_vaddr, dest_addr, size); break; } case PageType::Memory: { @@ -633,9 +637,9 @@ void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr, switch (page_table.attributes[page_index]) { case PageType::Unmapped: { - NGLOG_ERROR(HW_Memory, - "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", - current_vaddr, src_addr, size); + LOG_ERROR(HW_Memory, + "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", + current_vaddr, src_addr, size); ZeroBlock(process, dest_addr, copy_amount); break; } @@ -688,7 +692,7 @@ boost::optional<PAddr> TryVirtualToPhysicalAddress(const VAddr addr) { PAddr VirtualToPhysicalAddress(const VAddr addr) { auto paddr = TryVirtualToPhysicalAddress(addr); if (!paddr) { - NGLOG_ERROR(HW_Memory, "Unknown virtual address @ 0x{:016X}", addr); + LOG_ERROR(HW_Memory, "Unknown virtual address @ 0x{:016X}", addr); // To help with debugging, set bit on address so that it's obviously invalid. return addr | 0x80000000; } diff --git a/src/core/memory.h b/src/core/memory.h index 3f56a2c6a6..8d5d017a46 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -188,6 +188,11 @@ enum : VAddr { MAP_REGION_VADDR = NEW_MAP_REGION_VADDR_END, MAP_REGION_SIZE = 0x1000000000, MAP_REGION_VADDR_END = MAP_REGION_VADDR + MAP_REGION_SIZE, + + /// Kernel Virtual Address Range + KERNEL_REGION_VADDR = 0xFFFFFF8000000000, + KERNEL_REGION_SIZE = 0x7FFFE00000, + KERNEL_REGION_END = KERNEL_REGION_VADDR + KERNEL_REGION_SIZE, }; /// Currently active page table @@ -197,6 +202,8 @@ PageTable* GetCurrentPageTable(); /// Determines if the given VAddr is valid for the specified process. bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr); bool IsValidVirtualAddress(const VAddr addr); +/// Determines if the given VAddr is a kernel address +bool IsKernelVirtualAddress(const VAddr addr); bool IsValidPhysicalAddress(const PAddr addr); diff --git a/src/core/settings.h b/src/core/settings.h index a7f1e5fa0d..7150d97555 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -129,6 +129,7 @@ struct Values { // Renderer float resolution_factor; bool toggle_framelimit; + bool use_accurate_framebuffers; float bg_red; float bg_green; diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index a60aa1143b..b9a603df32 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -42,14 +42,14 @@ u64 GetTelemetryId() { if (FileUtil::Exists(filename)) { FileUtil::IOFile file(filename, "rb"); if (!file.IsOpen()) { - NGLOG_ERROR(Core, "failed to open telemetry_id: {}", filename); + LOG_ERROR(Core, "failed to open telemetry_id: {}", filename); return {}; } file.ReadBytes(&telemetry_id, sizeof(u64)); } else { FileUtil::IOFile file(filename, "wb"); if (!file.IsOpen()) { - NGLOG_ERROR(Core, "failed to open telemetry_id: {}", filename); + LOG_ERROR(Core, "failed to open telemetry_id: {}", filename); return {}; } telemetry_id = GenerateTelemetryId(); @@ -65,7 +65,7 @@ u64 RegenerateTelemetryId() { FileUtil::IOFile file(filename, "wb"); if (!file.IsOpen()) { - NGLOG_ERROR(Core, "failed to open telemetry_id: {}", filename); + LOG_ERROR(Core, "failed to open telemetry_id: {}", filename); return {}; } file.WriteBytes(&new_telemetry_id, sizeof(u64)); @@ -161,6 +161,8 @@ TelemetrySession::TelemetrySession() { Settings::values.resolution_factor); AddField(Telemetry::FieldType::UserConfig, "Renderer_ToggleFramelimit", Settings::values.toggle_framelimit); + AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateFramebuffers", + Settings::values.use_accurate_framebuffers); AddField(Telemetry::FieldType::UserConfig, "System_UseDockedMode", Settings::values.use_docked_mode); } diff --git a/src/core/tracer/recorder.cpp b/src/core/tracer/recorder.cpp index 2f848c9940..af032f0c9b 100644 --- a/src/core/tracer/recorder.cpp +++ b/src/core/tracer/recorder.cpp @@ -159,7 +159,7 @@ void Recorder::Finish(const std::string& filename) { throw "Failed to write stream element"; } } catch (const char* str) { - NGLOG_ERROR(HW_GPU, "Writing CiTrace file failed: {}", str); + LOG_ERROR(HW_GPU, "Writing CiTrace file failed: {}", str); } } diff --git a/src/input_common/sdl/sdl.cpp b/src/input_common/sdl/sdl.cpp index 231a0f7afb..8d117c2d4f 100644 --- a/src/input_common/sdl/sdl.cpp +++ b/src/input_common/sdl/sdl.cpp @@ -32,7 +32,7 @@ public: explicit SDLJoystick(int joystick_index) : joystick{SDL_JoystickOpen(joystick_index), SDL_JoystickClose} { if (!joystick) { - NGLOG_ERROR(Input, "failed to open joystick {}", joystick_index); + LOG_ERROR(Input, "failed to open joystick {}", joystick_index); } } @@ -204,7 +204,7 @@ public: trigger_if_greater = false; } else { trigger_if_greater = true; - NGLOG_ERROR(Input, "Unknown direction '{}'", direction_name); + LOG_ERROR(Input, "Unknown direction '{}'", direction_name); } return std::make_unique<SDLAxisButton>(GetJoystick(joystick_index), axis, threshold, trigger_if_greater); @@ -235,7 +235,7 @@ public: void Init() { if (SDL_Init(SDL_INIT_JOYSTICK) < 0) { - NGLOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError()); + LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError()); } else { using namespace Input; RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>()); diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 2818103576..c6431e722c 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -9,6 +9,8 @@ add_library(video_core STATIC engines/maxwell_3d.h engines/maxwell_compute.cpp engines/maxwell_compute.h + engines/maxwell_dma.cpp + engines/maxwell_dma.h engines/shader_bytecode.h gpu.cpp gpu.h @@ -39,6 +41,8 @@ add_library(video_core STATIC renderer_opengl/maxwell_to_gl.h renderer_opengl/renderer_opengl.cpp renderer_opengl/renderer_opengl.h + textures/astc.cpp + textures/astc.h textures/decoders.cpp textures/decoders.h textures/texture.h diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp index d72d6f7600..31ea3adad2 100644 --- a/src/video_core/command_processor.cpp +++ b/src/video_core/command_processor.cpp @@ -16,6 +16,7 @@ #include "video_core/engines/fermi_2d.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/engines/maxwell_compute.h" +#include "video_core/engines/maxwell_dma.h" #include "video_core/gpu.h" #include "video_core/renderer_base.h" #include "video_core/video_core.h" @@ -28,21 +29,21 @@ enum class BufferMethods { }; void GPU::WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params) { - NGLOG_WARNING(HW_GPU, - "Processing method {:08X} on subchannel {} value " - "{:08X} remaining params {}", - method, subchannel, value, remaining_params); + LOG_WARNING(HW_GPU, + "Processing method {:08X} on subchannel {} value " + "{:08X} remaining params {}", + method, subchannel, value, remaining_params); if (method == static_cast<u32>(BufferMethods::BindObject)) { // Bind the current subchannel to the desired engine id. - NGLOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", subchannel, value); + LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", subchannel, value); bound_engines[subchannel] = static_cast<EngineID>(value); return; } if (method < static_cast<u32>(BufferMethods::CountBufferMethods)) { // TODO(Subv): Research and implement these methods. - NGLOG_ERROR(HW_GPU, "Special buffer methods other than Bind are not implemented"); + LOG_ERROR(HW_GPU, "Special buffer methods other than Bind are not implemented"); return; } @@ -60,8 +61,11 @@ void GPU::WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params) case EngineID::MAXWELL_COMPUTE_B: maxwell_compute->WriteReg(method, value); break; + case EngineID::MAXWELL_DMA_COPY_A: + maxwell_dma->WriteReg(method, value); + break; default: - UNIMPLEMENTED(); + UNIMPLEMENTED_MSG("Unimplemented engine"); } } diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h index bbba8e3807..9382a75e57 100644 --- a/src/video_core/debug_utils/debug_utils.h +++ b/src/video_core/debug_utils/debug_utils.h @@ -55,8 +55,10 @@ public: virtual ~BreakPointObserver() { auto context = context_weak.lock(); if (context) { - std::unique_lock<std::mutex> lock(context->breakpoint_mutex); - context->breakpoint_observers.remove(this); + { + std::unique_lock<std::mutex> lock(context->breakpoint_mutex); + context->breakpoint_observers.remove(this); + } // If we are the last observer to be destroyed, tell the debugger context that // it is free to continue. In particular, this is required for a proper yuzu diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp index 6b9382f062..34053e393d 100644 --- a/src/video_core/engines/fermi_2d.cpp +++ b/src/video_core/engines/fermi_2d.cpp @@ -26,8 +26,8 @@ void Fermi2D::WriteReg(u32 method, u32 value) { } void Fermi2D::HandleSurfaceCopy() { - NGLOG_WARNING(HW_GPU, "Requested a surface copy with operation {}", - static_cast<u32>(regs.operation)); + LOG_WARNING(HW_GPU, "Requested a surface copy with operation {}", + static_cast<u32>(regs.operation)); const GPUVAddr source = regs.src.Address(); const GPUVAddr dest = regs.dst.Address(); @@ -47,6 +47,7 @@ void Fermi2D::HandleSurfaceCopy() { if (regs.src.linear == regs.dst.linear) { // If the input layout and the output layout are the same, just perform a raw copy. + ASSERT(regs.src.BlockHeight() == regs.dst.BlockHeight()); Memory::CopyBlock(dest_cpu, source_cpu, src_bytes_per_pixel * regs.dst.width * regs.dst.height); return; diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 86e9dc998c..3bca16364e 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -126,6 +126,10 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) { DrawArrays(); break; } + case MAXWELL3D_REG_INDEX(clear_buffers): { + ProcessClearBuffers(); + break; + } case MAXWELL3D_REG_INDEX(query.query_get): { ProcessQueryGet(); break; @@ -207,8 +211,8 @@ void Maxwell3D::ProcessQueryGet() { } void Maxwell3D::DrawArrays() { - NGLOG_DEBUG(HW_GPU, "called, topology={}, count={}", - static_cast<u32>(regs.draw.topology.Value()), regs.vertex_buffer.count); + LOG_DEBUG(HW_GPU, "called, topology={}, count={}", static_cast<u32>(regs.draw.topology.Value()), + regs.vertex_buffer.count); ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?"); auto debug_context = Core::System::GetInstance().GetGPUDebugContext(); @@ -328,8 +332,9 @@ std::vector<Texture::FullTextureInfo> Maxwell3D::GetStageTextures(Regs::ShaderSt Texture::FullTextureInfo tex_info{}; // TODO(Subv): Use the shader to determine which textures are actually accessed. - tex_info.index = (current_texture - tex_info_buffer.address - TextureInfoOffset) / - sizeof(Texture::TextureHandle); + tex_info.index = + static_cast<u32>(current_texture - tex_info_buffer.address - TextureInfoOffset) / + sizeof(Texture::TextureHandle); // Load the TIC data. if (tex_handle.tic_id != 0) { @@ -414,5 +419,13 @@ bool Maxwell3D::IsShaderStageEnabled(Regs::ShaderStage stage) const { UNREACHABLE(); } +void Maxwell3D::ProcessClearBuffers() { + ASSERT(regs.clear_buffers.R == regs.clear_buffers.G && + regs.clear_buffers.R == regs.clear_buffers.B && + regs.clear_buffers.R == regs.clear_buffers.A); + + VideoCore::g_renderer->Rasterizer()->Clear(); +} + } // namespace Engines } // namespace Tegra diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 2dc2512059..5a7cf01072 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -280,6 +280,46 @@ public: UnsignedInt = 0x2, }; + enum class ComparisonOp : u32 { + // These values are used by Nouveau and most games, they correspond to the OpenGL token + // values for these operations. + Never = 0x200, + Less = 0x201, + Equal = 0x202, + LessEqual = 0x203, + Greater = 0x204, + NotEqual = 0x205, + GreaterEqual = 0x206, + Always = 0x207, + + // These values are used by some games, they seem to be NV04 values. + NeverOld = 1, + LessOld = 2, + EqualOld = 3, + LessEqualOld = 4, + GreaterOld = 5, + NotEqualOld = 6, + GreaterEqualOld = 7, + AlwaysOld = 8, + }; + + struct Cull { + enum class FrontFace : u32 { + ClockWise = 0x0900, + CounterClockWise = 0x0901, + }; + + enum class CullFace : u32 { + Front = 0x0404, + Back = 0x0405, + FrontAndBack = 0x0408, + }; + + u32 enabled; + FrontFace front_face; + CullFace cull_face; + }; + struct Blend { enum class Equation : u32 { Add = 1, @@ -321,6 +361,24 @@ public: INSERT_PADDING_WORDS(1); }; + struct RenderTargetConfig { + u32 address_high; + u32 address_low; + u32 width; + u32 height; + Tegra::RenderTargetFormat format; + u32 block_dimensions; + u32 array_mode; + u32 layer_stride; + u32 base_layer; + INSERT_PADDING_WORDS(7); + + GPUVAddr Address() const { + return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | + address_low); + } + }; + union { struct { INSERT_PADDING_WORDS(0x45); @@ -333,23 +391,7 @@ public: INSERT_PADDING_WORDS(0x1B8); - struct { - u32 address_high; - u32 address_low; - u32 width; - u32 height; - Tegra::RenderTargetFormat format; - u32 block_dimensions; - u32 array_mode; - u32 layer_stride; - u32 base_layer; - INSERT_PADDING_WORDS(7); - - GPUVAddr Address() const { - return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | - address_low); - } - } rt[NumRenderTargets]; + RenderTargetConfig rt[NumRenderTargets]; struct { f32 scale_x; @@ -406,12 +448,17 @@ public: u32 count; } vertex_buffer; - INSERT_PADDING_WORDS(0x99); + INSERT_PADDING_WORDS(1); + + float clear_color[4]; + float clear_depth; + + INSERT_PADDING_WORDS(0x93); struct { u32 address_high; u32 address_low; - u32 format; + Tegra::DepthFormat format; u32 block_dimensions; u32 layer_stride; @@ -433,11 +480,23 @@ public: }; } rt_control; - INSERT_PADDING_WORDS(0x31); + INSERT_PADDING_WORDS(0x2B); + + u32 depth_test_enable; + + INSERT_PADDING_WORDS(0x5); u32 independent_blend_enable; - INSERT_PADDING_WORDS(0x15); + u32 depth_write_enabled; + + INSERT_PADDING_WORDS(0x7); + + u32 d3d_cull_mode; + + ComparisonOp depth_test_func; + + INSERT_PADDING_WORDS(0xB); struct { u32 separate_alpha; @@ -453,7 +512,17 @@ public: u32 enable[NumRenderTargets]; } blend; - INSERT_PADDING_WORDS(0x77); + INSERT_PADDING_WORDS(0xB); + + union { + BitField<4, 1, u32> triangle_rast_flip; + } screen_y_control; + + INSERT_PADDING_WORDS(0x21); + + u32 vb_element_base; + + INSERT_PADDING_WORDS(0x49); struct { u32 tsc_address_high; @@ -479,7 +548,12 @@ public: } } tic; - INSERT_PADDING_WORDS(0x22); + INSERT_PADDING_WORDS(0x21); + + union { + BitField<2, 1, u32> coord_origin; + BitField<3, 10, u32> enable; + } point_coord_replace; struct { u32 code_address_high; @@ -534,7 +608,27 @@ public: } } index_array; - INSERT_PADDING_WORDS(0xC7); + INSERT_PADDING_WORDS(0x7); + + INSERT_PADDING_WORDS(0x46); + + Cull cull; + + INSERT_PADDING_WORDS(0x2B); + + union { + u32 raw; + BitField<0, 1, u32> Z; + BitField<1, 1, u32> S; + BitField<2, 1, u32> R; + BitField<3, 1, u32> G; + BitField<4, 1, u32> B; + BitField<5, 1, u32> A; + BitField<6, 4, u32> RT; + BitField<10, 11, u32> layer; + } clear_buffers; + + INSERT_PADDING_WORDS(0x4B); struct { u32 query_address_high; @@ -716,6 +810,9 @@ private: /// Handles writes to the macro uploading registers. void ProcessMacroUpload(u32 data); + /// Handles a write to the CLEAR_BUFFERS register. + void ProcessClearBuffers(); + /// Handles a write to the QUERY_GET register. void ProcessQueryGet(); @@ -738,16 +835,27 @@ ASSERT_REG_POSITION(rt, 0x200); ASSERT_REG_POSITION(viewport_transform[0], 0x280); ASSERT_REG_POSITION(viewport, 0x300); ASSERT_REG_POSITION(vertex_buffer, 0x35D); +ASSERT_REG_POSITION(clear_color[0], 0x360); +ASSERT_REG_POSITION(clear_depth, 0x364); ASSERT_REG_POSITION(zeta, 0x3F8); ASSERT_REG_POSITION(vertex_attrib_format[0], 0x458); ASSERT_REG_POSITION(rt_control, 0x487); +ASSERT_REG_POSITION(depth_test_enable, 0x4B3); ASSERT_REG_POSITION(independent_blend_enable, 0x4B9); +ASSERT_REG_POSITION(depth_write_enabled, 0x4BA); +ASSERT_REG_POSITION(d3d_cull_mode, 0x4C2); +ASSERT_REG_POSITION(depth_test_func, 0x4C3); ASSERT_REG_POSITION(blend, 0x4CF); +ASSERT_REG_POSITION(screen_y_control, 0x4EB); +ASSERT_REG_POSITION(vb_element_base, 0x50D); ASSERT_REG_POSITION(tsc, 0x557); ASSERT_REG_POSITION(tic, 0x55D); +ASSERT_REG_POSITION(point_coord_replace, 0x581); ASSERT_REG_POSITION(code_address, 0x582); ASSERT_REG_POSITION(draw, 0x585); ASSERT_REG_POSITION(index_array, 0x5F2); +ASSERT_REG_POSITION(cull, 0x646); +ASSERT_REG_POSITION(clear_buffers, 0x674); ASSERT_REG_POSITION(query, 0x6C0); ASSERT_REG_POSITION(vertex_array[0], 0x700); ASSERT_REG_POSITION(independent_blend, 0x780); diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp new file mode 100644 index 0000000000..6e740713f2 --- /dev/null +++ b/src/video_core/engines/maxwell_dma.cpp @@ -0,0 +1,73 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/memory.h" +#include "video_core/engines/maxwell_dma.h" +#include "video_core/textures/decoders.h" + +namespace Tegra { +namespace Engines { + +MaxwellDMA::MaxwellDMA(MemoryManager& memory_manager) : memory_manager(memory_manager) {} + +void MaxwellDMA::WriteReg(u32 method, u32 value) { + ASSERT_MSG(method < Regs::NUM_REGS, + "Invalid MaxwellDMA register, increase the size of the Regs structure"); + + regs.reg_array[method] = value; + +#define MAXWELLDMA_REG_INDEX(field_name) \ + (offsetof(Tegra::Engines::MaxwellDMA::Regs, field_name) / sizeof(u32)) + + switch (method) { + case MAXWELLDMA_REG_INDEX(exec): { + HandleCopy(); + break; + } + } + +#undef MAXWELLDMA_REG_INDEX +} + +void MaxwellDMA::HandleCopy() { + LOG_WARNING(HW_GPU, "Requested a DMA copy"); + + const GPUVAddr source = regs.src_address.Address(); + const GPUVAddr dest = regs.dst_address.Address(); + + const VAddr source_cpu = *memory_manager.GpuToCpuAddress(source); + const VAddr dest_cpu = *memory_manager.GpuToCpuAddress(dest); + + // TODO(Subv): Perform more research and implement all features of this engine. + ASSERT(regs.exec.enable_swizzle == 0); + ASSERT(regs.exec.enable_2d == 1); + ASSERT(regs.exec.query_mode == Regs::QueryMode::None); + ASSERT(regs.exec.query_intr == Regs::QueryIntr::None); + ASSERT(regs.exec.copy_mode == Regs::CopyMode::Unk2); + ASSERT(regs.src_params.pos_x == 0); + ASSERT(regs.src_params.pos_y == 0); + ASSERT(regs.dst_params.pos_x == 0); + ASSERT(regs.dst_params.pos_y == 0); + + if (regs.exec.is_dst_linear == regs.exec.is_src_linear) { + Memory::CopyBlock(dest_cpu, source_cpu, regs.x_count * regs.y_count); + return; + } + + u8* src_buffer = Memory::GetPointer(source_cpu); + u8* dst_buffer = Memory::GetPointer(dest_cpu); + + if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) { + // If the input is tiled and the output is linear, deswizzle the input and copy it over. + Texture::CopySwizzledData(regs.src_params.size_x, regs.src_params.size_y, 1, 1, src_buffer, + dst_buffer, true, regs.src_params.BlockHeight()); + } else { + // If the input is linear and the output is tiled, swizzle the input and copy it over. + Texture::CopySwizzledData(regs.dst_params.size_x, regs.dst_params.size_y, 1, 1, dst_buffer, + src_buffer, false, regs.dst_params.BlockHeight()); + } +} + +} // namespace Engines +} // namespace Tegra diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h new file mode 100644 index 0000000000..905749bde5 --- /dev/null +++ b/src/video_core/engines/maxwell_dma.h @@ -0,0 +1,155 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include "common/assert.h" +#include "common/bit_field.h" +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "video_core/gpu.h" +#include "video_core/memory_manager.h" + +namespace Tegra { +namespace Engines { + +class MaxwellDMA final { +public: + explicit MaxwellDMA(MemoryManager& memory_manager); + ~MaxwellDMA() = default; + + /// Write the value to the register identified by method. + void WriteReg(u32 method, u32 value); + + struct Regs { + static constexpr size_t NUM_REGS = 0x1D6; + + struct Parameters { + union { + BitField<0, 4, u32> block_depth; + BitField<4, 4, u32> block_height; + BitField<8, 4, u32> block_width; + }; + u32 size_x; + u32 size_y; + u32 size_z; + u32 pos_z; + union { + BitField<0, 16, u32> pos_x; + BitField<16, 16, u32> pos_y; + }; + + u32 BlockHeight() const { + return 1 << block_height; + } + }; + + static_assert(sizeof(Parameters) == 24, "Parameters has wrong size"); + + enum class CopyMode : u32 { + None = 0, + Unk1 = 1, + Unk2 = 2, + }; + + enum class QueryMode : u32 { + None = 0, + Short = 1, + Long = 2, + }; + + enum class QueryIntr : u32 { + None = 0, + Block = 1, + NonBlock = 2, + }; + + union { + struct { + INSERT_PADDING_WORDS(0xC0); + + struct { + union { + BitField<0, 2, CopyMode> copy_mode; + BitField<2, 1, u32> flush; + + BitField<3, 2, QueryMode> query_mode; + BitField<5, 2, QueryIntr> query_intr; + + BitField<7, 1, u32> is_src_linear; + BitField<8, 1, u32> is_dst_linear; + + BitField<9, 1, u32> enable_2d; + BitField<10, 1, u32> enable_swizzle; + }; + } exec; + + INSERT_PADDING_WORDS(0x3F); + + struct { + u32 address_high; + u32 address_low; + + GPUVAddr Address() const { + return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | + address_low); + } + } src_address; + + struct { + u32 address_high; + u32 address_low; + + GPUVAddr Address() const { + return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | + address_low); + } + } dst_address; + + u32 src_pitch; + u32 dst_pitch; + u32 x_count; + u32 y_count; + + INSERT_PADDING_WORDS(0xBB); + + Parameters dst_params; + + INSERT_PADDING_WORDS(1); + + Parameters src_params; + + INSERT_PADDING_WORDS(0x13); + }; + std::array<u32, NUM_REGS> reg_array; + }; + } regs{}; + + MemoryManager& memory_manager; + +private: + /// Performs the copy from the source buffer to the destination buffer as configured in the + /// registers. + void HandleCopy(); +}; + +#define ASSERT_REG_POSITION(field_name, position) \ + static_assert(offsetof(MaxwellDMA::Regs, field_name) == position * 4, \ + "Field " #field_name " has invalid position") + +ASSERT_REG_POSITION(exec, 0xC0); +ASSERT_REG_POSITION(src_address, 0x100); +ASSERT_REG_POSITION(dst_address, 0x102); +ASSERT_REG_POSITION(src_pitch, 0x104); +ASSERT_REG_POSITION(dst_pitch, 0x105); +ASSERT_REG_POSITION(x_count, 0x106); +ASSERT_REG_POSITION(y_count, 0x107); +ASSERT_REG_POSITION(dst_params, 0x1C3); +ASSERT_REG_POSITION(src_params, 0x1CA); + +#undef ASSERT_REG_POSITION + +} // namespace Engines +} // namespace Tegra diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index ec8dbd370b..2bc1782ad7 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -142,6 +142,7 @@ enum class PredCondition : u64 { GreaterThan = 4, NotEqual = 5, GreaterEqual = 6, + NotEqualWithNan = 13, // TODO(Subv): Other condition types }; @@ -165,7 +166,7 @@ enum class SubOp : u64 { Lg2 = 0x3, Rcp = 0x4, Rsq = 0x5, - Min = 0x8, + Sqrt = 0x8, }; enum class F2iRoundingOp : u64 { @@ -193,6 +194,13 @@ enum class UniformType : u64 { Double = 5, }; +enum class IMinMaxExchange : u64 { + None = 0, + XLo = 1, + XMed = 2, + XHi = 3, +}; + union Instruction { Instruction& operator=(const Instruction& instr) { value = instr.value; @@ -209,20 +217,19 @@ union Instruction { } pred; BitField<19, 1, u64> negate_pred; BitField<20, 8, Register> gpr20; - BitField<20, 7, SubOp> sub_op; + BitField<20, 4, SubOp> sub_op; BitField<28, 8, Register> gpr28; BitField<39, 8, Register> gpr39; BitField<48, 16, u64> opcode; - BitField<50, 1, u64> saturate_a; union { BitField<20, 19, u64> imm20_19; - BitField<20, 32, u64> imm20_32; + BitField<20, 32, s64> imm20_32; BitField<45, 1, u64> negate_b; BitField<46, 1, u64> abs_a; BitField<48, 1, u64> negate_a; BitField<49, 1, u64> abs_b; - BitField<50, 1, u64> abs_d; + BitField<50, 1, u64> saturate_d; BitField<56, 1, u64> negate_imm; union { @@ -231,10 +238,18 @@ union Instruction { } fmnmx; union { + BitField<39, 1, u64> invert_a; + BitField<40, 1, u64> invert_b; + BitField<41, 2, LogicOperation> operation; + BitField<44, 2, u64> unk44; + BitField<48, 3, Pred> pred48; + } lop; + + union { BitField<53, 2, LogicOperation> operation; BitField<55, 1, u64> invert_a; BitField<56, 1, u64> invert_b; - } lop; + } lop32i; float GetImm20_19() const { float result{}; @@ -247,7 +262,7 @@ union Instruction { float GetImm20_32() const { float result{}; - u32 imm{static_cast<u32>(imm20_32)}; + s32 imm{static_cast<s32>(imm20_32)}; std::memcpy(&result, &imm, sizeof(imm)); return result; } @@ -271,6 +286,18 @@ union Instruction { } alu_integer; union { + BitField<39, 3, u64> pred; + BitField<42, 1, u64> negate_pred; + BitField<43, 2, IMinMaxExchange> exchange; + BitField<48, 1, u64> is_signed; + } imnmx; + + union { + BitField<54, 1, u64> saturate; + BitField<56, 1, u64> negate_a; + } iadd32i; + + union { BitField<20, 8, u64> shift_position; BitField<28, 8, u64> shift_length; BitField<48, 1, u64> negate_b; @@ -316,6 +343,19 @@ union Instruction { } isetp; union { + BitField<0, 3, u64> pred0; + BitField<3, 3, u64> pred3; + BitField<12, 3, u64> pred12; + BitField<15, 1, u64> neg_pred12; + BitField<24, 2, PredOperation> cond; + BitField<29, 3, u64> pred29; + BitField<32, 1, u64> neg_pred29; + BitField<39, 3, u64> pred39; + BitField<42, 1, u64> neg_pred39; + BitField<45, 2, PredOperation> op; + } psetp; + + union { BitField<39, 3, u64> pred39; BitField<42, 1, u64> neg_pred; BitField<43, 1, u64> neg_a; @@ -339,7 +379,8 @@ union Instruction { } iset; union { - BitField<10, 2, Register::Size> size; + BitField<8, 2, Register::Size> dest_size; + BitField<10, 2, Register::Size> src_size; BitField<12, 1, u64> is_output_signed; BitField<13, 1, u64> is_input_signed; BitField<41, 2, u64> selector; @@ -359,7 +400,7 @@ union Instruction { BitField<31, 4, u64> component_mask; bool IsComponentEnabled(size_t component) const { - return ((1 << component) & component_mask) != 0; + return ((1ull << component) & component_mask) != 0; } } tex; @@ -378,7 +419,7 @@ union Instruction { ASSERT(component_mask_selector < mask.size()); - return ((1 << component) & mask[component_mask_selector]) != 0; + return ((1ull << component) & mask[component_mask_selector]) != 0; } } texs; @@ -424,6 +465,8 @@ public: enum class Id { KIL, SSY, + SYNC, + DEPBAR, BFE_C, BFE_R, BFE_IMM, @@ -451,6 +494,7 @@ public: IADD_C, IADD_R, IADD_IMM, + IADD32I, ISCADD_C, // Scale and Add ISCADD_R, ISCADD_IMM, @@ -470,6 +514,9 @@ public: I2I_C, I2I_R, I2I_IMM, + LOP_C, + LOP_R, + LOP_IMM, LOP32I, MOV_C, MOV_R, @@ -509,12 +556,14 @@ public: enum class Type { Trivial, Arithmetic, + ArithmeticImmediate, ArithmeticInteger, + ArithmeticIntegerImmediate, Bfe, - Logic, Shift, Ffma, Flow, + Synch, Memory, FloatSet, FloatSetPredicate, @@ -619,10 +668,12 @@ private: INST("111000110011----", Id::KIL, Type::Flow, "KIL"), INST("111000101001----", Id::SSY, Type::Flow, "SSY"), INST("111000100100----", Id::BRA, Type::Flow, "BRA"), + INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"), + INST("1111000011111---", Id::SYNC, Type::Synch, "SYNC"), INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"), INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"), INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"), - INST("1100000000111---", Id::TEX, Type::Memory, "TEX"), + INST("110000----111---", Id::TEX, Type::Memory, "TEX"), INST("1101111101001---", Id::TEXQ, Type::Memory, "TEXQ"), INST("1101100---------", Id::TEXS, Type::Memory, "TEXS"), INST("1101101---------", Id::TLDS, Type::Memory, "TLDS"), @@ -638,10 +689,11 @@ private: INST("0100110001101---", Id::FMUL_C, Type::Arithmetic, "FMUL_C"), INST("0101110001101---", Id::FMUL_R, Type::Arithmetic, "FMUL_R"), INST("0011100-01101---", Id::FMUL_IMM, Type::Arithmetic, "FMUL_IMM"), - INST("00011110--------", Id::FMUL32_IMM, Type::Arithmetic, "FMUL32_IMM"), + INST("00011110--------", Id::FMUL32_IMM, Type::ArithmeticImmediate, "FMUL32_IMM"), INST("0100110000010---", Id::IADD_C, Type::ArithmeticInteger, "IADD_C"), INST("0101110000010---", Id::IADD_R, Type::ArithmeticInteger, "IADD_R"), INST("0011100-00010---", Id::IADD_IMM, Type::ArithmeticInteger, "IADD_IMM"), + INST("0001110---------", Id::IADD32I, Type::ArithmeticIntegerImmediate, "IADD32I"), INST("0100110000011---", Id::ISCADD_C, Type::ArithmeticInteger, "ISCADD_C"), INST("0101110000011---", Id::ISCADD_R, Type::ArithmeticInteger, "ISCADD_R"), INST("0011100-00011---", Id::ISCADD_IMM, Type::ArithmeticInteger, "ISCADD_IMM"), @@ -658,17 +710,20 @@ private: INST("0100110010011---", Id::MOV_C, Type::Arithmetic, "MOV_C"), INST("0101110010011---", Id::MOV_R, Type::Arithmetic, "MOV_R"), INST("0011100-10011---", Id::MOV_IMM, Type::Arithmetic, "MOV_IMM"), - INST("000000010000----", Id::MOV32_IMM, Type::Arithmetic, "MOV32_IMM"), + INST("000000010000----", Id::MOV32_IMM, Type::ArithmeticImmediate, "MOV32_IMM"), INST("0100110001100---", Id::FMNMX_C, Type::Arithmetic, "FMNMX_C"), INST("0101110001100---", Id::FMNMX_R, Type::Arithmetic, "FMNMX_R"), INST("0011100-01100---", Id::FMNMX_IMM, Type::Arithmetic, "FMNMX_IMM"), - INST("0100110000100---", Id::IMNMX_C, Type::Arithmetic, "FMNMX_IMM"), - INST("0101110000100---", Id::IMNMX_R, Type::Arithmetic, "FMNMX_IMM"), - INST("0011100-00100---", Id::IMNMX_IMM, Type::Arithmetic, "FMNMX_IMM"), + INST("0100110000100---", Id::IMNMX_C, Type::ArithmeticInteger, "IMNMX_C"), + INST("0101110000100---", Id::IMNMX_R, Type::ArithmeticInteger, "IMNMX_R"), + INST("0011100-00100---", Id::IMNMX_IMM, Type::ArithmeticInteger, "IMNMX_IMM"), INST("0100110000000---", Id::BFE_C, Type::Bfe, "BFE_C"), INST("0101110000000---", Id::BFE_R, Type::Bfe, "BFE_R"), INST("0011100-00000---", Id::BFE_IMM, Type::Bfe, "BFE_IMM"), - INST("000001----------", Id::LOP32I, Type::Logic, "LOP32I"), + INST("0100110001000---", Id::LOP_C, Type::ArithmeticInteger, "LOP_C"), + INST("0101110001000---", Id::LOP_R, Type::ArithmeticInteger, "LOP_R"), + INST("0011100001000---", Id::LOP_IMM, Type::ArithmeticInteger, "LOP_IMM"), + INST("000001----------", Id::LOP32I, Type::ArithmeticIntegerImmediate, "LOP32I"), INST("0100110001001---", Id::SHL_C, Type::Shift, "SHL_C"), INST("0101110001001---", Id::SHL_R, Type::Shift, "SHL_R"), INST("0011100-01001---", Id::SHL_IMM, Type::Shift, "SHL_IMM"), diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 66351fe6e4..e364831454 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -5,6 +5,7 @@ #include "video_core/engines/fermi_2d.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/engines/maxwell_compute.h" +#include "video_core/engines/maxwell_dma.h" #include "video_core/gpu.h" namespace Tegra { @@ -14,6 +15,7 @@ GPU::GPU() { maxwell_3d = std::make_unique<Engines::Maxwell3D>(*memory_manager); fermi_2d = std::make_unique<Engines::Fermi2D>(*memory_manager); maxwell_compute = std::make_unique<Engines::MaxwellCompute>(); + maxwell_dma = std::make_unique<Engines::MaxwellDMA>(*memory_manager); } GPU::~GPU() = default; diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 5852b96199..cc5ca656e4 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -16,6 +16,7 @@ namespace Tegra { enum class RenderTargetFormat : u32 { NONE = 0x0, RGBA32_FLOAT = 0xC0, + RGBA32_UINT = 0xC2, RGBA16_FLOAT = 0xCA, RGB10_A2_UNORM = 0xD1, RGBA8_UNORM = 0xD5, @@ -23,6 +24,15 @@ enum class RenderTargetFormat : u32 { R11G11B10_FLOAT = 0xE0, }; +enum class DepthFormat : u32 { + Z32_FLOAT = 0xA, + Z16_UNORM = 0x13, + S8_Z24_UNORM = 0x14, + Z24_X8_UNORM = 0x15, + Z24_S8_UNORM = 0x16, + Z24_C8_UNORM = 0x18, +}; + /// Returns the number of bytes per pixel of each rendertarget format. u32 RenderTargetBytesPerPixel(RenderTargetFormat format); @@ -63,6 +73,7 @@ namespace Engines { class Fermi2D; class Maxwell3D; class MaxwellCompute; +class MaxwellDMA; } // namespace Engines enum class EngineID { @@ -103,6 +114,8 @@ private: std::unique_ptr<Engines::Fermi2D> fermi_2d; /// Compute engine std::unique_ptr<Engines::MaxwellCompute> maxwell_compute; + /// DMA engine + std::unique_ptr<Engines::MaxwellDMA> maxwell_dma; }; } // namespace Tegra diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index 5cefce9fce..2f814a1842 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -100,9 +100,9 @@ boost::optional<GPUVAddr> MemoryManager::FindFreeBlock(u64 size, u64 align) { boost::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) { VAddr base_addr = PageSlot(gpu_addr); - ASSERT(base_addr != static_cast<u64>(PageStatus::Unmapped)); - if (base_addr == static_cast<u64>(PageStatus::Allocated)) { + if (base_addr == static_cast<u64>(PageStatus::Allocated) || + base_addr == static_cast<u64>(PageStatus::Unmapped)) { return {}; } diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index f0e48a802a..499e84b892 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h @@ -19,6 +19,9 @@ public: /// Draw the current batch of vertex arrays virtual void DrawArrays() = 0; + /// Clear the current framebuffer + virtual void Clear() = 0; + /// Notify rasterizer that the specified Maxwell register has been changed virtual void NotifyMaxwellRegisterChanged(u32 method) = 0; @@ -51,9 +54,8 @@ public: } /// Attempt to use a faster method to display the framebuffer to screen - virtual bool AccelerateDisplay(const Tegra::FramebufferConfig& framebuffer, - VAddr framebuffer_addr, u32 pixel_stride, - ScreenInfo& screen_info) { + virtual bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, + u32 pixel_stride, ScreenInfo& screen_info) { return false; } diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 6f05f24a01..ea138d4029 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -112,7 +112,7 @@ RasterizerOpenGL::RasterizerOpenGL() { glEnable(GL_BLEND); - NGLOG_CRITICAL(Render_OpenGL, "Sync fixed function OpenGL state here!"); + LOG_CRITICAL(Render_OpenGL, "Sync fixed function OpenGL state here!"); } RasterizerOpenGL::~RasterizerOpenGL() { @@ -146,7 +146,6 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr, u64 size = end - start + 1; // Copy vertex array data - res_cache.FlushRegion(start, size, nullptr); Memory::ReadBlock(*memory_manager->GpuToCpuAddress(start), array_ptr, size); // Bind the vertex array to the buffer at the current offset. @@ -166,9 +165,9 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr, // assume every shader uses them all. for (unsigned index = 0; index < 16; ++index) { auto& attrib = regs.vertex_attrib_format[index]; - NGLOG_DEBUG(HW_GPU, "vertex attrib {}, count={}, size={}, type={}, offset={}, normalize={}", - index, attrib.ComponentCount(), attrib.SizeString(), attrib.TypeString(), - attrib.offset.Value(), attrib.IsNormalized()); + LOG_DEBUG(HW_GPU, "vertex attrib {}, count={}, size={}, type={}, offset={}, normalize={}", + index, attrib.ComponentCount(), attrib.SizeString(), attrib.TypeString(), + attrib.offset.Value(), attrib.IsNormalized()); auto& buffer = regs.vertex_array[attrib.buffer]; ASSERT(buffer.IsEnabled()); @@ -197,8 +196,8 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) { ASSERT_MSG(!gpu.regs.shader_config[0].enable, "VertexA is unsupported!"); // Next available bindpoints to use when uploading the const buffers and textures to the GLSL - // shaders. - u32 current_constbuffer_bindpoint = 0; + // shaders. The constbuffer bindpoint starts after the shader stage configuration bind points. + u32 current_constbuffer_bindpoint = uniform_buffers.size(); u32 current_texture_bindpoint = 0; for (unsigned index = 1; index < Maxwell::MaxShaderProgram; ++index) { @@ -252,8 +251,8 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) { break; } default: - NGLOG_CRITICAL(HW_GPU, "Unimplemented shader index={}, enable={}, offset=0x{:08X}", - index, shader_config.enable.Value(), shader_config.offset); + LOG_CRITICAL(HW_GPU, "Unimplemented shader index={}, enable={}, offset=0x{:08X}", index, + shader_config.enable.Value(), shader_config.offset); UNREACHABLE(); } @@ -298,17 +297,16 @@ bool RasterizerOpenGL::AccelerateDrawBatch(bool is_indexed) { return true; } -void RasterizerOpenGL::DrawArrays() { - if (accelerate_draw == AccelDraw::Disabled) - return; - - MICROPROFILE_SCOPE(OpenGL_Drawing); +std::pair<Surface, Surface> RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, + bool using_depth_fb) { const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; - // TODO(bunnei): Implement these + // Sync the depth test state before configuring the framebuffer surfaces. + SyncDepthTestState(); + + // TODO(bunnei): Implement this const bool has_stencil = false; - const bool using_color_fb = true; - const bool using_depth_fb = false; + const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()}; const bool write_color_fb = @@ -325,35 +323,21 @@ void RasterizerOpenGL::DrawArrays() { std::tie(color_surface, depth_surface, surfaces_rect) = res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb, viewport_rect); - const u16 res_scale = color_surface != nullptr - ? color_surface->res_scale - : (depth_surface == nullptr ? 1u : depth_surface->res_scale); - MathUtil::Rectangle<u32> draw_rect{ + static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.left, + surfaces_rect.left, surfaces_rect.right)), // Left + static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + viewport_rect.top, + surfaces_rect.bottom, surfaces_rect.top)), // Top + static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.right, + surfaces_rect.left, surfaces_rect.right)), // Right static_cast<u32>( - std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.left * res_scale, - surfaces_rect.left, surfaces_rect.right)), // Left - static_cast<u32>( - std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + viewport_rect.top * res_scale, - surfaces_rect.bottom, surfaces_rect.top)), // Top - static_cast<u32>( - std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.right * res_scale, - surfaces_rect.left, surfaces_rect.right)), // Right - static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + - viewport_rect.bottom * res_scale, - surfaces_rect.bottom, surfaces_rect.top))}; // Bottom + std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + viewport_rect.bottom, + surfaces_rect.bottom, surfaces_rect.top))}; // Bottom // Bind the framebuffer surfaces BindFramebufferSurfaces(color_surface, depth_surface, has_stencil); - // Sync the viewport - SyncViewport(surfaces_rect, res_scale); - - // Sync the blend state registers - SyncBlendState(); - - // TODO(bunnei): Sync framebuffer_scale uniform here - // TODO(bunnei): Sync scissorbox uniform(s) here + SyncViewport(surfaces_rect); // Viewport can have negative offsets or larger dimensions than our framebuffer sub-rect. Enable // scissor test to prevent drawing outside of the framebuffer region @@ -364,6 +348,66 @@ void RasterizerOpenGL::DrawArrays() { state.scissor.height = draw_rect.GetHeight(); state.Apply(); + // Only return the surface to be marked as dirty if writing to it is enabled. + return std::make_pair(write_color_fb ? color_surface : nullptr, + write_depth_fb ? depth_surface : nullptr); +} + +void RasterizerOpenGL::Clear() { + const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; + + bool use_color_fb = false; + bool use_depth_fb = false; + + GLbitfield clear_mask = 0; + if (regs.clear_buffers.R && regs.clear_buffers.G && regs.clear_buffers.B && + regs.clear_buffers.A) { + clear_mask |= GL_COLOR_BUFFER_BIT; + use_color_fb = true; + } + if (regs.clear_buffers.Z) { + clear_mask |= GL_DEPTH_BUFFER_BIT; + use_depth_fb = true; + } + + if (clear_mask == 0) + return; + + auto [dirty_color_surface, dirty_depth_surface] = + ConfigureFramebuffers(use_color_fb, use_depth_fb); + + // TODO(Subv): Support clearing only partial colors. + glClearColor(regs.clear_color[0], regs.clear_color[1], regs.clear_color[2], + regs.clear_color[3]); + glClearDepth(regs.clear_depth); + + glClear(clear_mask); + + // Mark framebuffer surfaces as dirty + if (dirty_color_surface != nullptr) { + res_cache.MarkSurfaceAsDirty(dirty_color_surface); + } + if (dirty_depth_surface != nullptr) { + res_cache.MarkSurfaceAsDirty(dirty_depth_surface); + } +} + +void RasterizerOpenGL::DrawArrays() { + if (accelerate_draw == AccelDraw::Disabled) + return; + + MICROPROFILE_SCOPE(OpenGL_Drawing); + const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; + + auto [dirty_color_surface, dirty_depth_surface] = + ConfigureFramebuffers(true, regs.zeta.Address() != 0); + + SyncBlendState(); + SyncCullMode(); + + // TODO(bunnei): Sync framebuffer_scale uniform here + // TODO(bunnei): Sync scissorbox uniform(s) here + // Draw the vertex batch const bool is_indexed = accelerate_draw == AccelDraw::Indexed; const u64 index_buffer_size{regs.index_array.count * regs.index_array.FormatSizeInBytes()}; @@ -420,14 +464,16 @@ void RasterizerOpenGL::DrawArrays() { const GLenum primitive_mode{MaxwellToGL::PrimitiveTopology(regs.draw.topology)}; if (is_indexed) { - const GLint index_min{static_cast<GLint>(regs.index_array.first)}; - const GLint index_max{static_cast<GLint>(regs.index_array.first + regs.index_array.count)}; - glDrawRangeElementsBaseVertex(primitive_mode, index_min, index_max, regs.index_array.count, - MaxwellToGL::IndexFormat(regs.index_array.format), - reinterpret_cast<const void*>(index_buffer_offset), - -index_min); + const GLint base_vertex{static_cast<GLint>(regs.vb_element_base)}; + + // Adjust the index buffer offset so it points to the first desired index. + index_buffer_offset += regs.index_array.first * regs.index_array.FormatSizeInBytes(); + + glDrawElementsBaseVertex(primitive_mode, regs.index_array.count, + MaxwellToGL::IndexFormat(regs.index_array.format), + reinterpret_cast<const void*>(index_buffer_offset), base_vertex); } else { - glDrawArrays(primitive_mode, 0, regs.vertex_buffer.count); + glDrawArrays(primitive_mode, regs.vertex_buffer.first, regs.vertex_buffer.count); } // Disable scissor test @@ -437,24 +483,16 @@ void RasterizerOpenGL::DrawArrays() { // Unbind textures for potential future use as framebuffer attachments for (auto& texture_unit : state.texture_units) { - texture_unit.texture_2d = 0; + texture_unit.Unbind(); } state.Apply(); // Mark framebuffer surfaces as dirty - MathUtil::Rectangle<u32> draw_rect_unscaled{ - draw_rect.left / res_scale, draw_rect.top / res_scale, draw_rect.right / res_scale, - draw_rect.bottom / res_scale}; - - if (color_surface != nullptr && write_color_fb) { - auto interval = color_surface->GetSubRectInterval(draw_rect_unscaled); - res_cache.InvalidateRegion(boost::icl::first(interval), boost::icl::length(interval), - color_surface); + if (dirty_color_surface != nullptr) { + res_cache.MarkSurfaceAsDirty(dirty_color_surface); } - if (depth_surface != nullptr && write_depth_fb) { - auto interval = depth_surface->GetSubRectInterval(draw_rect_unscaled); - res_cache.InvalidateRegion(boost::icl::first(interval), boost::icl::length(interval), - depth_surface); + if (dirty_depth_surface != nullptr) { + res_cache.MarkSurfaceAsDirty(dirty_depth_surface); } } @@ -462,7 +500,7 @@ void RasterizerOpenGL::NotifyMaxwellRegisterChanged(u32 method) {} void RasterizerOpenGL::FlushAll() { MICROPROFILE_SCOPE(OpenGL_CacheManagement); - res_cache.FlushAll(); + res_cache.FlushRegion(0, Kernel::VMManager::MAX_ADDRESS); } void RasterizerOpenGL::FlushRegion(Tegra::GPUVAddr addr, u64 size) { @@ -472,13 +510,13 @@ void RasterizerOpenGL::FlushRegion(Tegra::GPUVAddr addr, u64 size) { void RasterizerOpenGL::InvalidateRegion(Tegra::GPUVAddr addr, u64 size) { MICROPROFILE_SCOPE(OpenGL_CacheManagement); - res_cache.InvalidateRegion(addr, size, nullptr); + res_cache.InvalidateRegion(addr, size); } void RasterizerOpenGL::FlushAndInvalidateRegion(Tegra::GPUVAddr addr, u64 size) { MICROPROFILE_SCOPE(OpenGL_CacheManagement); res_cache.FlushRegion(addr, size); - res_cache.InvalidateRegion(addr, size, nullptr); + res_cache.InvalidateRegion(addr, size); } bool RasterizerOpenGL::AccelerateDisplayTransfer(const void* config) { @@ -497,45 +535,28 @@ bool RasterizerOpenGL::AccelerateFill(const void* config) { return true; } -bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& framebuffer, +bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, u32 pixel_stride, ScreenInfo& screen_info) { - if (framebuffer_addr == 0) { - return false; + if (!framebuffer_addr) { + return {}; } + MICROPROFILE_SCOPE(OpenGL_CacheManagement); - SurfaceParams src_params; - src_params.cpu_addr = framebuffer_addr; - src_params.addr = res_cache.TryFindFramebufferGpuAddress(framebuffer_addr).get_value_or(0); - src_params.width = std::min(framebuffer.width, pixel_stride); - src_params.height = framebuffer.height; - src_params.stride = pixel_stride; - src_params.is_tiled = true; - src_params.block_height = Tegra::Texture::TICEntry::DefaultBlockHeight; - src_params.pixel_format = - SurfaceParams::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format); - src_params.component_type = - SurfaceParams::ComponentTypeFromGPUPixelFormat(framebuffer.pixel_format); - src_params.UpdateParams(); - - MathUtil::Rectangle<u32> src_rect; - Surface src_surface; - std::tie(src_surface, src_rect) = - res_cache.GetSurfaceSubRect(src_params, ScaleMatch::Ignore, true); - - if (src_surface == nullptr) { - return false; + const auto& surface{res_cache.TryFindFramebufferSurface(framebuffer_addr)}; + if (!surface) { + return {}; } - u32 scaled_width = src_surface->GetScaledWidth(); - u32 scaled_height = src_surface->GetScaledHeight(); + // Verify that the cached surface is the same size and format as the requested framebuffer + const auto& params{surface->GetSurfaceParams()}; + const auto& pixel_format{SurfaceParams::PixelFormatFromGPUPixelFormat(config.pixel_format)}; + ASSERT_MSG(params.width == config.width, "Framebuffer width is different"); + ASSERT_MSG(params.height == config.height, "Framebuffer height is different"); + ASSERT_MSG(params.pixel_format == pixel_format, "Framebuffer pixel_format is different"); - screen_info.display_texcoords = MathUtil::Rectangle<float>( - (float)src_rect.bottom / (float)scaled_height, (float)src_rect.left / (float)scaled_width, - (float)src_rect.top / (float)scaled_height, (float)src_rect.right / (float)scaled_width); - - screen_info.display_texture = src_surface->texture.handle; + screen_info.display_texture = surface->Texture().handle; return true; } @@ -608,32 +629,44 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint progr boost::optional<VAddr> addr = gpu.memory_manager->GpuToCpuAddress(buffer.address); - std::vector<u8> data; + size_t size = 0; + if (used_buffer.IsIndirect()) { // Buffer is accessed indirectly, so upload the entire thing - data.resize(buffer.size * sizeof(float)); + size = buffer.size * sizeof(float); + + if (size > MaxConstbufferSize) { + LOG_ERROR(HW_GPU, "indirect constbuffer size {} exceeds maximum {}", size, + MaxConstbufferSize); + size = MaxConstbufferSize; + } } else { // Buffer is accessed directly, upload just what we use - data.resize(used_buffer.GetSize() * sizeof(float)); + size = used_buffer.GetSize() * sizeof(float); } + // Align the actual size so it ends up being a multiple of vec4 to meet the OpenGL std140 + // UBO alignment requirements. + size = Common::AlignUp(size, sizeof(GLvec4)); + ASSERT_MSG(size <= MaxConstbufferSize, "Constbuffer too big"); + + std::vector<u8> data(size); Memory::ReadBlock(*addr, data.data(), data.size()); - glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer_draw_state.ssbo); - glBufferData(GL_SHADER_STORAGE_BUFFER, data.size(), data.data(), GL_DYNAMIC_DRAW); - glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + glBindBuffer(GL_UNIFORM_BUFFER, buffer_draw_state.ssbo); + glBufferData(GL_UNIFORM_BUFFER, data.size(), data.data(), GL_DYNAMIC_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); // Now configure the bindpoint of the buffer inside the shader std::string buffer_name = used_buffer.GetName(); - GLuint index = - glGetProgramResourceIndex(program, GL_SHADER_STORAGE_BLOCK, buffer_name.c_str()); + GLuint index = glGetProgramResourceIndex(program, GL_UNIFORM_BLOCK, buffer_name.c_str()); if (index != -1) - glShaderStorageBlockBinding(program, index, buffer_draw_state.bindpoint); + glUniformBlockBinding(program, index, buffer_draw_state.bindpoint); } state.Apply(); - return current_bindpoint + entries.size(); + return current_bindpoint + static_cast<u32>(entries.size()); } u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, GLuint program, u32 current_unit, @@ -653,16 +686,23 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, GLuint program, // Bind the uniform to the sampler. GLint uniform = glGetUniformLocation(program, entry.GetName().c_str()); - ASSERT(uniform != -1); + if (uniform == -1) { + continue; + } + glProgramUniform1i(program, uniform, current_bindpoint); const auto texture = maxwell3d.GetStageTexture(entry.GetStage(), entry.GetOffset()); - ASSERT(texture.enabled); + + if (!texture.enabled) { + state.texture_units[current_bindpoint].texture_2d = 0; + continue; + } texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc); Surface surface = res_cache.GetTextureSurface(texture); if (surface != nullptr) { - state.texture_units[current_bindpoint].texture_2d = surface->texture.handle; + state.texture_units[current_bindpoint].texture_2d = surface->Texture().handle; state.texture_units[current_bindpoint].swizzle.r = MaxwellToGL::SwizzleSource(texture.tic.x_source); state.texture_units[current_bindpoint].swizzle.g = @@ -679,7 +719,7 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, GLuint program, state.Apply(); - return current_unit + entries.size(); + return current_unit + static_cast<u32>(entries.size()); } void RasterizerOpenGL::BindFramebufferSurfaces(const Surface& color_surface, @@ -688,16 +728,16 @@ void RasterizerOpenGL::BindFramebufferSurfaces(const Surface& color_surface, state.Apply(); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - color_surface != nullptr ? color_surface->texture.handle : 0, 0); + color_surface != nullptr ? color_surface->Texture().handle : 0, 0); if (depth_surface != nullptr) { if (has_stencil) { // attach both depth and stencil glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, - depth_surface->texture.handle, 0); + depth_surface->Texture().handle, 0); } else { // attach depth glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, - depth_surface->texture.handle, 0); + depth_surface->Texture().handle, 0); // clear stencil attachment glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); } @@ -708,14 +748,14 @@ void RasterizerOpenGL::BindFramebufferSurfaces(const Surface& color_surface, } } -void RasterizerOpenGL::SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect, u16 res_scale) { +void RasterizerOpenGL::SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect) { const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()}; - state.viewport.x = static_cast<GLint>(surfaces_rect.left) + viewport_rect.left * res_scale; - state.viewport.y = static_cast<GLint>(surfaces_rect.bottom) + viewport_rect.bottom * res_scale; - state.viewport.width = static_cast<GLsizei>(viewport_rect.GetWidth() * res_scale); - state.viewport.height = static_cast<GLsizei>(viewport_rect.GetHeight() * res_scale); + state.viewport.x = static_cast<GLint>(surfaces_rect.left) + viewport_rect.left; + state.viewport.y = static_cast<GLint>(surfaces_rect.bottom) + viewport_rect.bottom; + state.viewport.width = static_cast<GLsizei>(viewport_rect.GetWidth()); + state.viewport.height = static_cast<GLsizei>(viewport_rect.GetHeight()); } void RasterizerOpenGL::SyncClipEnabled() { @@ -727,7 +767,27 @@ void RasterizerOpenGL::SyncClipCoef() { } void RasterizerOpenGL::SyncCullMode() { - UNREACHABLE(); + const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; + + state.cull.enabled = regs.cull.enabled != 0; + + if (state.cull.enabled) { + state.cull.front_face = MaxwellToGL::FrontFace(regs.cull.front_face); + state.cull.mode = MaxwellToGL::CullFace(regs.cull.cull_face); + + const bool flip_triangles{regs.screen_y_control.triangle_rast_flip == 0 || + regs.viewport_transform[0].scale_y < 0.0f}; + + // If the GPU is configured to flip the rasterized triangles, then we need to flip the + // notion of front and back. Note: We flip the triangles when the value of the register is 0 + // because OpenGL already does it for us. + if (flip_triangles) { + if (state.cull.front_face == GL_CCW) + state.cull.front_face = GL_CW; + else if (state.cull.front_face == GL_CW) + state.cull.front_face = GL_CCW; + } + } } void RasterizerOpenGL::SyncDepthScale() { @@ -738,9 +798,20 @@ void RasterizerOpenGL::SyncDepthOffset() { UNREACHABLE(); } +void RasterizerOpenGL::SyncDepthTestState() { + const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; + + state.depth.test_enabled = regs.depth_test_enable != 0; + state.depth.write_mask = regs.depth_write_enabled ? GL_TRUE : GL_FALSE; + + if (!state.depth.test_enabled) + return; + + state.depth.test_func = MaxwellToGL::ComparisonOp(regs.depth_test_func); +} + void RasterizerOpenGL::SyncBlendState() { const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; - ASSERT_MSG(regs.independent_blend_enable == 1, "Only independent blending is implemented"); // TODO(Subv): Support more than just render target 0. state.blend.enabled = regs.blend.enable[0] != 0; @@ -748,6 +819,7 @@ void RasterizerOpenGL::SyncBlendState() { if (!state.blend.enabled) return; + ASSERT_MSG(regs.independent_blend_enable == 1, "Only independent blending is implemented"); ASSERT_MSG(!regs.independent_blend[0].separate_alpha, "Unimplemented"); state.blend.rgb_equation = MaxwellToGL::BlendEquation(regs.independent_blend[0].equation_rgb); state.blend.src_rgb_func = MaxwellToGL::BlendFunc(regs.independent_blend[0].factor_source_rgb); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index b7c8cf843a..c406142e49 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -7,6 +7,7 @@ #include <array> #include <cstddef> #include <memory> +#include <utility> #include <vector> #include <glad/glad.h> #include "common/common_types.h" @@ -28,6 +29,7 @@ public: ~RasterizerOpenGL() override; void DrawArrays() override; + void Clear() override; void NotifyMaxwellRegisterChanged(u32 method) override; void FlushAll() override; void FlushRegion(Tegra::GPUVAddr addr, u64 size) override; @@ -54,6 +56,11 @@ public: OGLShader shader; }; + /// Maximum supported size that a constbuffer can have in bytes. + static constexpr size_t MaxConstbufferSize = 0x10000; + static_assert(MaxConstbufferSize % sizeof(GLvec4) == 0, + "The maximum size of a constbuffer must be a multiple of the size of GLvec4"); + private: class SamplerInfo { public: @@ -76,6 +83,10 @@ private: u32 border_color_a; }; + /// Configures the color and depth framebuffer states and returns the dirty <Color, Depth> + /// surfaces if writing was enabled. + std::pair<Surface, Surface> ConfigureFramebuffers(bool using_color_fb, bool using_depth_fb); + /// Binds the framebuffer color and depth surface void BindFramebufferSurfaces(const Surface& color_surface, const Surface& depth_surface, bool has_stencil); @@ -104,7 +115,7 @@ private: u32 current_unit, const std::vector<GLShader::SamplerEntry>& entries); /// Syncs the viewport to match the guest state - void SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect, u16 res_scale); + void SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect); /// Syncs the clip enabled status to match the guest state void SyncClipEnabled(); @@ -121,6 +132,9 @@ private: /// Syncs the depth offset to match the guest state void SyncDepthOffset(); + /// Syncs the depth test state to match the guest state + void SyncDepthTestState(); + /// Syncs the blend state to match the guest state void SyncBlendState(); diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index ff48a2669b..323ff7408e 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -1,36 +1,23 @@ -// Copyright 2015 Citra Emulator Project +// Copyright 2018 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include <algorithm> -#include <atomic> -#include <cstring> -#include <iterator> -#include <memory> -#include <utility> -#include <vector> -#include <boost/optional.hpp> -#include <boost/range/iterator_range.hpp> #include <glad/glad.h> + #include "common/alignment.h" -#include "common/bit_field.h" -#include "common/color.h" -#include "common/logging/log.h" -#include "common/math_util.h" +#include "common/assert.h" #include "common/microprofile.h" #include "common/scope_exit.h" #include "core/core.h" -#include "core/frontend/emu_window.h" #include "core/hle/kernel/process.h" -#include "core/hle/kernel/vm_manager.h" #include "core/memory.h" #include "core/settings.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/renderer_opengl/gl_rasterizer_cache.h" -#include "video_core/renderer_opengl/gl_state.h" +#include "video_core/textures/astc.h" #include "video_core/textures/decoders.h" #include "video_core/utils.h" -#include "video_core/video_core.h" using SurfaceType = SurfaceParams::SurfaceType; using PixelFormat = SurfaceParams::PixelFormat; @@ -40,89 +27,178 @@ struct FormatTuple { GLint internal_format; GLenum format; GLenum type; + ComponentType component_type; bool compressed; }; +/*static*/ SurfaceParams SurfaceParams::CreateForTexture( + const Tegra::Texture::FullTextureInfo& config) { + + SurfaceParams params{}; + params.addr = config.tic.Address(); + params.is_tiled = config.tic.IsTiled(); + params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0, + params.pixel_format = PixelFormatFromTextureFormat(config.tic.format); + params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value()); + params.type = GetFormatType(params.pixel_format); + params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format)); + params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format)); + params.unaligned_height = config.tic.Height(); + params.size_in_bytes = params.SizeInBytes(); + return params; +} + +/*static*/ SurfaceParams SurfaceParams::CreateForFramebuffer( + const Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig& config) { + + SurfaceParams params{}; + params.addr = config.Address(); + params.is_tiled = true; + params.block_height = Tegra::Texture::TICEntry::DefaultBlockHeight; + params.pixel_format = PixelFormatFromRenderTargetFormat(config.format); + params.component_type = ComponentTypeFromRenderTarget(config.format); + params.type = GetFormatType(params.pixel_format); + params.width = config.width; + params.height = config.height; + params.unaligned_height = config.height; + params.size_in_bytes = params.SizeInBytes(); + return params; +} + +/*static*/ SurfaceParams SurfaceParams::CreateForDepthBuffer( + const Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig& config, Tegra::GPUVAddr zeta_address, + Tegra::DepthFormat format) { + + SurfaceParams params{}; + params.addr = zeta_address; + params.is_tiled = true; + params.block_height = Tegra::Texture::TICEntry::DefaultBlockHeight; + params.pixel_format = PixelFormatFromDepthFormat(format); + params.component_type = ComponentTypeFromDepthFormat(format); + params.type = GetFormatType(params.pixel_format); + params.size_in_bytes = params.SizeInBytes(); + params.width = config.width; + params.height = config.height; + params.unaligned_height = config.height; + params.size_in_bytes = params.SizeInBytes(); + return params; +} + static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_format_tuples = {{ - {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, false}, // ABGR8 - {GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, false}, // B5G6R5 - {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, false}, // A2B10G10R10 - {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, false}, // A1B5G5R5 - {GL_R8, GL_RED, GL_UNSIGNED_BYTE, false}, // R8 - {GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, false}, // RGBA16F - {GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, false}, // R11FG11FB10F - {GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT1 - {GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT23 - {GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT45 - {GL_COMPRESSED_RED_RGTC1, GL_RED, GL_UNSIGNED_INT_8_8_8_8, true}, // DXN1 + {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // ABGR8 + {GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV, ComponentType::UNorm, false}, // B5G6R5 + {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, ComponentType::UNorm, + false}, // A2B10G10R10 + {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, ComponentType::UNorm, false}, // A1B5G5R5 + {GL_R8, GL_RED, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // R8 + {GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, ComponentType::Float, false}, // RGBA16F + {GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, ComponentType::Float, + false}, // R11FG11FB10F + {GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RGBA32UI + {GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, + true}, // DXT1 + {GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, + true}, // DXT23 + {GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, + true}, // DXT45 + {GL_COMPRESSED_RED_RGTC1, GL_RED, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, true}, // DXN1 + {GL_COMPRESSED_RGBA_BPTC_UNORM_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, + true}, // BC7U + {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4 + + // DepthStencil formats + {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, ComponentType::UNorm, + false}, // Z24S8 + {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, ComponentType::UNorm, + false}, // S8Z24 + {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F }}; static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType component_type) { - const SurfaceType type = SurfaceParams::GetFormatType(pixel_format); - if (type == SurfaceType::ColorTexture) { - ASSERT(static_cast<size_t>(pixel_format) < tex_format_tuples.size()); - // For now only UNORM components are supported, or either R11FG11FB10F or RGBA16F which are - // type FLOAT - ASSERT(component_type == ComponentType::UNorm || pixel_format == PixelFormat::RGBA16F || - pixel_format == PixelFormat::R11FG11FB10F); - return tex_format_tuples[static_cast<unsigned int>(pixel_format)]; - } else if (type == SurfaceType::Depth || type == SurfaceType::DepthStencil) { - // TODO(Subv): Implement depth formats - ASSERT_MSG(false, "Unimplemented"); - } + ASSERT(static_cast<size_t>(pixel_format) < tex_format_tuples.size()); + auto& format = tex_format_tuples[static_cast<unsigned int>(pixel_format)]; + ASSERT(component_type == format.component_type); - UNREACHABLE(); - return {}; + return format; } -template <typename Map, typename Interval> -constexpr auto RangeFromInterval(Map& map, const Interval& interval) { - return boost::make_iterator_range(map.equal_range(interval)); +VAddr SurfaceParams::GetCpuAddr() const { + const auto& gpu = Core::System::GetInstance().GPU(); + return *gpu.memory_manager->GpuToCpuAddress(addr); } -static u16 GetResolutionScaleFactor() { - return static_cast<u16>(!Settings::values.resolution_factor - ? VideoCore::g_emu_window->GetFramebufferLayout().GetScalingRatio() - : Settings::values.resolution_factor); +static bool IsPixelFormatASTC(PixelFormat format) { + switch (format) { + case PixelFormat::ASTC_2D_4X4: + return true; + default: + return false; + } +} + +static std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) { + switch (format) { + case PixelFormat::ASTC_2D_4X4: + return {4, 4}; + default: + LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format)); + UNREACHABLE(); + } +} + +MathUtil::Rectangle<u32> SurfaceParams::GetRect() const { + u32 actual_height{unaligned_height}; + if (IsPixelFormatASTC(pixel_format)) { + // ASTC formats must stop at the ATSC block size boundary + actual_height = Common::AlignDown(actual_height, GetASTCBlockSize(pixel_format).second); + } + return {0, actual_height, width, 0}; } template <bool morton_to_gl, PixelFormat format> -void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, Tegra::GPUVAddr base, - Tegra::GPUVAddr start, Tegra::GPUVAddr end) { +void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, Tegra::GPUVAddr addr) { constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT; constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format); const auto& gpu = Core::System::GetInstance().GPU(); if (morton_to_gl) { - auto data = Tegra::Texture::UnswizzleTexture( - *gpu.memory_manager->GpuToCpuAddress(base), - SurfaceParams::TextureFormatFromPixelFormat(format), stride, height, block_height); - std::memcpy(gl_buffer, data.data(), data.size()); + if (SurfaceParams::GetFormatType(format) == SurfaceType::ColorTexture) { + auto data = Tegra::Texture::UnswizzleTexture( + *gpu.memory_manager->GpuToCpuAddress(addr), + SurfaceParams::TextureFormatFromPixelFormat(format), stride, height, block_height); + std::memcpy(gl_buffer, data.data(), data.size()); + } else { + auto data = Tegra::Texture::UnswizzleDepthTexture( + *gpu.memory_manager->GpuToCpuAddress(addr), + SurfaceParams::DepthFormatFromPixelFormat(format), stride, height, block_height); + std::memcpy(gl_buffer, data.data(), data.size()); + } } else { - // TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should check - // the configuration for this and perform more generic un/swizzle - NGLOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!"); + // TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should + // check the configuration for this and perform more generic un/swizzle + LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!"); VideoCore::MortonCopyPixels128( stride, height, bytes_per_pixel, gl_bytes_per_pixel, - Memory::GetPointer(*gpu.memory_manager->GpuToCpuAddress(base)), gl_buffer, + Memory::GetPointer(*gpu.memory_manager->GpuToCpuAddress(addr)), gl_buffer, morton_to_gl); } } -static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr, Tegra::GPUVAddr, - Tegra::GPUVAddr), +static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr), SurfaceParams::MaxPixelFormat> morton_to_gl_fns = { MortonCopy<true, PixelFormat::ABGR8>, MortonCopy<true, PixelFormat::B5G6R5>, MortonCopy<true, PixelFormat::A2B10G10R10>, MortonCopy<true, PixelFormat::A1B5G5R5>, MortonCopy<true, PixelFormat::R8>, MortonCopy<true, PixelFormat::RGBA16F>, - MortonCopy<true, PixelFormat::R11FG11FB10F>, MortonCopy<true, PixelFormat::DXT1>, - MortonCopy<true, PixelFormat::DXT23>, MortonCopy<true, PixelFormat::DXT45>, - MortonCopy<true, PixelFormat::DXN1>, + MortonCopy<true, PixelFormat::R11FG11FB10F>, MortonCopy<true, PixelFormat::RGBA32UI>, + MortonCopy<true, PixelFormat::DXT1>, MortonCopy<true, PixelFormat::DXT23>, + MortonCopy<true, PixelFormat::DXT45>, MortonCopy<true, PixelFormat::DXN1>, + MortonCopy<true, PixelFormat::BC7U>, MortonCopy<true, PixelFormat::ASTC_2D_4X4>, + MortonCopy<true, PixelFormat::Z24S8>, MortonCopy<true, PixelFormat::S8Z24>, + MortonCopy<true, PixelFormat::Z32F>, }; -static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr, Tegra::GPUVAddr, - Tegra::GPUVAddr), +static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr), SurfaceParams::MaxPixelFormat> gl_to_morton_fns = { MortonCopy<false, PixelFormat::ABGR8>, @@ -132,11 +208,17 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr, Tegra: MortonCopy<false, PixelFormat::R8>, MortonCopy<false, PixelFormat::RGBA16F>, MortonCopy<false, PixelFormat::R11FG11FB10F>, - // TODO(Subv): Swizzling the DXT1/DXT23/DXT45/DXN1 formats is not yet supported + MortonCopy<false, PixelFormat::RGBA32UI>, + // TODO(Subv): Swizzling the DXT1/DXT23/DXT45/DXN1/BC7U formats is not yet supported + nullptr, nullptr, nullptr, nullptr, nullptr, + MortonCopy<false, PixelFormat::ABGR8>, + MortonCopy<false, PixelFormat::Z24S8>, + MortonCopy<false, PixelFormat::S8Z24>, + MortonCopy<false, PixelFormat::Z32F>, }; // Allocate an uninitialized texture of appropriate size and format for the surface @@ -166,374 +248,144 @@ static void AllocateSurfaceTexture(GLuint texture, const FormatTuple& format_tup cur_state.Apply(); } -static bool BlitTextures(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rect, GLuint dst_tex, - const MathUtil::Rectangle<u32>& dst_rect, SurfaceType type, - GLuint read_fb_handle, GLuint draw_fb_handle) { - - glCopyImageSubData(src_tex, GL_TEXTURE_2D, 0, src_rect.left, src_rect.bottom, 0, dst_tex, - GL_TEXTURE_2D, 0, dst_rect.left, dst_rect.bottom, 0, src_rect.GetWidth(), - src_rect.GetHeight(), 0); - return true; -} - -static bool FillSurface(const Surface& surface, const u8* fill_data, - const MathUtil::Rectangle<u32>& fill_rect, GLuint draw_fb_handle) { - UNREACHABLE(); - return {}; -} - -SurfaceParams SurfaceParams::FromInterval(SurfaceInterval interval) const { - SurfaceParams params = *this; - const u32 tiled_size = is_tiled ? 8 : 1; - const u64 stride_tiled_bytes = BytesInPixels(stride * tiled_size); - Tegra::GPUVAddr aligned_start = - addr + Common::AlignDown(boost::icl::first(interval) - addr, stride_tiled_bytes); - Tegra::GPUVAddr aligned_end = - addr + Common::AlignUp(boost::icl::last_next(interval) - addr, stride_tiled_bytes); - - if (aligned_end - aligned_start > stride_tiled_bytes) { - params.addr = aligned_start; - params.height = static_cast<u32>((aligned_end - aligned_start) / BytesInPixels(stride)); - } else { - // 1 row - ASSERT(aligned_end - aligned_start == stride_tiled_bytes); - const u64 tiled_alignment = BytesInPixels(is_tiled ? 8 * 8 : 1); - aligned_start = - addr + Common::AlignDown(boost::icl::first(interval) - addr, tiled_alignment); - aligned_end = - addr + Common::AlignUp(boost::icl::last_next(interval) - addr, tiled_alignment); - params.addr = aligned_start; - params.width = static_cast<u32>(PixelsInBytes(aligned_end - aligned_start) / tiled_size); - params.stride = params.width; - params.height = tiled_size; - } - params.UpdateParams(); - - return params; -} - -SurfaceInterval SurfaceParams::GetSubRectInterval(MathUtil::Rectangle<u32> unscaled_rect) const { - if (unscaled_rect.GetHeight() == 0 || unscaled_rect.GetWidth() == 0) { - return {}; - } - - if (is_tiled) { - unscaled_rect.left = Common::AlignDown(unscaled_rect.left, 8) * 8; - unscaled_rect.bottom = Common::AlignDown(unscaled_rect.bottom, 8) / 8; - unscaled_rect.right = Common::AlignUp(unscaled_rect.right, 8) * 8; - unscaled_rect.top = Common::AlignUp(unscaled_rect.top, 8) / 8; - } - - const u32 stride_tiled = !is_tiled ? stride : stride * 8; - - const u32 pixel_offset = - stride_tiled * (!is_tiled ? unscaled_rect.bottom : (height / 8) - unscaled_rect.top) + - unscaled_rect.left; - - const u32 pixels = (unscaled_rect.GetHeight() - 1) * stride_tiled + unscaled_rect.GetWidth(); - - return {addr + BytesInPixels(pixel_offset), addr + BytesInPixels(pixel_offset + pixels)}; -} - -MathUtil::Rectangle<u32> SurfaceParams::GetSubRect(const SurfaceParams& sub_surface) const { - const u32 begin_pixel_index = static_cast<u32>(PixelsInBytes(sub_surface.addr - addr)); - - if (is_tiled) { - const int x0 = (begin_pixel_index % (stride * 8)) / 8; - const int y0 = (begin_pixel_index / (stride * 8)) * 8; - // Top to bottom - return MathUtil::Rectangle<u32>(x0, height - y0, x0 + sub_surface.width, - height - (y0 + sub_surface.height)); - } - - const int x0 = begin_pixel_index % stride; - const int y0 = begin_pixel_index / stride; - // Bottom to top - return MathUtil::Rectangle<u32>(x0, y0 + sub_surface.height, x0 + sub_surface.width, y0); -} - -MathUtil::Rectangle<u32> SurfaceParams::GetScaledSubRect(const SurfaceParams& sub_surface) const { - auto rect = GetSubRect(sub_surface); - rect.left = rect.left * res_scale; - rect.right = rect.right * res_scale; - rect.top = rect.top * res_scale; - rect.bottom = rect.bottom * res_scale; - return rect; -} - -bool SurfaceParams::ExactMatch(const SurfaceParams& other_surface) const { - return std::tie(other_surface.addr, other_surface.width, other_surface.height, - other_surface.stride, other_surface.block_height, other_surface.pixel_format, - other_surface.component_type, - other_surface.is_tiled) == std::tie(addr, width, height, stride, block_height, - pixel_format, component_type, is_tiled) && - pixel_format != PixelFormat::Invalid; -} - -bool SurfaceParams::CanSubRect(const SurfaceParams& sub_surface) const { - return sub_surface.addr >= addr && sub_surface.end <= end && - sub_surface.pixel_format == pixel_format && pixel_format != PixelFormat::Invalid && - sub_surface.is_tiled == is_tiled && sub_surface.block_height == block_height && - sub_surface.component_type == component_type && - (sub_surface.addr - addr) % BytesInPixels(is_tiled ? 64 : 1) == 0 && - (sub_surface.stride == stride || sub_surface.height <= (is_tiled ? 8u : 1u)) && - GetSubRect(sub_surface).left + sub_surface.width <= stride; -} - -bool SurfaceParams::CanExpand(const SurfaceParams& expanded_surface) const { - return pixel_format != PixelFormat::Invalid && pixel_format == expanded_surface.pixel_format && - addr <= expanded_surface.end && expanded_surface.addr <= end && - is_tiled == expanded_surface.is_tiled && block_height == expanded_surface.block_height && - component_type == expanded_surface.component_type && stride == expanded_surface.stride && - (std::max(expanded_surface.addr, addr) - std::min(expanded_surface.addr, addr)) % - BytesInPixels(stride * (is_tiled ? 8 : 1)) == - 0; -} - -bool SurfaceParams::CanTexCopy(const SurfaceParams& texcopy_params) const { - if (pixel_format == PixelFormat::Invalid || addr > texcopy_params.addr || - end < texcopy_params.end) { - return false; - } - if (texcopy_params.block_height != block_height || - texcopy_params.component_type != component_type) - return false; - - if (texcopy_params.width != texcopy_params.stride) { - const u32 tile_stride = static_cast<u32>(BytesInPixels(stride * (is_tiled ? 8 : 1))); - return (texcopy_params.addr - addr) % BytesInPixels(is_tiled ? 64 : 1) == 0 && - texcopy_params.width % BytesInPixels(is_tiled ? 64 : 1) == 0 && - (texcopy_params.height == 1 || texcopy_params.stride == tile_stride) && - ((texcopy_params.addr - addr) % tile_stride) + texcopy_params.width <= tile_stride; - } - return FromInterval(texcopy_params.GetInterval()).GetInterval() == texcopy_params.GetInterval(); -} - -VAddr SurfaceParams::GetCpuAddr() const { - // When this function is used, only cpu_addr or (GPU) addr should be set, not both - ASSERT(!(cpu_addr && addr)); - const auto& gpu = Core::System::GetInstance().GPU(); - return cpu_addr.get_value_or(*gpu.memory_manager->GpuToCpuAddress(addr)); -} - -bool CachedSurface::CanFill(const SurfaceParams& dest_surface, - SurfaceInterval fill_interval) const { - if (type == SurfaceType::Fill && IsRegionValid(fill_interval) && - boost::icl::first(fill_interval) >= addr && - boost::icl::last_next(fill_interval) <= end && // dest_surface is within our fill range - dest_surface.FromInterval(fill_interval).GetInterval() == - fill_interval) { // make sure interval is a rectangle in dest surface - if (fill_size * CHAR_BIT != dest_surface.GetFormatBpp()) { - // Check if bits repeat for our fill_size - const u32 dest_bytes_per_pixel = std::max(dest_surface.GetFormatBpp() / CHAR_BIT, 1u); - std::vector<u8> fill_test(fill_size * dest_bytes_per_pixel); - - for (u32 i = 0; i < dest_bytes_per_pixel; ++i) - std::memcpy(&fill_test[i * fill_size], &fill_data[0], fill_size); - - for (u32 i = 0; i < fill_size; ++i) - if (std::memcmp(&fill_test[dest_bytes_per_pixel * i], &fill_test[0], - dest_bytes_per_pixel) != 0) - return false; - - if (dest_surface.GetFormatBpp() == 4 && (fill_test[0] & 0xF) != (fill_test[0] >> 4)) - return false; +CachedSurface::CachedSurface(const SurfaceParams& params) : params(params) { + texture.Create(); + const auto& rect{params.GetRect()}; + AllocateSurfaceTexture(texture.handle, + GetFormatTuple(params.pixel_format, params.component_type), + rect.GetWidth(), rect.GetHeight()); +} + +static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) { + union S8Z24 { + BitField<0, 24, u32> z24; + BitField<24, 8, u32> s8; + }; + static_assert(sizeof(S8Z24) == 4, "S8Z24 is incorrect size"); + + union Z24S8 { + BitField<0, 8, u32> s8; + BitField<8, 24, u32> z24; + }; + static_assert(sizeof(Z24S8) == 4, "Z24S8 is incorrect size"); + + S8Z24 input_pixel{}; + Z24S8 output_pixel{}; + for (size_t y = 0; y < height; ++y) { + for (size_t x = 0; x < width; ++x) { + const size_t offset{y * width + x}; + std::memcpy(&input_pixel, &data[offset], sizeof(S8Z24)); + output_pixel.s8.Assign(input_pixel.s8); + output_pixel.z24.Assign(input_pixel.z24); + std::memcpy(&data[offset], &output_pixel, sizeof(Z24S8)); } - return true; } - return false; -} - -bool CachedSurface::CanCopy(const SurfaceParams& dest_surface, - SurfaceInterval copy_interval) const { - SurfaceParams subrect_params = dest_surface.FromInterval(copy_interval); - ASSERT(subrect_params.GetInterval() == copy_interval); - if (CanSubRect(subrect_params)) - return true; - - if (CanFill(dest_surface, copy_interval)) - return true; - - return false; } - -SurfaceInterval SurfaceParams::GetCopyableInterval(const Surface& src_surface) const { - SurfaceInterval result{}; - const auto valid_regions = - SurfaceRegions(GetInterval() & src_surface->GetInterval()) - src_surface->invalid_regions; - for (auto& valid_interval : valid_regions) { - const SurfaceInterval aligned_interval{ - addr + Common::AlignUp(boost::icl::first(valid_interval) - addr, - BytesInPixels(is_tiled ? 8 * 8 : 1)), - addr + Common::AlignDown(boost::icl::last_next(valid_interval) - addr, - BytesInPixels(is_tiled ? 8 * 8 : 1))}; - - if (BytesInPixels(is_tiled ? 8 * 8 : 1) > boost::icl::length(valid_interval) || - boost::icl::length(aligned_interval) == 0) { - continue; - } - - // Get the rectangle within aligned_interval - const u32 stride_bytes = static_cast<u32>(BytesInPixels(stride)) * (is_tiled ? 8 : 1); - SurfaceInterval rect_interval{ - addr + Common::AlignUp(boost::icl::first(aligned_interval) - addr, stride_bytes), - addr + Common::AlignDown(boost::icl::last_next(aligned_interval) - addr, stride_bytes), - }; - if (boost::icl::first(rect_interval) > boost::icl::last_next(rect_interval)) { - // 1 row - rect_interval = aligned_interval; - } else if (boost::icl::length(rect_interval) == 0) { - // 2 rows that do not make a rectangle, return the larger one - const SurfaceInterval row1{boost::icl::first(aligned_interval), - boost::icl::first(rect_interval)}; - const SurfaceInterval row2{boost::icl::first(rect_interval), - boost::icl::last_next(aligned_interval)}; - rect_interval = (boost::icl::length(row1) > boost::icl::length(row2)) ? row1 : row2; - } - - if (boost::icl::length(rect_interval) > boost::icl::length(result)) { - result = rect_interval; - } +/** + * Helper function to perform software conversion (as needed) when loading a buffer from Switch + * memory. This is for Maxwell pixel formats that cannot be represented as-is in OpenGL or with + * typical desktop GPUs. + */ +static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelFormat pixel_format, + u32 width, u32 height) { + switch (pixel_format) { + case PixelFormat::ASTC_2D_4X4: { + // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC. + u32 block_width{}; + u32 block_height{}; + std::tie(block_width, block_height) = GetASTCBlockSize(pixel_format); + data = Tegra::Texture::ASTC::Decompress(data, width, height, block_width, block_height); + break; + } + case PixelFormat::S8Z24: + // Convert the S8Z24 depth format to Z24S8, as OpenGL does not support S8Z24. + ConvertS8Z24ToZ24S8(data, width, height); + break; + } +} + +/** + * Helper function to perform software conversion (as needed) when flushing a buffer to Switch + * memory. This is for Maxwell pixel formats that cannot be represented as-is in OpenGL or with + * typical desktop GPUs. + */ +static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& /*data*/, PixelFormat pixel_format, + u32 /*width*/, u32 /*height*/) { + switch (pixel_format) { + case PixelFormat::ASTC_2D_4X4: + case PixelFormat::S8Z24: + LOG_CRITICAL(Render_OpenGL, "Unimplemented pixel_format={}", + static_cast<u32>(pixel_format)); + UNREACHABLE(); + break; } - return result; -} - -void RasterizerCacheOpenGL::CopySurface(const Surface& src_surface, const Surface& dst_surface, - SurfaceInterval copy_interval) { - SurfaceParams subrect_params = dst_surface->FromInterval(copy_interval); - ASSERT(subrect_params.GetInterval() == copy_interval); - - ASSERT(src_surface != dst_surface); - - // This is only called when CanCopy is true, no need to run checks here - if (src_surface->type == SurfaceType::Fill) { - // FillSurface needs a 4 bytes buffer - const u64 fill_offset = - (boost::icl::first(copy_interval) - src_surface->addr) % src_surface->fill_size; - std::array<u8, 4> fill_buffer; - - u64 fill_buff_pos = fill_offset; - for (int i : {0, 1, 2, 3}) - fill_buffer[i] = src_surface->fill_data[fill_buff_pos++ % src_surface->fill_size]; - - FillSurface(dst_surface, &fill_buffer[0], dst_surface->GetScaledSubRect(subrect_params), - draw_framebuffer.handle); - return; - } - if (src_surface->CanSubRect(subrect_params)) { - BlitTextures(src_surface->texture.handle, src_surface->GetScaledSubRect(subrect_params), - dst_surface->texture.handle, dst_surface->GetScaledSubRect(subrect_params), - src_surface->type, read_framebuffer.handle, draw_framebuffer.handle); - return; - } - UNREACHABLE(); } MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192)); -void CachedSurface::LoadGLBuffer(Tegra::GPUVAddr load_start, Tegra::GPUVAddr load_end) { - ASSERT(type != SurfaceType::Fill); +void CachedSurface::LoadGLBuffer() { + ASSERT(params.type != SurfaceType::Fill); - u8* const texture_src_data = Memory::GetPointer(GetCpuAddr()); - if (texture_src_data == nullptr) - return; + u8* const texture_src_data = Memory::GetPointer(params.GetCpuAddr()); - if (gl_buffer == nullptr) { - gl_buffer_size = GetActualWidth() * GetActualHeight() * GetGLBytesPerPixel(pixel_format); - gl_buffer.reset(new u8[gl_buffer_size]); - } + ASSERT(texture_src_data); - MICROPROFILE_SCOPE(OpenGL_SurfaceLoad); + gl_buffer.resize(params.width * params.height * GetGLBytesPerPixel(params.pixel_format)); - ASSERT(load_start >= addr && load_end <= end); - const u64 start_offset = load_start - addr; + MICROPROFILE_SCOPE(OpenGL_SurfaceLoad); - if (!is_tiled) { - const u32 bytes_per_pixel{GetFormatBpp() >> 3}; + if (!params.is_tiled) { + const u32 bytes_per_pixel{params.GetFormatBpp() >> 3}; - std::memcpy(&gl_buffer[start_offset], texture_src_data + start_offset, - bytes_per_pixel * width * height); + std::memcpy(gl_buffer.data(), texture_src_data, + bytes_per_pixel * params.width * params.height); } else { - morton_to_gl_fns[static_cast<size_t>(pixel_format)](GetActualWidth(), block_height, - GetActualHeight(), &gl_buffer[0], addr, - load_start, load_end); + morton_to_gl_fns[static_cast<size_t>(params.pixel_format)]( + params.width, params.block_height, params.height, gl_buffer.data(), params.addr); } + + ConvertFormatAsNeeded_LoadGLBuffer(gl_buffer, params.pixel_format, params.width, params.height); } MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64)); -void CachedSurface::FlushGLBuffer(Tegra::GPUVAddr flush_start, Tegra::GPUVAddr flush_end) { - u8* const dst_buffer = Memory::GetPointer(GetCpuAddr()); - if (dst_buffer == nullptr) - return; - - ASSERT(gl_buffer_size == width * height * GetGLBytesPerPixel(pixel_format)); +void CachedSurface::FlushGLBuffer() { + u8* const dst_buffer = Memory::GetPointer(params.GetCpuAddr()); - // TODO: Should probably be done in ::Memory:: and check for other regions too - // same as loadglbuffer() - if (flush_start < Memory::VRAM_VADDR_END && flush_end > Memory::VRAM_VADDR_END) - flush_end = Memory::VRAM_VADDR_END; - - if (flush_start < Memory::VRAM_VADDR && flush_end > Memory::VRAM_VADDR) - flush_start = Memory::VRAM_VADDR; + ASSERT(dst_buffer); + ASSERT(gl_buffer.size() == + params.width * params.height * GetGLBytesPerPixel(params.pixel_format)); MICROPROFILE_SCOPE(OpenGL_SurfaceFlush); - ASSERT(flush_start >= addr && flush_end <= end); - const u64 start_offset = flush_start - addr; - const u64 end_offset = flush_end - addr; - - if (type == SurfaceType::Fill) { - const u64 coarse_start_offset = start_offset - (start_offset % fill_size); - const u64 backup_bytes = start_offset % fill_size; - std::array<u8, 4> backup_data; - if (backup_bytes) - std::memcpy(&backup_data[0], &dst_buffer[coarse_start_offset], backup_bytes); - - for (u64 offset = coarse_start_offset; offset < end_offset; offset += fill_size) { - std::memcpy(&dst_buffer[offset], &fill_data[0], - std::min(fill_size, end_offset - offset)); - } + ConvertFormatAsNeeded_FlushGLBuffer(gl_buffer, params.pixel_format, params.width, + params.height); - if (backup_bytes) - std::memcpy(&dst_buffer[coarse_start_offset], &backup_data[0], backup_bytes); - } else if (!is_tiled) { - std::memcpy(dst_buffer + start_offset, &gl_buffer[start_offset], flush_end - flush_start); + if (!params.is_tiled) { + std::memcpy(dst_buffer, gl_buffer.data(), params.size_in_bytes); } else { - gl_to_morton_fns[static_cast<size_t>(pixel_format)]( - stride, block_height, height, &gl_buffer[0], addr, flush_start, flush_end); + gl_to_morton_fns[static_cast<size_t>(params.pixel_format)]( + params.width, params.block_height, params.height, gl_buffer.data(), params.addr); } } MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192)); -void CachedSurface::UploadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint read_fb_handle, - GLuint draw_fb_handle) { - if (type == SurfaceType::Fill) +void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle) { + if (params.type == SurfaceType::Fill) return; MICROPROFILE_SCOPE(OpenGL_TextureUL); - ASSERT(gl_buffer_size == - GetActualWidth() * GetActualHeight() * GetGLBytesPerPixel(pixel_format)); + ASSERT(gl_buffer.size() == + params.width * params.height * GetGLBytesPerPixel(params.pixel_format)); + + const auto& rect{params.GetRect()}; // Load data from memory to the surface GLint x0 = static_cast<GLint>(rect.left); GLint y0 = static_cast<GLint>(rect.bottom); - size_t buffer_offset = (y0 * stride + x0) * GetGLBytesPerPixel(pixel_format); + size_t buffer_offset = (y0 * params.width + x0) * GetGLBytesPerPixel(params.pixel_format); - const FormatTuple& tuple = GetFormatTuple(pixel_format, component_type); + const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); GLuint target_tex = texture.handle; - - // If not 1x scale, create 1x texture that we will blit from to replace texture subrect in - // surface - OGLTexture unscaled_tex; - if (res_scale != 1) { - x0 = 0; - y0 = 0; - - unscaled_tex.Create(); - AllocateSurfaceTexture(unscaled_tex.handle, tuple, rect.GetWidth(), rect.GetHeight()); - target_tex = unscaled_tex.handle; - } - OpenGLState cur_state = OpenGLState::GetCurState(); GLuint old_tex = cur_state.texture_units[0].texture_2d; @@ -541,15 +393,15 @@ void CachedSurface::UploadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint cur_state.Apply(); // Ensure no bad interactions with GL_UNPACK_ALIGNMENT - ASSERT(stride * GetGLBytesPerPixel(pixel_format) % 4 == 0); - glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(stride)); + ASSERT(params.width * GetGLBytesPerPixel(params.pixel_format) % 4 == 0); + glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.width)); glActiveTexture(GL_TEXTURE0); if (tuple.compressed) { - glCompressedTexImage2D(GL_TEXTURE_2D, 0, tuple.internal_format, - static_cast<GLsizei>(rect.GetWidth() * GetCompresssionFactor()), - static_cast<GLsizei>(rect.GetHeight() * GetCompresssionFactor()), 0, - size, &gl_buffer[buffer_offset]); + glCompressedTexImage2D( + GL_TEXTURE_2D, 0, tuple.internal_format, static_cast<GLsizei>(params.width), + static_cast<GLsizei>(params.height), 0, static_cast<GLsizei>(params.size_in_bytes), + &gl_buffer[buffer_offset]); } else { glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()), static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, @@ -560,845 +412,250 @@ void CachedSurface::UploadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint cur_state.texture_units[0].texture_2d = old_tex; cur_state.Apply(); - - if (res_scale != 1) { - auto scaled_rect = rect; - scaled_rect.left *= res_scale; - scaled_rect.top *= res_scale; - scaled_rect.right *= res_scale; - scaled_rect.bottom *= res_scale; - - BlitTextures(unscaled_tex.handle, {0, rect.GetHeight(), rect.GetWidth(), 0}, texture.handle, - scaled_rect, type, read_fb_handle, draw_fb_handle); - } } MICROPROFILE_DEFINE(OpenGL_TextureDL, "OpenGL", "Texture Download", MP_RGB(128, 192, 64)); -void CachedSurface::DownloadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint read_fb_handle, - GLuint draw_fb_handle) { - if (type == SurfaceType::Fill) +void CachedSurface::DownloadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle) { + if (params.type == SurfaceType::Fill) return; MICROPROFILE_SCOPE(OpenGL_TextureDL); - if (gl_buffer == nullptr) { - gl_buffer_size = width * height * GetGLBytesPerPixel(pixel_format); - gl_buffer.reset(new u8[gl_buffer_size]); - } + gl_buffer.resize(params.width * params.height * GetGLBytesPerPixel(params.pixel_format)); OpenGLState state = OpenGLState::GetCurState(); OpenGLState prev_state = state; SCOPE_EXIT({ prev_state.Apply(); }); - const FormatTuple& tuple = GetFormatTuple(pixel_format, component_type); + const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); // Ensure no bad interactions with GL_PACK_ALIGNMENT - ASSERT(stride * GetGLBytesPerPixel(pixel_format) % 4 == 0); - glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(stride)); - size_t buffer_offset = (rect.bottom * stride + rect.left) * GetGLBytesPerPixel(pixel_format); - - // If not 1x scale, blit scaled texture to a new 1x texture and use that to flush - if (res_scale != 1) { - auto scaled_rect = rect; - scaled_rect.left *= res_scale; - scaled_rect.top *= res_scale; - scaled_rect.right *= res_scale; - scaled_rect.bottom *= res_scale; - - OGLTexture unscaled_tex; - unscaled_tex.Create(); - - MathUtil::Rectangle<u32> unscaled_tex_rect{0, rect.GetHeight(), rect.GetWidth(), 0}; - AllocateSurfaceTexture(unscaled_tex.handle, tuple, rect.GetWidth(), rect.GetHeight()); - BlitTextures(texture.handle, scaled_rect, unscaled_tex.handle, unscaled_tex_rect, type, - read_fb_handle, draw_fb_handle); - - state.texture_units[0].texture_2d = unscaled_tex.handle; - state.Apply(); - - glActiveTexture(GL_TEXTURE0); - glGetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, &gl_buffer[buffer_offset]); - } else { - state.ResetTexture(texture.handle); - state.draw.read_framebuffer = read_fb_handle; - state.Apply(); - - if (type == SurfaceType::ColorTexture) { - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - texture.handle, 0); - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, - 0, 0); - } else if (type == SurfaceType::Depth) { - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, - texture.handle, 0); - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); - } else { - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, - texture.handle, 0); - } - glReadPixels(static_cast<GLint>(rect.left), static_cast<GLint>(rect.bottom), - static_cast<GLsizei>(rect.GetWidth()), static_cast<GLsizei>(rect.GetHeight()), - tuple.format, tuple.type, &gl_buffer[buffer_offset]); - } + ASSERT(params.width * GetGLBytesPerPixel(params.pixel_format) % 4 == 0); + glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.width)); - glPixelStorei(GL_PACK_ROW_LENGTH, 0); -} - -enum class MatchFlags { - None = 0, - Invalid = 1, // Flag that can be applied to other match types, invalid matches require - // validation before they can be used - Exact = 1 << 1, // Surfaces perfectly match - SubRect = 1 << 2, // Surface encompasses params - Copy = 1 << 3, // Surface we can copy from - Expand = 1 << 4, // Surface that can expand params - TexCopy = 1 << 5 // Surface that will match a display transfer "texture copy" parameters -}; - -constexpr MatchFlags operator|(MatchFlags lhs, MatchFlags rhs) { - return static_cast<MatchFlags>(static_cast<int>(lhs) | static_cast<int>(rhs)); -} + const auto& rect{params.GetRect()}; + size_t buffer_offset = + (rect.bottom * params.width + rect.left) * GetGLBytesPerPixel(params.pixel_format); -constexpr MatchFlags operator&(MatchFlags lhs, MatchFlags rhs) { - return static_cast<MatchFlags>(static_cast<int>(lhs) & static_cast<int>(rhs)); -} + state.UnbindTexture(texture.handle); + state.draw.read_framebuffer = read_fb_handle; + state.Apply(); -/// Get the best surface match (and its match type) for the given flags -template <MatchFlags find_flags> -Surface FindMatch(const SurfaceCache& surface_cache, const SurfaceParams& params, - ScaleMatch match_scale_type, - boost::optional<SurfaceInterval> validate_interval = boost::none) { - Surface match_surface = nullptr; - bool match_valid = false; - u32 match_scale = 0; - SurfaceInterval match_interval{}; - - for (auto& pair : RangeFromInterval(surface_cache, params.GetInterval())) { - for (auto& surface : pair.second) { - bool res_scale_matched = match_scale_type == ScaleMatch::Exact - ? (params.res_scale == surface->res_scale) - : (params.res_scale <= surface->res_scale); - // validity will be checked in GetCopyableInterval - bool is_valid = - (find_flags & MatchFlags::Copy) != MatchFlags::None - ? true - : surface->IsRegionValid(validate_interval.value_or(params.GetInterval())); - - if ((find_flags & MatchFlags::Invalid) == MatchFlags::None && !is_valid) - continue; - - auto IsMatch_Helper = [&](auto check_type, auto match_fn) { - if ((find_flags & check_type) == MatchFlags::None) - return; - - bool matched; - SurfaceInterval surface_interval; - std::tie(matched, surface_interval) = match_fn(); - if (!matched) - return; - - if (!res_scale_matched && match_scale_type != ScaleMatch::Ignore && - surface->type != SurfaceType::Fill) - return; - - // Found a match, update only if this is better than the previous one - auto UpdateMatch = [&] { - match_surface = surface; - match_valid = is_valid; - match_scale = surface->res_scale; - match_interval = surface_interval; - }; - - if (surface->res_scale > match_scale) { - UpdateMatch(); - return; - } else if (surface->res_scale < match_scale) { - return; - } - - if (is_valid && !match_valid) { - UpdateMatch(); - return; - } else if (is_valid != match_valid) { - return; - } - - if (boost::icl::length(surface_interval) > boost::icl::length(match_interval)) { - UpdateMatch(); - } - }; - IsMatch_Helper(std::integral_constant<MatchFlags, MatchFlags::Exact>{}, [&] { - return std::make_pair(surface->ExactMatch(params), surface->GetInterval()); - }); - IsMatch_Helper(std::integral_constant<MatchFlags, MatchFlags::SubRect>{}, [&] { - return std::make_pair(surface->CanSubRect(params), surface->GetInterval()); - }); - IsMatch_Helper(std::integral_constant<MatchFlags, MatchFlags::Copy>{}, [&] { - auto copy_interval = - params.FromInterval(*validate_interval).GetCopyableInterval(surface); - bool matched = boost::icl::length(copy_interval & *validate_interval) != 0 && - surface->CanCopy(params, copy_interval); - return std::make_pair(matched, copy_interval); - }); - IsMatch_Helper(std::integral_constant<MatchFlags, MatchFlags::Expand>{}, [&] { - return std::make_pair(surface->CanExpand(params), surface->GetInterval()); - }); - IsMatch_Helper(std::integral_constant<MatchFlags, MatchFlags::TexCopy>{}, [&] { - return std::make_pair(surface->CanTexCopy(params), surface->GetInterval()); - }); - } + if (params.type == SurfaceType::ColorTexture) { + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + texture.handle, 0); + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, + 0); + } else if (params.type == SurfaceType::Depth) { + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, + texture.handle, 0); + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); + } else { + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, + texture.handle, 0); } - return match_surface; + glReadPixels(static_cast<GLint>(rect.left), static_cast<GLint>(rect.bottom), + static_cast<GLsizei>(rect.GetWidth()), static_cast<GLsizei>(rect.GetHeight()), + tuple.format, tuple.type, &gl_buffer[buffer_offset]); + + glPixelStorei(GL_PACK_ROW_LENGTH, 0); } RasterizerCacheOpenGL::RasterizerCacheOpenGL() { read_framebuffer.Create(); draw_framebuffer.Create(); - - attributeless_vao.Create(); - - d24s8_abgr_buffer.Create(); - d24s8_abgr_buffer_size = 0; - - const char* vs_source = R"( -#version 330 core -const vec2 vertices[4] = vec2[4](vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0)); -void main() { - gl_Position = vec4(vertices[gl_VertexID], 0.0, 1.0); -} -)"; - const char* fs_source = R"( -#version 330 core - -uniform samplerBuffer tbo; -uniform vec2 tbo_size; -uniform vec4 viewport; - -out vec4 color; - -void main() { - vec2 tbo_coord = (gl_FragCoord.xy - viewport.xy) * tbo_size / viewport.zw; - int tbo_offset = int(tbo_coord.y) * int(tbo_size.x) + int(tbo_coord.x); - color = texelFetch(tbo, tbo_offset).rabg; -} -)"; - d24s8_abgr_shader.CreateFromSource(vs_source, nullptr, fs_source); - - OpenGLState state = OpenGLState::GetCurState(); - GLuint old_program = state.draw.shader_program; - state.draw.shader_program = d24s8_abgr_shader.handle; - state.Apply(); - - GLint tbo_u_id = glGetUniformLocation(d24s8_abgr_shader.handle, "tbo"); - ASSERT(tbo_u_id != -1); - glUniform1i(tbo_u_id, 0); - - state.draw.shader_program = old_program; - state.Apply(); - - d24s8_abgr_tbo_size_u_id = glGetUniformLocation(d24s8_abgr_shader.handle, "tbo_size"); - ASSERT(d24s8_abgr_tbo_size_u_id != -1); - d24s8_abgr_viewport_u_id = glGetUniformLocation(d24s8_abgr_shader.handle, "viewport"); - ASSERT(d24s8_abgr_viewport_u_id != -1); } RasterizerCacheOpenGL::~RasterizerCacheOpenGL() { - FlushAll(); - while (!surface_cache.empty()) - UnregisterSurface(*surface_cache.begin()->second.begin()); -} - -bool RasterizerCacheOpenGL::BlitSurfaces(const Surface& src_surface, - const MathUtil::Rectangle<u32>& src_rect, - const Surface& dst_surface, - const MathUtil::Rectangle<u32>& dst_rect) { - if (!SurfaceParams::CheckFormatsBlittable(src_surface->pixel_format, dst_surface->pixel_format)) - return false; - - return BlitTextures(src_surface->texture.handle, src_rect, dst_surface->texture.handle, - dst_rect, src_surface->type, read_framebuffer.handle, - draw_framebuffer.handle); -} - -void RasterizerCacheOpenGL::ConvertD24S8toABGR(GLuint src_tex, - const MathUtil::Rectangle<u32>& src_rect, - GLuint dst_tex, - const MathUtil::Rectangle<u32>& dst_rect) { - OpenGLState prev_state = OpenGLState::GetCurState(); - SCOPE_EXIT({ prev_state.Apply(); }); - - OpenGLState state; - state.draw.read_framebuffer = read_framebuffer.handle; - state.draw.draw_framebuffer = draw_framebuffer.handle; - state.Apply(); - - glBindBuffer(GL_PIXEL_PACK_BUFFER, d24s8_abgr_buffer.handle); - - GLsizeiptr target_pbo_size = src_rect.GetWidth() * src_rect.GetHeight() * 4; - if (target_pbo_size > d24s8_abgr_buffer_size) { - d24s8_abgr_buffer_size = target_pbo_size * 2; - glBufferData(GL_PIXEL_PACK_BUFFER, d24s8_abgr_buffer_size, nullptr, GL_STREAM_COPY); - } - - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, src_tex, - 0); - glReadPixels(static_cast<GLint>(src_rect.left), static_cast<GLint>(src_rect.bottom), - static_cast<GLsizei>(src_rect.GetWidth()), - static_cast<GLsizei>(src_rect.GetHeight()), GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, - 0); - - glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); - - // PBO now contains src_tex in RABG format - state.draw.shader_program = d24s8_abgr_shader.handle; - state.draw.vertex_array = attributeless_vao.handle; - state.viewport.x = static_cast<GLint>(dst_rect.left); - state.viewport.y = static_cast<GLint>(dst_rect.bottom); - state.viewport.width = static_cast<GLsizei>(dst_rect.GetWidth()); - state.viewport.height = static_cast<GLsizei>(dst_rect.GetHeight()); - state.Apply(); - - OGLTexture tbo; - tbo.Create(); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_BUFFER, tbo.handle); - glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA8, d24s8_abgr_buffer.handle); - - glUniform2f(d24s8_abgr_tbo_size_u_id, static_cast<GLfloat>(src_rect.GetWidth()), - static_cast<GLfloat>(src_rect.GetHeight())); - glUniform4f(d24s8_abgr_viewport_u_id, static_cast<GLfloat>(state.viewport.x), - static_cast<GLfloat>(state.viewport.y), static_cast<GLfloat>(state.viewport.width), - static_cast<GLfloat>(state.viewport.height)); - - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, 0); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - glBindTexture(GL_TEXTURE_BUFFER, 0); -} - -Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, ScaleMatch match_res_scale, - bool load_if_create) { - if (params.addr == 0 || params.height * params.width == 0) { - return nullptr; - } - // Use GetSurfaceSubRect instead - ASSERT(params.width == params.stride); - - ASSERT(!params.is_tiled || - (params.GetActualWidth() % 8 == 0 && params.GetActualHeight() % 8 == 0)); - - // Check for an exact match in existing surfaces - Surface surface = - FindMatch<MatchFlags::Exact | MatchFlags::Invalid>(surface_cache, params, match_res_scale); - - if (surface == nullptr) { - u16 target_res_scale = params.res_scale; - if (match_res_scale != ScaleMatch::Exact) { - // This surface may have a subrect of another surface with a higher res_scale, find it - // to adjust our params - SurfaceParams find_params = params; - Surface expandable = FindMatch<MatchFlags::Expand | MatchFlags::Invalid>( - surface_cache, find_params, match_res_scale); - if (expandable != nullptr && expandable->res_scale > target_res_scale) { - target_res_scale = expandable->res_scale; - } - } - SurfaceParams new_params = params; - new_params.res_scale = target_res_scale; - surface = CreateSurface(new_params); - RegisterSurface(surface); - } - - if (load_if_create) { - ValidateSurface(surface, params.addr, params.size); - } - - return surface; -} - -boost::optional<Tegra::GPUVAddr> RasterizerCacheOpenGL::TryFindFramebufferGpuAddress( - VAddr cpu_addr) const { - // Tries to find the GPU address of a framebuffer based on the CPU address. This is because - // final output framebuffers are specified by CPU address, but internally our GPU cache uses GPU - // addresses. We iterate through all cached framebuffers, and compare their starting CPU address - // to the one provided. This is obviously not great, and won't work if the framebuffer overlaps - // surfaces. - - std::vector<Tegra::GPUVAddr> gpu_addresses; - for (const auto& pair : surface_cache) { - for (const auto& surface : pair.second) { - const VAddr surface_cpu_addr = surface->GetCpuAddr(); - if (cpu_addr >= surface_cpu_addr && cpu_addr < (surface_cpu_addr + surface->size)) { - ASSERT_MSG(cpu_addr == surface_cpu_addr, "overlapping surfaces are unsupported"); - gpu_addresses.push_back(surface->addr); - } - } + while (!surface_cache.empty()) { + UnregisterSurface(surface_cache.begin()->second); } - - if (gpu_addresses.empty()) { - return {}; - } - - ASSERT_MSG(gpu_addresses.size() == 1, ">1 surface is unsupported"); - return gpu_addresses[0]; -} - -SurfaceRect_Tuple RasterizerCacheOpenGL::GetSurfaceSubRect(const SurfaceParams& params, - ScaleMatch match_res_scale, - bool load_if_create) { - if (params.addr == 0 || params.height * params.width == 0) { - return std::make_tuple(nullptr, MathUtil::Rectangle<u32>{}); - } - - // Attempt to find encompassing surface - Surface surface = FindMatch<MatchFlags::SubRect | MatchFlags::Invalid>(surface_cache, params, - match_res_scale); - - // Check if FindMatch failed because of res scaling - // If that's the case create a new surface with - // the dimensions of the lower res_scale surface - // to suggest it should not be used again - if (surface == nullptr && match_res_scale != ScaleMatch::Ignore) { - surface = FindMatch<MatchFlags::SubRect | MatchFlags::Invalid>(surface_cache, params, - ScaleMatch::Ignore); - if (surface != nullptr) { - ASSERT(surface->res_scale < params.res_scale); - SurfaceParams new_params = *surface; - new_params.res_scale = params.res_scale; - - surface = CreateSurface(new_params); - RegisterSurface(surface); - } - } - - SurfaceParams aligned_params = params; - if (params.is_tiled) { - aligned_params.height = Common::AlignUp(params.height, 8); - aligned_params.width = Common::AlignUp(params.width, 8); - aligned_params.stride = Common::AlignUp(params.stride, 8); - aligned_params.UpdateParams(); - } - - // Check for a surface we can expand before creating a new one - if (surface == nullptr) { - surface = FindMatch<MatchFlags::Expand | MatchFlags::Invalid>(surface_cache, aligned_params, - match_res_scale); - if (surface != nullptr) { - aligned_params.width = aligned_params.stride; - aligned_params.UpdateParams(); - - SurfaceParams new_params = *surface; - new_params.addr = std::min(aligned_params.addr, surface->addr); - new_params.end = std::max(aligned_params.end, surface->end); - new_params.size = new_params.end - new_params.addr; - new_params.height = static_cast<u32>( - new_params.size / aligned_params.BytesInPixels(aligned_params.stride)); - ASSERT(new_params.size % aligned_params.BytesInPixels(aligned_params.stride) == 0); - - Surface new_surface = CreateSurface(new_params); - DuplicateSurface(surface, new_surface); - - // Delete the expanded surface, this can't be done safely yet - // because it may still be in use - remove_surfaces.emplace(surface); - - surface = new_surface; - RegisterSurface(new_surface); - } - } - - // No subrect found - create and return a new surface - if (surface == nullptr) { - SurfaceParams new_params = aligned_params; - // Can't have gaps in a surface - new_params.width = aligned_params.stride; - new_params.UpdateParams(); - // GetSurface will create the new surface and possibly adjust res_scale if necessary - surface = GetSurface(new_params, match_res_scale, load_if_create); - } else if (load_if_create) { - ValidateSurface(surface, aligned_params.addr, aligned_params.size); - } - - return std::make_tuple(surface, surface->GetScaledSubRect(params)); } Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextureInfo& config) { - auto& gpu = Core::System::GetInstance().GPU(); - - SurfaceParams params; - params.addr = config.tic.Address(); - params.is_tiled = config.tic.IsTiled(); - params.pixel_format = SurfaceParams::PixelFormatFromTextureFormat(config.tic.format); - - params.width = Common::AlignUp(config.tic.Width(), params.GetCompresssionFactor()) / - params.GetCompresssionFactor(); - params.height = Common::AlignUp(config.tic.Height(), params.GetCompresssionFactor()) / - params.GetCompresssionFactor(); - - // TODO(Subv): Different types per component are not supported. - ASSERT(config.tic.r_type.Value() == config.tic.g_type.Value() && - config.tic.r_type.Value() == config.tic.b_type.Value() && - config.tic.r_type.Value() == config.tic.a_type.Value()); - - params.component_type = SurfaceParams::ComponentTypeFromTexture(config.tic.r_type.Value()); - - if (config.tic.IsTiled()) { - params.block_height = config.tic.BlockHeight(); - params.width = Common::AlignUp(params.width, params.block_height); - params.height = Common::AlignUp(params.height, params.block_height); - } else { - // Use the texture-provided stride value if the texture isn't tiled. - params.stride = static_cast<u32>(params.PixelsInBytes(config.tic.Pitch())); - } - - params.UpdateParams(); - - if (params.GetActualWidth() % 8 != 0 || params.GetActualHeight() % 8 != 0 || - params.stride != params.width) { - Surface src_surface; - MathUtil::Rectangle<u32> rect; - std::tie(src_surface, rect) = GetSurfaceSubRect(params, ScaleMatch::Ignore, true); - - rect = rect.Scale(params.GetCompresssionFactor()); - - params.res_scale = src_surface->res_scale; - Surface tmp_surface = CreateSurface(params); - - auto dst_rect = tmp_surface->GetScaledRect().Scale(params.GetCompresssionFactor()); - BlitTextures(src_surface->texture.handle, rect, tmp_surface->texture.handle, dst_rect, - SurfaceParams::GetFormatType(params.pixel_format), read_framebuffer.handle, - draw_framebuffer.handle); - - remove_surfaces.emplace(tmp_surface); - return tmp_surface; - } - - return GetSurface(params, ScaleMatch::Ignore, true); + return GetSurface(SurfaceParams::CreateForTexture(config)); } SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces( bool using_color_fb, bool using_depth_fb, const MathUtil::Rectangle<s32>& viewport) { const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; - const auto& config = regs.rt[0]; // TODO(bunnei): This is hard corded to use just the first render buffer - NGLOG_WARNING(Render_OpenGL, "hard-coded for render target 0!"); - - // update resolution_scale_factor and reset cache if changed - // TODO (bunnei): This code was ported as-is from Citra, and is technically not thread-safe. We - // need to fix this before making the renderer multi-threaded. - static u16 resolution_scale_factor = GetResolutionScaleFactor(); - if (resolution_scale_factor != GetResolutionScaleFactor()) { - resolution_scale_factor = GetResolutionScaleFactor(); - FlushAll(); - while (!surface_cache.empty()) - UnregisterSurface(*surface_cache.begin()->second.begin()); - } - - MathUtil::Rectangle<u32> viewport_clamped{ - static_cast<u32>(std::clamp(viewport.left, 0, static_cast<s32>(config.width))), - static_cast<u32>(std::clamp(viewport.top, 0, static_cast<s32>(config.height))), - static_cast<u32>(std::clamp(viewport.right, 0, static_cast<s32>(config.width))), - static_cast<u32>(std::clamp(viewport.bottom, 0, static_cast<s32>(config.height)))}; + LOG_WARNING(Render_OpenGL, "hard-coded for render target 0!"); // get color and depth surfaces - SurfaceParams color_params; - color_params.is_tiled = true; - color_params.res_scale = resolution_scale_factor; - color_params.width = config.width; - color_params.height = config.height; - // TODO(Subv): Can framebuffers use a different block height? - color_params.block_height = Tegra::Texture::TICEntry::DefaultBlockHeight; - SurfaceParams depth_params = color_params; - - color_params.addr = config.Address(); - color_params.pixel_format = SurfaceParams::PixelFormatFromRenderTargetFormat(config.format); - color_params.component_type = SurfaceParams::ComponentTypeFromRenderTarget(config.format); - color_params.UpdateParams(); - - ASSERT_MSG(!using_depth_fb, "depth buffer is unimplemented"); - // depth_params.addr = config.GetDepthBufferPhysicalAddress(); - // depth_params.pixel_format = SurfaceParams::PixelFormatFromDepthFormat(config.depth_format); - // depth_params.UpdateParams(); - - auto color_vp_interval = color_params.GetSubRectInterval(viewport_clamped); - auto depth_vp_interval = depth_params.GetSubRectInterval(viewport_clamped); - - // Make sure that framebuffers don't overlap if both color and depth are being used - if (using_color_fb && using_depth_fb && - boost::icl::length(color_vp_interval & depth_vp_interval)) { - NGLOG_CRITICAL(Render_OpenGL, "Color and depth framebuffer memory regions overlap; " - "overlapping framebuffers not supported!"); - using_depth_fb = false; + SurfaceParams color_params{}; + SurfaceParams depth_params{}; + + if (using_color_fb) { + color_params = SurfaceParams::CreateForFramebuffer(regs.rt[0]); + } + + if (using_depth_fb) { + depth_params = + SurfaceParams::CreateForDepthBuffer(regs.rt[0], regs.zeta.Address(), regs.zeta.format); } MathUtil::Rectangle<u32> color_rect{}; - Surface color_surface = nullptr; - if (using_color_fb) - std::tie(color_surface, color_rect) = - GetSurfaceSubRect(color_params, ScaleMatch::Exact, false); + Surface color_surface; + if (using_color_fb) { + color_surface = GetSurface(color_params); + if (color_surface) { + color_rect = color_surface->GetSurfaceParams().GetRect(); + } + } MathUtil::Rectangle<u32> depth_rect{}; - Surface depth_surface = nullptr; - if (using_depth_fb) - std::tie(depth_surface, depth_rect) = - GetSurfaceSubRect(depth_params, ScaleMatch::Exact, false); + Surface depth_surface; + if (using_depth_fb) { + depth_surface = GetSurface(depth_params); + if (depth_surface) { + depth_rect = depth_surface->GetSurfaceParams().GetRect(); + } + } MathUtil::Rectangle<u32> fb_rect{}; - if (color_surface != nullptr && depth_surface != nullptr) { + if (color_surface && depth_surface) { fb_rect = color_rect; // Color and Depth surfaces must have the same dimensions and offsets if (color_rect.bottom != depth_rect.bottom || color_rect.top != depth_rect.top || color_rect.left != depth_rect.left || color_rect.right != depth_rect.right) { - color_surface = GetSurface(color_params, ScaleMatch::Exact, false); - depth_surface = GetSurface(depth_params, ScaleMatch::Exact, false); - fb_rect = color_surface->GetScaledRect(); + color_surface = GetSurface(color_params); + depth_surface = GetSurface(depth_params); + fb_rect = color_surface->GetSurfaceParams().GetRect(); } - } else if (color_surface != nullptr) { + } else if (color_surface) { fb_rect = color_rect; - } else if (depth_surface != nullptr) { + } else if (depth_surface) { fb_rect = depth_rect; } - if (color_surface != nullptr) { - ValidateSurface(color_surface, boost::icl::first(color_vp_interval), - boost::icl::length(color_vp_interval)); - } - if (depth_surface != nullptr) { - ValidateSurface(depth_surface, boost::icl::first(depth_vp_interval), - boost::icl::length(depth_vp_interval)); - } - return std::make_tuple(color_surface, depth_surface, fb_rect); } -Surface RasterizerCacheOpenGL::GetFillSurface(const void* config) { - UNREACHABLE(); - return {}; +void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) { + surface->LoadGLBuffer(); + surface->UploadGLTexture(read_framebuffer.handle, draw_framebuffer.handle); } -SurfaceRect_Tuple RasterizerCacheOpenGL::GetTexCopySurface(const SurfaceParams& params) { - MathUtil::Rectangle<u32> rect{}; +void RasterizerCacheOpenGL::MarkSurfaceAsDirty(const Surface& surface) { + if (Settings::values.use_accurate_framebuffers) { + // If enabled, always flush dirty surfaces + surface->DownloadGLTexture(read_framebuffer.handle, draw_framebuffer.handle); + surface->FlushGLBuffer(); + } else { + // Otherwise, don't mark surfaces that we write to as cached, because the resulting loads + // and flushes are very slow and do not seem to improve accuracy + const auto& params{surface->GetSurfaceParams()}; + Memory::RasterizerMarkRegionCached(params.addr, params.size_in_bytes, false); + } +} - Surface match_surface = FindMatch<MatchFlags::TexCopy | MatchFlags::Invalid>( - surface_cache, params, ScaleMatch::Ignore); +Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params) { + if (params.addr == 0 || params.height * params.width == 0) { + return {}; + } - if (match_surface != nullptr) { - ValidateSurface(match_surface, params.addr, params.size); + const auto& gpu = Core::System::GetInstance().GPU(); + // Don't try to create any entries in the cache if the address of the texture is invalid. + if (gpu.memory_manager->GpuToCpuAddress(params.addr) == boost::none) + return {}; - SurfaceParams match_subrect; - if (params.width != params.stride) { - const u32 tiled_size = match_surface->is_tiled ? 8 : 1; - match_subrect = params; - match_subrect.width = - static_cast<u32>(match_surface->PixelsInBytes(params.width) / tiled_size); - match_subrect.stride = - static_cast<u32>(match_surface->PixelsInBytes(params.stride) / tiled_size); - match_subrect.height *= tiled_size; - } else { - match_subrect = match_surface->FromInterval(params.GetInterval()); - ASSERT(match_subrect.GetInterval() == params.GetInterval()); + // Check for an exact match in existing surfaces + const auto& surface_key{SurfaceKey::Create(params)}; + const auto& search{surface_cache.find(surface_key)}; + Surface surface; + if (search != surface_cache.end()) { + surface = search->second; + if (Settings::values.use_accurate_framebuffers) { + // Reload the surface from Switch memory + LoadSurface(surface); } - - rect = match_surface->GetScaledSubRect(match_subrect); + } else { + surface = std::make_shared<CachedSurface>(params); + RegisterSurface(surface); + LoadSurface(surface); } - return std::make_tuple(match_surface, rect); + return surface; } -void RasterizerCacheOpenGL::DuplicateSurface(const Surface& src_surface, - const Surface& dest_surface) { - ASSERT(dest_surface->addr <= src_surface->addr && dest_surface->end >= src_surface->end); - - BlitSurfaces(src_surface, src_surface->GetScaledRect(), dest_surface, - dest_surface->GetScaledSubRect(*src_surface)); - - dest_surface->invalid_regions -= src_surface->GetInterval(); - dest_surface->invalid_regions += src_surface->invalid_regions; - - SurfaceRegions regions; - for (auto& pair : RangeFromInterval(dirty_regions, src_surface->GetInterval())) { - if (pair.second == src_surface) { - regions += pair.first; +Surface RasterizerCacheOpenGL::TryFindFramebufferSurface(VAddr cpu_addr) const { + // Tries to find the GPU address of a framebuffer based on the CPU address. This is because + // final output framebuffers are specified by CPU address, but internally our GPU cache uses + // GPU addresses. We iterate through all cached framebuffers, and compare their starting CPU + // address to the one provided. This is obviously not great, and won't work if the + // framebuffer overlaps surfaces. + + std::vector<Surface> surfaces; + for (const auto& surface : surface_cache) { + const auto& params = surface.second->GetSurfaceParams(); + const VAddr surface_cpu_addr = params.GetCpuAddr(); + if (cpu_addr >= surface_cpu_addr && cpu_addr < (surface_cpu_addr + params.size_in_bytes)) { + ASSERT_MSG(cpu_addr == surface_cpu_addr, "overlapping surfaces are unsupported"); + surfaces.push_back(surface.second); } } - for (auto& interval : regions) { - dirty_regions.set({interval, dest_surface}); - } -} -void RasterizerCacheOpenGL::ValidateSurface(const Surface& surface, Tegra::GPUVAddr addr, - u64 size) { - if (size == 0) - return; - - const SurfaceInterval validate_interval(addr, addr + size); - - if (surface->type == SurfaceType::Fill) { - // Sanity check, fill surfaces will always be valid when used - ASSERT(surface->IsRegionValid(validate_interval)); - return; + if (surfaces.empty()) { + return {}; } - while (true) { - const auto it = surface->invalid_regions.find(validate_interval); - if (it == surface->invalid_regions.end()) - break; - - const auto interval = *it & validate_interval; - // Look for a valid surface to copy from - SurfaceParams params = surface->FromInterval(interval); - - Surface copy_surface = - FindMatch<MatchFlags::Copy>(surface_cache, params, ScaleMatch::Ignore, interval); - if (copy_surface != nullptr) { - SurfaceInterval copy_interval = params.GetCopyableInterval(copy_surface); - CopySurface(copy_surface, surface, copy_interval); - surface->invalid_regions.erase(copy_interval); - continue; - } + ASSERT_MSG(surfaces.size() == 1, ">1 surface is unsupported"); - // Load data from Switch memory - FlushRegion(params.addr, params.size); - surface->LoadGLBuffer(params.addr, params.end); - surface->UploadGLTexture(surface->GetSubRect(params), read_framebuffer.handle, - draw_framebuffer.handle); - surface->invalid_regions.erase(params.GetInterval()); - } + return surfaces[0]; } -void RasterizerCacheOpenGL::FlushRegion(Tegra::GPUVAddr addr, u64 size, Surface flush_surface) { - if (size == 0) - return; - - const SurfaceInterval flush_interval(addr, addr + size); - SurfaceRegions flushed_intervals; - - for (auto& pair : RangeFromInterval(dirty_regions, flush_interval)) { - // small sizes imply that this most likely comes from the cpu, flush the entire region - // the point is to avoid thousands of small writes every frame if the cpu decides to access - // that region, anything higher than 8 you're guaranteed it comes from a service - const auto interval = size <= 8 ? pair.first : pair.first & flush_interval; - auto& surface = pair.second; - - if (flush_surface != nullptr && surface != flush_surface) - continue; +void RasterizerCacheOpenGL::FlushRegion(Tegra::GPUVAddr /*addr*/, size_t /*size*/) { + // TODO(bunnei): This is unused in the current implementation of the rasterizer cache. We should + // probably implement this in the future, but for now, the `use_accurate_framebufers` setting + // can be used to always flush. +} - // Sanity check, this surface is the last one that marked this region dirty - ASSERT(surface->IsRegionValid(interval)); +void RasterizerCacheOpenGL::InvalidateRegion(Tegra::GPUVAddr addr, size_t size) { + for (const auto& pair : surface_cache) { + const auto& surface{pair.second}; + const auto& params{surface->GetSurfaceParams()}; - if (surface->type != SurfaceType::Fill) { - SurfaceParams params = surface->FromInterval(interval); - surface->DownloadGLTexture(surface->GetSubRect(params), read_framebuffer.handle, - draw_framebuffer.handle); + if (params.IsOverlappingRegion(addr, size)) { + UnregisterSurface(surface); } - surface->FlushGLBuffer(boost::icl::first(interval), boost::icl::last_next(interval)); - flushed_intervals += interval; } - // Reset dirty regions - dirty_regions -= flushed_intervals; } -void RasterizerCacheOpenGL::FlushAll() { - FlushRegion(0, Kernel::VMManager::MAX_ADDRESS); -} +void RasterizerCacheOpenGL::RegisterSurface(const Surface& surface) { + const auto& params{surface->GetSurfaceParams()}; + const auto& surface_key{SurfaceKey::Create(params)}; + const auto& search{surface_cache.find(surface_key)}; -void RasterizerCacheOpenGL::InvalidateRegion(Tegra::GPUVAddr addr, u64 size, - const Surface& region_owner) { - if (size == 0) + if (search != surface_cache.end()) { + // Registered already return; - - const SurfaceInterval invalid_interval(addr, addr + size); - - if (region_owner != nullptr) { - ASSERT(addr >= region_owner->addr && addr + size <= region_owner->end); - // Surfaces can't have a gap - ASSERT(region_owner->width == region_owner->stride); - region_owner->invalid_regions.erase(invalid_interval); - } - - for (auto& pair : RangeFromInterval(surface_cache, invalid_interval)) { - for (auto& cached_surface : pair.second) { - if (cached_surface == region_owner) - continue; - - // If cpu is invalidating this region we want to remove it - // to (likely) mark the memory pages as uncached - if (region_owner == nullptr && size <= 8) { - FlushRegion(cached_surface->addr, cached_surface->size, cached_surface); - remove_surfaces.emplace(cached_surface); - continue; - } - - const auto interval = cached_surface->GetInterval() & invalid_interval; - cached_surface->invalid_regions.insert(interval); - - // Remove only "empty" fill surfaces to avoid destroying and recreating OGL textures - if (cached_surface->type == SurfaceType::Fill && - cached_surface->IsSurfaceFullyInvalid()) { - remove_surfaces.emplace(cached_surface); - } - } } - if (region_owner != nullptr) - dirty_regions.set({invalid_interval, region_owner}); - else - dirty_regions.erase(invalid_interval); - - for (auto& remove_surface : remove_surfaces) { - if (remove_surface == region_owner) { - Surface expanded_surface = FindMatch<MatchFlags::SubRect | MatchFlags::Invalid>( - surface_cache, *region_owner, ScaleMatch::Ignore); - ASSERT(expanded_surface); - - if ((region_owner->invalid_regions - expanded_surface->invalid_regions).empty()) { - DuplicateSurface(region_owner, expanded_surface); - } else { - continue; - } - } - UnregisterSurface(remove_surface); - } - - remove_surfaces.clear(); + surface_cache[surface_key] = surface; + UpdatePagesCachedCount(params.addr, params.size_in_bytes, 1); } -Surface RasterizerCacheOpenGL::CreateSurface(const SurfaceParams& params) { - Surface surface = std::make_shared<CachedSurface>(); - static_cast<SurfaceParams&>(*surface) = params; - - surface->texture.Create(); - - surface->gl_buffer_size = 0; - surface->invalid_regions.insert(surface->GetInterval()); - AllocateSurfaceTexture(surface->texture.handle, - GetFormatTuple(surface->pixel_format, surface->component_type), - surface->GetScaledWidth(), surface->GetScaledHeight()); - - return surface; -} +void RasterizerCacheOpenGL::UnregisterSurface(const Surface& surface) { + const auto& params{surface->GetSurfaceParams()}; + const auto& surface_key{SurfaceKey::Create(params)}; + const auto& search{surface_cache.find(surface_key)}; -void RasterizerCacheOpenGL::RegisterSurface(const Surface& surface) { - if (surface->registered) { + if (search == surface_cache.end()) { + // Unregistered already return; } - surface->registered = true; - surface_cache.add({surface->GetInterval(), SurfaceSet{surface}}); - UpdatePagesCachedCount(surface->addr, surface->size, 1); + + UpdatePagesCachedCount(params.addr, params.size_in_bytes, -1); + surface_cache.erase(search); } -void RasterizerCacheOpenGL::UnregisterSurface(const Surface& surface) { - if (!surface->registered) { - return; - } - surface->registered = false; - UpdatePagesCachedCount(surface->addr, surface->size, -1); - surface_cache.subtract({surface->GetInterval(), SurfaceSet{surface}}); +template <typename Map, typename Interval> +constexpr auto RangeFromInterval(Map& map, const Interval& interval) { + return boost::make_iterator_range(map.equal_range(interval)); } void RasterizerCacheOpenGL::UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, int delta) { diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 0f43e863d0..1bedae992e 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -1,57 +1,26 @@ -// Copyright 2015 Citra Emulator Project +// Copyright 2018 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once #include <array> +#include <map> #include <memory> -#include <set> -#include <tuple> -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-local-typedefs" -#endif +#include <vector> #include <boost/icl/interval_map.hpp> -#include <boost/icl/interval_set.hpp> -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif -#include <boost/optional.hpp> -#include <glad/glad.h> -#include "common/assert.h" -#include "common/common_funcs.h" #include "common/common_types.h" +#include "common/hash.h" #include "common/math_util.h" -#include "video_core/gpu.h" -#include "video_core/memory_manager.h" +#include "video_core/engines/maxwell_3d.h" #include "video_core/renderer_opengl/gl_resource_manager.h" #include "video_core/textures/texture.h" -struct CachedSurface; +class CachedSurface; using Surface = std::shared_ptr<CachedSurface>; -using SurfaceSet = std::set<Surface>; - -using SurfaceRegions = boost::icl::interval_set<Tegra::GPUVAddr>; -using SurfaceMap = boost::icl::interval_map<Tegra::GPUVAddr, Surface>; -using SurfaceCache = boost::icl::interval_map<Tegra::GPUVAddr, SurfaceSet>; - -using SurfaceInterval = SurfaceCache::interval_type; -static_assert(std::is_same<SurfaceRegions::interval_type, SurfaceCache::interval_type>() && - std::is_same<SurfaceMap::interval_type, SurfaceCache::interval_type>(), - "incorrect interval types"); - -using SurfaceRect_Tuple = std::tuple<Surface, MathUtil::Rectangle<u32>>; using SurfaceSurfaceRect_Tuple = std::tuple<Surface, Surface, MathUtil::Rectangle<u32>>; - using PageMap = boost::icl::interval_map<u64, int>; -enum class ScaleMatch { - Exact, // only accept same res scale - Upscale, // only allow higher scale than params - Ignore // accept every scaled res -}; - struct SurfaceParams { enum class PixelFormat { ABGR8 = 0, @@ -61,12 +30,24 @@ struct SurfaceParams { R8 = 4, RGBA16F = 5, R11FG11FB10F = 6, - DXT1 = 7, - DXT23 = 8, - DXT45 = 9, - DXN1 = 10, // This is also known as BC4 + RGBA32UI = 7, + DXT1 = 8, + DXT23 = 9, + DXT45 = 10, + DXN1 = 11, // This is also known as BC4 + BC7U = 12, + ASTC_2D_4X4 = 13, - Max, + MaxColorFormat, + + // DepthStencil formats + Z24S8 = 14, + S8Z24 = 15, + Z32F = 16, + + MaxDepthStencilFormat, + + Max = MaxDepthStencilFormat, Invalid = 255, }; @@ -92,10 +73,10 @@ struct SurfaceParams { /** * Gets the compression factor for the specified PixelFormat. This applies to just the * "compressed width" and "compressed height", not the overall compression factor of a - * compressed image. This is used for maintaining proper surface sizes for compressed texture - * formats. + * compressed image. This is used for maintaining proper surface sizes for compressed + * texture formats. */ - static constexpr u32 GetCompresssionFactor(PixelFormat format) { + static constexpr u32 GetCompressionFactor(PixelFormat format) { if (format == PixelFormat::Invalid) return 0; @@ -107,18 +88,21 @@ struct SurfaceParams { 1, // R8 1, // RGBA16F 1, // R11FG11FB10F + 1, // RGBA32UI 4, // DXT1 4, // DXT23 4, // DXT45 4, // DXN1 + 4, // BC7U + 4, // ASTC_2D_4X4 + 1, // Z24S8 + 1, // S8Z24 + 1, // Z32F }}; ASSERT(static_cast<size_t>(format) < compression_factor_table.size()); return compression_factor_table[static_cast<size_t>(format)]; } - u32 GetCompresssionFactor() const { - return GetCompresssionFactor(pixel_format); - } static constexpr u32 GetFormatBpp(PixelFormat format) { if (format == PixelFormat::Invalid) @@ -132,10 +116,16 @@ struct SurfaceParams { 8, // R8 64, // RGBA16F 32, // R11FG11FB10F + 128, // RGBA32UI 64, // DXT1 128, // DXT23 128, // DXT45 64, // DXN1 + 128, // BC7U + 32, // ASTC_2D_4X4 + 32, // Z24S8 + 32, // S8Z24 + 32, // Z32F }}; ASSERT(static_cast<size_t>(format) < bpp_table.size()); @@ -145,6 +135,20 @@ struct SurfaceParams { return GetFormatBpp(pixel_format); } + static PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format) { + switch (format) { + case Tegra::DepthFormat::S8_Z24_UNORM: + return PixelFormat::S8Z24; + case Tegra::DepthFormat::Z24_S8_UNORM: + return PixelFormat::Z24S8; + case Tegra::DepthFormat::Z32_FLOAT: + return PixelFormat::Z32F; + default: + LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); + UNREACHABLE(); + } + } + static PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) { switch (format) { case Tegra::RenderTargetFormat::RGBA8_UNORM: @@ -156,18 +160,10 @@ struct SurfaceParams { return PixelFormat::RGBA16F; case Tegra::RenderTargetFormat::R11G11B10_FLOAT: return PixelFormat::R11FG11FB10F; + case Tegra::RenderTargetFormat::RGBA32_UINT: + return PixelFormat::RGBA32UI; default: - NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); - UNREACHABLE(); - } - } - - static PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format) { - switch (format) { - case Tegra::FramebufferConfig::PixelFormat::ABGR8: - return PixelFormat::ABGR8; - default: - NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); + LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); UNREACHABLE(); } } @@ -189,6 +185,8 @@ struct SurfaceParams { return PixelFormat::RGBA16F; case Tegra::Texture::TextureFormat::BF10GF11RF11: return PixelFormat::R11FG11FB10F; + case Tegra::Texture::TextureFormat::R32_G32_B32_A32: + return PixelFormat::RGBA32UI; case Tegra::Texture::TextureFormat::DXT1: return PixelFormat::DXT1; case Tegra::Texture::TextureFormat::DXT23: @@ -197,8 +195,12 @@ struct SurfaceParams { return PixelFormat::DXT45; case Tegra::Texture::TextureFormat::DXN1: return PixelFormat::DXN1; + case Tegra::Texture::TextureFormat::BC7U: + return PixelFormat::BC7U; + case Tegra::Texture::TextureFormat::ASTC_2D_4X4: + return PixelFormat::ASTC_2D_4X4; default: - NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); + LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); UNREACHABLE(); } } @@ -220,6 +222,8 @@ struct SurfaceParams { return Tegra::Texture::TextureFormat::R16_G16_B16_A16; case PixelFormat::R11FG11FB10F: return Tegra::Texture::TextureFormat::BF10GF11RF11; + case PixelFormat::RGBA32UI: + return Tegra::Texture::TextureFormat::R32_G32_B32_A32; case PixelFormat::DXT1: return Tegra::Texture::TextureFormat::DXT1; case PixelFormat::DXT23: @@ -228,6 +232,23 @@ struct SurfaceParams { return Tegra::Texture::TextureFormat::DXT45; case PixelFormat::DXN1: return Tegra::Texture::TextureFormat::DXN1; + case PixelFormat::BC7U: + return Tegra::Texture::TextureFormat::BC7U; + case PixelFormat::ASTC_2D_4X4: + return Tegra::Texture::TextureFormat::ASTC_2D_4X4; + default: + UNREACHABLE(); + } + } + + static Tegra::DepthFormat DepthFormatFromPixelFormat(PixelFormat format) { + switch (format) { + case PixelFormat::S8Z24: + return Tegra::DepthFormat::S8_Z24_UNORM; + case PixelFormat::Z24S8: + return Tegra::DepthFormat::Z24_S8_UNORM; + case PixelFormat::Z32F: + return Tegra::DepthFormat::Z32_FLOAT; default: UNREACHABLE(); } @@ -239,7 +260,7 @@ struct SurfaceParams { case Tegra::Texture::ComponentType::UNORM: return ComponentType::UNorm; default: - NGLOG_CRITICAL(HW_GPU, "Unimplemented component type={}", static_cast<u32>(type)); + LOG_CRITICAL(HW_GPU, "Unimplemented component type={}", static_cast<u32>(type)); UNREACHABLE(); } } @@ -254,215 +275,153 @@ struct SurfaceParams { case Tegra::RenderTargetFormat::RGBA16_FLOAT: case Tegra::RenderTargetFormat::R11G11B10_FLOAT: return ComponentType::Float; + case Tegra::RenderTargetFormat::RGBA32_UINT: + return ComponentType::UInt; default: - NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); + LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); UNREACHABLE(); } } - static ComponentType ComponentTypeFromGPUPixelFormat( - Tegra::FramebufferConfig::PixelFormat format) { + static PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format) { switch (format) { case Tegra::FramebufferConfig::PixelFormat::ABGR8: - return ComponentType::UNorm; + return PixelFormat::ABGR8; default: - NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); + LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); UNREACHABLE(); } } - static bool CheckFormatsBlittable(PixelFormat pixel_format_a, PixelFormat pixel_format_b) { - SurfaceType a_type = GetFormatType(pixel_format_a); - SurfaceType b_type = GetFormatType(pixel_format_b); - - if (a_type == SurfaceType::ColorTexture && b_type == SurfaceType::ColorTexture) { - return true; - } - - if (a_type == SurfaceType::Depth && b_type == SurfaceType::Depth) { - return true; - } - - if (a_type == SurfaceType::DepthStencil && b_type == SurfaceType::DepthStencil) { - return true; + static ComponentType ComponentTypeFromDepthFormat(Tegra::DepthFormat format) { + switch (format) { + case Tegra::DepthFormat::S8_Z24_UNORM: + case Tegra::DepthFormat::Z24_S8_UNORM: + return ComponentType::UNorm; + case Tegra::DepthFormat::Z32_FLOAT: + return ComponentType::Float; + default: + LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); + UNREACHABLE(); } - - return false; } static SurfaceType GetFormatType(PixelFormat pixel_format) { - if (static_cast<size_t>(pixel_format) < MaxPixelFormat) { + if (static_cast<size_t>(pixel_format) < static_cast<size_t>(PixelFormat::MaxColorFormat)) { return SurfaceType::ColorTexture; } + if (static_cast<size_t>(pixel_format) < + static_cast<size_t>(PixelFormat::MaxDepthStencilFormat)) { + return SurfaceType::DepthStencil; + } + // TODO(Subv): Implement the other formats ASSERT(false); return SurfaceType::Invalid; } - /// Update the params "size", "end" and "type" from the already set "addr", "width", "height" - /// and "pixel_format" - void UpdateParams() { - if (stride == 0) { - stride = width; - } - type = GetFormatType(pixel_format); - size = !is_tiled ? BytesInPixels(stride * (height - 1) + width) - : BytesInPixels(stride * 8 * (height / 8 - 1) + width * 8); - end = addr + size; - } - - SurfaceInterval GetInterval() const { - return SurfaceInterval::right_open(addr, end); - } - - // Returns the outer rectangle containing "interval" - SurfaceParams FromInterval(SurfaceInterval interval) const; - - SurfaceInterval GetSubRectInterval(MathUtil::Rectangle<u32> unscaled_rect) const; - - // Returns the region of the biggest valid rectange within interval - SurfaceInterval GetCopyableInterval(const Surface& src_surface) const; - - /** - * Gets the actual width (in pixels) of the surface. This is provided because `width` is used - * for tracking the surface region in memory, which may be compressed for certain formats. In - * this scenario, `width` is actually the compressed width. - */ - u32 GetActualWidth() const { - return width * GetCompresssionFactor(); - } - - /** - * Gets the actual height (in pixels) of the surface. This is provided because `height` is used - * for tracking the surface region in memory, which may be compressed for certain formats. In - * this scenario, `height` is actually the compressed height. - */ - u32 GetActualHeight() const { - return height * GetCompresssionFactor(); - } + /// Returns the rectangle corresponding to this surface + MathUtil::Rectangle<u32> GetRect() const; - u32 GetScaledWidth() const { - return width * res_scale; + /// Returns the size of this surface in bytes, adjusted for compression + size_t SizeInBytes() const { + const u32 compression_factor{GetCompressionFactor(pixel_format)}; + ASSERT(width % compression_factor == 0); + ASSERT(height % compression_factor == 0); + return (width / compression_factor) * (height / compression_factor) * + GetFormatBpp(pixel_format) / CHAR_BIT; } - u32 GetScaledHeight() const { - return height * res_scale; - } + /// Returns the CPU virtual address for this surface + VAddr GetCpuAddr() const; - MathUtil::Rectangle<u32> GetRect() const { - return {0, height, width, 0}; + /// Returns true if the specified region overlaps with this surface's region in Switch memory + bool IsOverlappingRegion(Tegra::GPUVAddr region_addr, size_t region_size) const { + return addr <= (region_addr + region_size) && region_addr <= (addr + size_in_bytes); } - MathUtil::Rectangle<u32> GetScaledRect() const { - return {0, GetScaledHeight(), GetScaledWidth(), 0}; - } + /// Creates SurfaceParams from a texture configuration + static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config); + + /// Creates SurfaceParams from a framebuffer configuration + static SurfaceParams CreateForFramebuffer( + const Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig& config); + + /// Creates SurfaceParams for a depth buffer configuration + static SurfaceParams CreateForDepthBuffer( + const Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig& config, + Tegra::GPUVAddr zeta_address, Tegra::DepthFormat format); + + Tegra::GPUVAddr addr; + bool is_tiled; + u32 block_height; + PixelFormat pixel_format; + ComponentType component_type; + SurfaceType type; + u32 width; + u32 height; + u32 unaligned_height; + size_t size_in_bytes; +}; - u64 PixelsInBytes(u64 size) const { - return size * CHAR_BIT / GetFormatBpp(pixel_format); +/// Hashable variation of SurfaceParams, used for a key in the surface cache +struct SurfaceKey : Common::HashableStruct<SurfaceParams> { + static SurfaceKey Create(const SurfaceParams& params) { + SurfaceKey res; + res.state = params; + return res; } +}; - u64 BytesInPixels(u64 pixels) const { - return pixels * GetFormatBpp(pixel_format) / CHAR_BIT; +namespace std { +template <> +struct hash<SurfaceKey> { + size_t operator()(const SurfaceKey& k) const { + return k.Hash(); } - - VAddr GetCpuAddr() const; - - bool ExactMatch(const SurfaceParams& other_surface) const; - bool CanSubRect(const SurfaceParams& sub_surface) const; - bool CanExpand(const SurfaceParams& expanded_surface) const; - bool CanTexCopy(const SurfaceParams& texcopy_params) const; - - MathUtil::Rectangle<u32> GetSubRect(const SurfaceParams& sub_surface) const; - MathUtil::Rectangle<u32> GetScaledSubRect(const SurfaceParams& sub_surface) const; - - Tegra::GPUVAddr addr = 0; - Tegra::GPUVAddr end = 0; - boost::optional<VAddr> cpu_addr; - u64 size = 0; - - u32 width = 0; - u32 height = 0; - u32 stride = 0; - u32 block_height = 0; - u16 res_scale = 1; - - bool is_tiled = false; - PixelFormat pixel_format = PixelFormat::Invalid; - SurfaceType type = SurfaceType::Invalid; - ComponentType component_type = ComponentType::Invalid; }; +} // namespace std -struct CachedSurface : SurfaceParams { - bool CanFill(const SurfaceParams& dest_surface, SurfaceInterval fill_interval) const; - bool CanCopy(const SurfaceParams& dest_surface, SurfaceInterval copy_interval) const; - - bool IsRegionValid(SurfaceInterval interval) const { - return (invalid_regions.find(interval) == invalid_regions.end()); - } +class CachedSurface final { +public: + CachedSurface(const SurfaceParams& params); - bool IsSurfaceFullyInvalid() const { - return (invalid_regions & GetInterval()) == SurfaceRegions(GetInterval()); + const OGLTexture& Texture() const { + return texture; } - bool registered = false; - SurfaceRegions invalid_regions; - - u64 fill_size = 0; /// Number of bytes to read from fill_data - std::array<u8, 4> fill_data; - - OGLTexture texture; - - static constexpr unsigned int GetGLBytesPerPixel(PixelFormat format) { - if (format == PixelFormat::Invalid) + static constexpr unsigned int GetGLBytesPerPixel(SurfaceParams::PixelFormat format) { + if (format == SurfaceParams::PixelFormat::Invalid) return 0; return SurfaceParams::GetFormatBpp(format) / CHAR_BIT; } - std::unique_ptr<u8[]> gl_buffer; - size_t gl_buffer_size = 0; + const SurfaceParams& GetSurfaceParams() const { + return params; + } // Read/Write data in Switch memory to/from gl_buffer - void LoadGLBuffer(Tegra::GPUVAddr load_start, Tegra::GPUVAddr load_end); - void FlushGLBuffer(Tegra::GPUVAddr flush_start, Tegra::GPUVAddr flush_end); + void LoadGLBuffer(); + void FlushGLBuffer(); // Upload/Download data in gl_buffer in/to this surface's texture - void UploadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint read_fb_handle, - GLuint draw_fb_handle); - void DownloadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint read_fb_handle, - GLuint draw_fb_handle); + void UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle); + void DownloadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle); + +private: + OGLTexture texture; + std::vector<u8> gl_buffer; + SurfaceParams params; }; -class RasterizerCacheOpenGL : NonCopyable { +class RasterizerCacheOpenGL final : NonCopyable { public: RasterizerCacheOpenGL(); ~RasterizerCacheOpenGL(); - /// Blit one surface's texture to another - bool BlitSurfaces(const Surface& src_surface, const MathUtil::Rectangle<u32>& src_rect, - const Surface& dst_surface, const MathUtil::Rectangle<u32>& dst_rect); - - void ConvertD24S8toABGR(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rect, - GLuint dst_tex, const MathUtil::Rectangle<u32>& dst_rect); - - /// Copy one surface's region to another - void CopySurface(const Surface& src_surface, const Surface& dst_surface, - SurfaceInterval copy_interval); - - /// Load a texture from Switch memory to OpenGL and cache it (if not already cached) - Surface GetSurface(const SurfaceParams& params, ScaleMatch match_res_scale, - bool load_if_create); - - /// Tries to find a framebuffer GPU address based on the provided CPU address - boost::optional<Tegra::GPUVAddr> TryFindFramebufferGpuAddress(VAddr cpu_addr) const; - - /// Attempt to find a subrect (resolution scaled) of a surface, otherwise loads a texture from - /// Switch memory to OpenGL and caches it (if not already cached) - SurfaceRect_Tuple GetSurfaceSubRect(const SurfaceParams& params, ScaleMatch match_res_scale, - bool load_if_create); - /// Get a surface based on the texture configuration Surface GetTextureSurface(const Tegra::Texture::FullTextureInfo& config); @@ -470,29 +429,21 @@ public: SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb, const MathUtil::Rectangle<s32>& viewport); - /// Get a surface that matches the fill config - Surface GetFillSurface(const void* config); + /// Marks the specified surface as "dirty", in that it is out of sync with Switch memory + void MarkSurfaceAsDirty(const Surface& surface); - /// Get a surface that matches a "texture copy" display transfer config - SurfaceRect_Tuple GetTexCopySurface(const SurfaceParams& params); + /// Tries to find a framebuffer GPU address based on the provided CPU address + Surface TryFindFramebufferSurface(VAddr cpu_addr) const; /// Write any cached resources overlapping the region back to memory (if dirty) - void FlushRegion(Tegra::GPUVAddr addr, u64 size, Surface flush_surface = nullptr); - - /// Mark region as being invalidated by region_owner (nullptr if Switch memory) - void InvalidateRegion(Tegra::GPUVAddr addr, u64 size, const Surface& region_owner); + void FlushRegion(Tegra::GPUVAddr addr, size_t size); - /// Flush all cached resources tracked by this cache manager - void FlushAll(); + /// Mark the specified region as being invalidated + void InvalidateRegion(Tegra::GPUVAddr addr, size_t size); private: - void DuplicateSurface(const Surface& src_surface, const Surface& dest_surface); - - /// Update surface's texture for given region when necessary - void ValidateSurface(const Surface& surface, Tegra::GPUVAddr addr, u64 size); - - /// Create a new surface - Surface CreateSurface(const SurfaceParams& params); + void LoadSurface(const Surface& surface); + Surface GetSurface(const SurfaceParams& params); /// Register surface into the cache void RegisterSurface(const Surface& surface); @@ -503,18 +454,9 @@ private: /// Increase/decrease the number of surface in pages touching the specified region void UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, int delta); - SurfaceCache surface_cache; + std::unordered_map<SurfaceKey, Surface> surface_cache; PageMap cached_pages; - SurfaceMap dirty_regions; - SurfaceSet remove_surfaces; OGLFramebuffer read_framebuffer; OGLFramebuffer draw_framebuffer; - - OGLVertexArray attributeless_vao; - OGLBuffer d24s8_abgr_buffer; - GLsizeiptr d24s8_abgr_buffer_size; - OGLProgram d24s8_abgr_shader; - GLint d24s8_abgr_tbo_size_u_id; - GLint d24s8_abgr_viewport_u_id; }; diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h index 93f9172e76..0fed93ca56 100644 --- a/src/video_core/renderer_opengl/gl_resource_manager.h +++ b/src/video_core/renderer_opengl/gl_resource_manager.h @@ -38,7 +38,7 @@ public: if (handle == 0) return; glDeleteTextures(1, &handle); - OpenGLState::GetCurState().ResetTexture(handle).Apply(); + OpenGLState::GetCurState().UnbindTexture(handle).Apply(); handle = 0; } diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 67726e7c6b..5914077e82 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -9,6 +9,7 @@ #include "common/assert.h" #include "common/common_types.h" #include "video_core/engines/shader_bytecode.h" +#include "video_core/renderer_opengl/gl_rasterizer.h" #include "video_core/renderer_opengl/gl_shader_decompiler.h" namespace GLShader { @@ -16,6 +17,7 @@ namespace Decompiler { using Tegra::Shader::Attribute; using Tegra::Shader::Instruction; +using Tegra::Shader::LogicOperation; using Tegra::Shader::OpCode; using Tegra::Shader::Register; using Tegra::Shader::Sampler; @@ -266,6 +268,27 @@ public: } /** + * Returns code that does an integer size conversion for the specified size. + * @param value Value to perform integer size conversion on. + * @param size Register size to use for conversion instructions. + * @returns GLSL string corresponding to the value converted to the specified size. + */ + static std::string ConvertIntegerSize(const std::string& value, Register::Size size) { + switch (size) { + case Register::Size::Byte: + return "((" + value + " << 24) >> 24)"; + case Register::Size::Short: + return "((" + value + " << 16) >> 16)"; + case Register::Size::Word: + // Default - do nothing + return value; + default: + LOG_CRITICAL(HW_GPU, "Unimplemented conversion size {}", static_cast<u32>(size)); + UNREACHABLE(); + } + } + + /** * Gets a register as an float. * @param reg The register to get. * @param elem The element to use for the operation. @@ -281,15 +304,18 @@ public: * @param reg The register to get. * @param elem The element to use for the operation. * @param is_signed Whether to get the register as a signed (or unsigned) integer. + * @param size Register size to use for conversion instructions. * @returns GLSL string corresponding to the register as an integer. */ - std::string GetRegisterAsInteger(const Register& reg, unsigned elem = 0, - bool is_signed = true) { + std::string GetRegisterAsInteger(const Register& reg, unsigned elem = 0, bool is_signed = true, + Register::Size size = Register::Size::Word) { const std::string func = GetGLSLConversionFunc( GLSLRegister::Type::Float, is_signed ? GLSLRegister::Type::Integer : GLSLRegister::Type::UnsignedInteger); - return func + '(' + GetRegister(reg, elem) + ')'; + std::string value = func + '(' + GetRegister(reg, elem) + ')'; + + return ConvertIntegerSize(value, size); } /** @@ -299,13 +325,15 @@ public: * @param value The code representing the value to assign. * @param dest_num_components Number of components in the destination. * @param value_num_components Number of components in the value. - * @param is_abs Optional, when True, applies absolute value to output. + * @param is_saturated Optional, when True, saturates the provided value. * @param dest_elem Optional, the destination element to use for the operation. */ void SetRegisterToFloat(const Register& reg, u64 elem, const std::string& value, - u64 dest_num_components, u64 value_num_components, bool is_abs = false, - u64 dest_elem = 0) { - SetRegister(reg, elem, value, dest_num_components, value_num_components, is_abs, dest_elem); + u64 dest_num_components, u64 value_num_components, + bool is_saturated = false, u64 dest_elem = 0) { + + SetRegister(reg, elem, is_saturated ? "clamp(" + value + ", 0.0, 1.0)" : value, + dest_num_components, value_num_components, dest_elem); } /** @@ -315,18 +343,22 @@ public: * @param value The code representing the value to assign. * @param dest_num_components Number of components in the destination. * @param value_num_components Number of components in the value. - * @param is_abs Optional, when True, applies absolute value to output. + * @param is_saturated Optional, when True, saturates the provided value. * @param dest_elem Optional, the destination element to use for the operation. + * @param size Register size to use for conversion instructions. */ void SetRegisterToInteger(const Register& reg, bool is_signed, u64 elem, const std::string& value, u64 dest_num_components, - u64 value_num_components, bool is_abs = false, u64 dest_elem = 0) { + u64 value_num_components, bool is_saturated = false, + u64 dest_elem = 0, Register::Size size = Register::Size::Word) { + ASSERT_MSG(!is_saturated, "Unimplemented"); + const std::string func = GetGLSLConversionFunc( is_signed ? GLSLRegister::Type::Integer : GLSLRegister::Type::UnsignedInteger, GLSLRegister::Type::Float); - SetRegister(reg, elem, func + '(' + value + ')', dest_num_components, value_num_components, - is_abs, dest_elem); + SetRegister(reg, elem, func + '(' + ConvertIntegerSize(value, size) + ')', + dest_num_components, value_num_components, dest_elem); } /** @@ -366,7 +398,8 @@ public: /// Generates code representing a uniform (C buffer) register, interpreted as the input type. std::string GetUniform(u64 index, u64 offset, GLSLRegister::Type type) { declr_const_buffers[index].MarkAsUsed(index, offset, stage); - std::string value = 'c' + std::to_string(index) + '[' + std::to_string(offset) + ']'; + std::string value = 'c' + std::to_string(index) + '[' + std::to_string(offset / 4) + "][" + + std::to_string(offset % 4) + ']'; if (type == GLSLRegister::Type::Float) { return value; @@ -380,8 +413,12 @@ public: std::string GetUniformIndirect(u64 index, s64 offset, const Register& index_reg, GLSLRegister::Type type) { declr_const_buffers[index].MarkAsUsedIndirect(index, stage); - std::string value = 'c' + std::to_string(index) + "[(floatBitsToInt(" + - GetRegister(index_reg, 0) + ") + " + std::to_string(offset) + ") / 4]"; + + std::string final_offset = "((floatBitsToInt(" + GetRegister(index_reg, 0) + ") + " + + std::to_string(offset) + ") / 4)"; + + std::string value = + 'c' + std::to_string(index) + '[' + final_offset + " / 4][" + final_offset + " % 4]"; if (type == GLSLRegister::Type::Float) { return value; @@ -423,9 +460,10 @@ public: unsigned const_buffer_layout = 0; for (const auto& entry : GetConstBuffersDeclarations()) { - declarations.AddLine("layout(std430) buffer " + entry.GetName()); + declarations.AddLine("layout(std140) uniform " + entry.GetName()); declarations.AddLine('{'); - declarations.AddLine(" float c" + std::to_string(entry.GetIndex()) + "[];"); + declarations.AddLine(" vec4 c" + std::to_string(entry.GetIndex()) + + "[MAX_CONSTBUFFER_ELEMENTS];"); declarations.AddLine("};"); declarations.AddNewLine(); ++const_buffer_layout; @@ -500,13 +538,11 @@ private: * @param value The code representing the value to assign. * @param dest_num_components Number of components in the destination. * @param value_num_components Number of components in the value. - * @param is_abs Optional, when True, applies absolute value to output. * @param dest_elem Optional, the destination element to use for the operation. */ void SetRegister(const Register& reg, u64 elem, const std::string& value, - u64 dest_num_components, u64 value_num_components, bool is_abs, - u64 dest_elem) { - std::string dest = GetRegister(reg, dest_elem); + u64 dest_num_components, u64 value_num_components, u64 dest_elem) { + std::string dest = GetRegister(reg, static_cast<u32>(dest_elem)); if (dest_num_components > 1) { dest += GetSwizzle(elem); } @@ -516,8 +552,6 @@ private: src += GetSwizzle(elem); } - src = is_abs ? "abs(" + src + ')' : src; - shader.AddLine(dest + " = " + src + ';'); } @@ -547,7 +581,7 @@ private: return "input_attribute_" + std::to_string(index); } - NGLOG_CRITICAL(HW_GPU, "Unhandled input attribute: {}", index); + LOG_CRITICAL(HW_GPU, "Unhandled input attribute: {}", index); UNREACHABLE(); } } @@ -565,7 +599,7 @@ private: return "output_attribute_" + std::to_string(index); } - NGLOG_CRITICAL(HW_GPU, "Unhandled output attribute: {}", index); + LOG_CRITICAL(HW_GPU, "Unhandled output attribute: {}", index); UNREACHABLE(); } } @@ -685,21 +719,31 @@ private: /** * Returns the comparison string to use to compare two values in the 'set' family of * instructions. - * @params condition The condition used in the 'set'-family instruction. + * @param condition The condition used in the 'set'-family instruction. + * @param op_a First operand to use for the comparison. + * @param op_b Second operand to use for the comparison. * @returns String corresponding to the GLSL operator that matches the desired comparison. */ - std::string GetPredicateComparison(Tegra::Shader::PredCondition condition) const { + std::string GetPredicateComparison(Tegra::Shader::PredCondition condition, + const std::string& op_a, const std::string& op_b) const { using Tegra::Shader::PredCondition; static const std::unordered_map<PredCondition, const char*> PredicateComparisonStrings = { - {PredCondition::LessThan, "<"}, {PredCondition::Equal, "=="}, - {PredCondition::LessEqual, "<="}, {PredCondition::GreaterThan, ">"}, - {PredCondition::NotEqual, "!="}, {PredCondition::GreaterEqual, ">="}, + {PredCondition::LessThan, "<"}, {PredCondition::Equal, "=="}, + {PredCondition::LessEqual, "<="}, {PredCondition::GreaterThan, ">"}, + {PredCondition::NotEqual, "!="}, {PredCondition::GreaterEqual, ">="}, + {PredCondition::NotEqualWithNan, "!="}, }; - auto comparison = PredicateComparisonStrings.find(condition); + const auto& comparison{PredicateComparisonStrings.find(condition)}; ASSERT_MSG(comparison != PredicateComparisonStrings.end(), "Unknown predicate comparison operation"); - return comparison->second; + + std::string predicate{'(' + op_a + ") " + comparison->second + " (" + op_b + ')'}; + if (condition == PredCondition::NotEqualWithNan) { + predicate += " || isnan(" + op_a + ") || isnan(" + op_b + ')'; + } + + return predicate; } /** @@ -733,6 +777,31 @@ private: return (absolute_offset % SchedPeriod) == 0; } + void WriteLogicOperation(Register dest, LogicOperation logic_op, const std::string& op_a, + const std::string& op_b) { + switch (logic_op) { + case LogicOperation::And: { + regs.SetRegisterToInteger(dest, true, 0, '(' + op_a + " & " + op_b + ')', 1, 1); + break; + } + case LogicOperation::Or: { + regs.SetRegisterToInteger(dest, true, 0, '(' + op_a + " | " + op_b + ')', 1, 1); + break; + } + case LogicOperation::Xor: { + regs.SetRegisterToInteger(dest, true, 0, '(' + op_a + " ^ " + op_b + ')', 1, 1); + break; + } + case LogicOperation::PassB: { + regs.SetRegisterToInteger(dest, true, 0, op_b, 1, 1); + break; + } + default: + LOG_CRITICAL(HW_GPU, "Unimplemented logic operation: {}", static_cast<u32>(logic_op)); + UNREACHABLE(); + } + } + /** * Compiles a single instruction from Tegra to GLSL. * @param offset the offset of the Tegra shader instruction. @@ -750,8 +819,9 @@ private: // Decoding failure if (!opcode) { - NGLOG_CRITICAL(HW_GPU, "Unhandled instruction: {0:x}", instr.value); + LOG_CRITICAL(HW_GPU, "Unhandled instruction: {0:x}", instr.value); UNREACHABLE(); + return offset + 1; } shader.AddLine("// " + std::to_string(offset) + ": " + opcode->GetName()); @@ -770,22 +840,25 @@ private: switch (opcode->GetType()) { case OpCode::Type::Arithmetic: { - std::string op_a = instr.alu.negate_a ? "-" : ""; - op_a += regs.GetRegisterAsFloat(instr.gpr8); + std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); if (instr.alu.abs_a) { op_a = "abs(" + op_a + ')'; } - std::string op_b = instr.alu.negate_b ? "-" : ""; + if (instr.alu.negate_a) { + op_a = "-(" + op_a + ')'; + } + + std::string op_b; if (instr.is_b_imm) { - op_b += GetImmediate19(instr); + op_b = GetImmediate19(instr); } else { if (instr.is_b_gpr) { - op_b += regs.GetRegisterAsFloat(instr.gpr20); + op_b = regs.GetRegisterAsFloat(instr.gpr20); } else { - op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, - GLSLRegister::Type::Float); + op_b = regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, + GLSLRegister::Type::Float); } } @@ -793,6 +866,10 @@ private: op_b = "abs(" + op_b + ')'; } + if (instr.alu.negate_b) { + op_b = "-(" + op_b + ')'; + } + switch (opcode->GetId()) { case OpCode::Id::MOV_C: case OpCode::Id::MOV_R: { @@ -800,68 +877,53 @@ private: break; } - case OpCode::Id::MOV32_IMM: { - // mov32i doesn't have abs or neg bits. - regs.SetRegisterToFloat(instr.gpr0, 0, GetImmediate32(instr), 1, 1); - break; - } case OpCode::Id::FMUL_C: case OpCode::Id::FMUL_R: case OpCode::Id::FMUL_IMM: { - ASSERT_MSG(!instr.saturate_a, "Unimplemented"); - - regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1, instr.alu.abs_d); - break; - } - case OpCode::Id::FMUL32_IMM: { - // fmul32i doesn't have abs or neg bits. - regs.SetRegisterToFloat( - instr.gpr0, 0, - regs.GetRegisterAsFloat(instr.gpr8) + " * " + GetImmediate32(instr), 1, 1); + regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1, + instr.alu.saturate_d); break; } case OpCode::Id::FADD_C: case OpCode::Id::FADD_R: case OpCode::Id::FADD_IMM: { - ASSERT_MSG(!instr.saturate_a, "Unimplemented"); - - regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, instr.alu.abs_d); + regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, + instr.alu.saturate_d); break; } case OpCode::Id::MUFU: { - ASSERT_MSG(!instr.saturate_a, "Unimplemented"); - switch (instr.sub_op) { case SubOp::Cos: regs.SetRegisterToFloat(instr.gpr0, 0, "cos(" + op_a + ')', 1, 1, - instr.alu.abs_d); + instr.alu.saturate_d); break; case SubOp::Sin: regs.SetRegisterToFloat(instr.gpr0, 0, "sin(" + op_a + ')', 1, 1, - instr.alu.abs_d); + instr.alu.saturate_d); break; case SubOp::Ex2: regs.SetRegisterToFloat(instr.gpr0, 0, "exp2(" + op_a + ')', 1, 1, - instr.alu.abs_d); + instr.alu.saturate_d); break; case SubOp::Lg2: regs.SetRegisterToFloat(instr.gpr0, 0, "log2(" + op_a + ')', 1, 1, - instr.alu.abs_d); + instr.alu.saturate_d); break; case SubOp::Rcp: - regs.SetRegisterToFloat(instr.gpr0, 0, "1.0 / " + op_a, 1, 1, instr.alu.abs_d); + regs.SetRegisterToFloat(instr.gpr0, 0, "1.0 / " + op_a, 1, 1, + instr.alu.saturate_d); break; case SubOp::Rsq: regs.SetRegisterToFloat(instr.gpr0, 0, "inversesqrt(" + op_a + ')', 1, 1, - instr.alu.abs_d); + instr.alu.saturate_d); break; - case SubOp::Min: - regs.SetRegisterToFloat(instr.gpr0, 0, "min(" + op_a + "," + op_b + ')', 1, 1, - instr.alu.abs_d); + case SubOp::Sqrt: + regs.SetRegisterToFloat(instr.gpr0, 0, "sqrt(" + op_a + ')', 1, 1, + instr.alu.saturate_d); break; default: - NGLOG_CRITICAL(HW_GPU, "Unhandled MUFU sub op: {0:x}", - static_cast<unsigned>(instr.sub_op.Value())); + LOG_CRITICAL(HW_GPU, "Unhandled MUFU sub op: {0:x}", + static_cast<unsigned>(instr.sub_op.Value())); UNREACHABLE(); } break; @@ -884,16 +946,31 @@ private: // Currently RRO is only implemented as a register move. // Usage of `abs_b` and `negate_b` here should also be correct. regs.SetRegisterToFloat(instr.gpr0, 0, op_b, 1, 1); - NGLOG_WARNING(HW_GPU, "RRO instruction is incomplete"); + LOG_WARNING(HW_GPU, "RRO instruction is incomplete"); break; } default: { - NGLOG_CRITICAL(HW_GPU, "Unhandled arithmetic instruction: {}", opcode->GetName()); + LOG_CRITICAL(HW_GPU, "Unhandled arithmetic instruction: {}", opcode->GetName()); UNREACHABLE(); } } break; } + case OpCode::Type::ArithmeticImmediate: { + switch (opcode->GetId()) { + case OpCode::Id::MOV32_IMM: { + regs.SetRegisterToFloat(instr.gpr0, 0, GetImmediate32(instr), 1, 1); + break; + } + case OpCode::Id::FMUL32_IMM: { + regs.SetRegisterToFloat( + instr.gpr0, 0, + regs.GetRegisterAsFloat(instr.gpr8) + " * " + GetImmediate32(instr), 1, 1); + break; + } + } + break; + } case OpCode::Type::Bfe: { ASSERT_MSG(!instr.bfe.negate_b, "Unimplemented"); @@ -912,56 +989,13 @@ private: break; } default: { - NGLOG_CRITICAL(HW_GPU, "Unhandled BFE instruction: {}", opcode->GetName()); + LOG_CRITICAL(HW_GPU, "Unhandled BFE instruction: {}", opcode->GetName()); UNREACHABLE(); } } break; } - case OpCode::Type::Logic: { - std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, true); - - if (instr.alu.lop.invert_a) - op_a = "~(" + op_a + ')'; - - switch (opcode->GetId()) { - case OpCode::Id::LOP32I: { - u32 imm = static_cast<u32>(instr.alu.imm20_32.Value()); - - if (instr.alu.lop.invert_b) - imm = ~imm; - - switch (instr.alu.lop.operation) { - case Tegra::Shader::LogicOperation::And: { - regs.SetRegisterToInteger(instr.gpr0, true, 0, - '(' + op_a + " & " + std::to_string(imm) + ')', 1, 1); - break; - } - case Tegra::Shader::LogicOperation::Or: { - regs.SetRegisterToInteger(instr.gpr0, true, 0, - '(' + op_a + " | " + std::to_string(imm) + ')', 1, 1); - break; - } - case Tegra::Shader::LogicOperation::Xor: { - regs.SetRegisterToInteger(instr.gpr0, true, 0, - '(' + op_a + " ^ " + std::to_string(imm) + ')', 1, 1); - break; - } - default: - NGLOG_CRITICAL(HW_GPU, "Unimplemented lop32i operation: {}", - static_cast<u32>(instr.alu.lop.operation.Value())); - UNREACHABLE(); - } - break; - } - default: { - NGLOG_CRITICAL(HW_GPU, "Unhandled logic instruction: {}", opcode->GetName()); - UNREACHABLE(); - } - } - break; - } case OpCode::Type::Shift: { std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, true); @@ -998,21 +1032,46 @@ private: regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " << " + op_b, 1, 1); break; default: { - NGLOG_CRITICAL(HW_GPU, "Unhandled shift instruction: {}", opcode->GetName()); + LOG_CRITICAL(HW_GPU, "Unhandled shift instruction: {}", opcode->GetName()); UNREACHABLE(); } } break; } - case OpCode::Type::ArithmeticInteger: { + case OpCode::Type::ArithmeticIntegerImmediate: { std::string op_a = regs.GetRegisterAsInteger(instr.gpr8); + std::string op_b = std::to_string(instr.alu.imm20_32.Value()); - if (instr.alu_integer.negate_a) - op_a = '-' + op_a; + switch (opcode->GetId()) { + case OpCode::Id::IADD32I: + if (instr.iadd32i.negate_a) + op_a = "-(" + op_a + ')'; - std::string op_b = instr.alu_integer.negate_b ? "-" : ""; + regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1, + instr.iadd32i.saturate != 0); + break; + case OpCode::Id::LOP32I: { + if (instr.alu.lop32i.invert_a) + op_a = "~(" + op_a + ')'; + if (instr.alu.lop32i.invert_b) + op_b = "~(" + op_b + ')'; + + WriteLogicOperation(instr.gpr0, instr.alu.lop32i.operation, op_a, op_b); + break; + } + default: { + LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticIntegerImmediate instruction: {}", + opcode->GetName()); + UNREACHABLE(); + } + } + break; + } + case OpCode::Type::ArithmeticInteger: { + std::string op_a = regs.GetRegisterAsInteger(instr.gpr8); + std::string op_b; if (instr.is_b_imm) { op_b += '(' + std::to_string(instr.alu.GetSignedImm20_20()) + ')'; } else { @@ -1028,22 +1087,63 @@ private: case OpCode::Id::IADD_C: case OpCode::Id::IADD_R: case OpCode::Id::IADD_IMM: { - ASSERT_MSG(!instr.saturate_a, "Unimplemented"); - regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1); + if (instr.alu_integer.negate_a) + op_a = "-(" + op_a + ')'; + + if (instr.alu_integer.negate_b) + op_b = "-(" + op_b + ')'; + + regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1, + instr.alu.saturate_d); break; } case OpCode::Id::ISCADD_C: case OpCode::Id::ISCADD_R: case OpCode::Id::ISCADD_IMM: { + if (instr.alu_integer.negate_a) + op_a = "-(" + op_a + ')'; + + if (instr.alu_integer.negate_b) + op_b = "-(" + op_b + ')'; + std::string shift = std::to_string(instr.alu_integer.shift_amount.Value()); regs.SetRegisterToInteger(instr.gpr0, true, 0, "((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1); break; } + case OpCode::Id::LOP_C: + case OpCode::Id::LOP_R: + case OpCode::Id::LOP_IMM: { + ASSERT_MSG(!instr.alu.lop.unk44, "Unimplemented"); + ASSERT_MSG(instr.alu.lop.pred48 == Pred::UnusedIndex, "Unimplemented"); + + if (instr.alu.lop.invert_a) + op_a = "~(" + op_a + ')'; + + if (instr.alu.lop.invert_b) + op_b = "~(" + op_b + ')'; + + WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b); + break; + } + case OpCode::Id::IMNMX_C: + case OpCode::Id::IMNMX_R: + case OpCode::Id::IMNMX_IMM: { + ASSERT_MSG(instr.imnmx.exchange == Tegra::Shader::IMinMaxExchange::None, + "Unimplemented"); + std::string condition = + GetPredicateCondition(instr.imnmx.pred, instr.imnmx.negate_pred != 0); + std::string parameters = op_a + ',' + op_b; + regs.SetRegisterToInteger(instr.gpr0, instr.imnmx.is_signed, 0, + '(' + condition + ") ? min(" + parameters + ") : max(" + + parameters + ')', + 1, 1); + break; + } default: { - NGLOG_CRITICAL(HW_GPU, "Unhandled ArithmeticInteger instruction: {}", - opcode->GetName()); + LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticInteger instruction: {}", + opcode->GetName()); UNREACHABLE(); } } @@ -1051,8 +1151,6 @@ private: break; } case OpCode::Type::Ffma: { - ASSERT_MSG(!instr.saturate_a, "Unimplemented"); - std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); std::string op_b = instr.ffma.negate_b ? "-" : ""; std::string op_c = instr.ffma.negate_c ? "-" : ""; @@ -1081,38 +1179,38 @@ private: break; } default: { - NGLOG_CRITICAL(HW_GPU, "Unhandled FFMA instruction: {}", opcode->GetName()); + LOG_CRITICAL(HW_GPU, "Unhandled FFMA instruction: {}", opcode->GetName()); UNREACHABLE(); } } - regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b + " + " + op_c, 1, 1); + regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b + " + " + op_c, 1, 1, + instr.alu.saturate_d); break; } case OpCode::Type::Conversion: { - ASSERT_MSG(instr.conversion.size == Register::Size::Word, "Unimplemented"); ASSERT_MSG(!instr.conversion.negate_a, "Unimplemented"); - ASSERT_MSG(!instr.saturate_a, "Unimplemented"); switch (opcode->GetId()) { case OpCode::Id::I2I_R: { ASSERT_MSG(!instr.conversion.selector, "Unimplemented"); - std::string op_a = - regs.GetRegisterAsInteger(instr.gpr20, 0, instr.conversion.is_input_signed); + std::string op_a = regs.GetRegisterAsInteger( + instr.gpr20, 0, instr.conversion.is_input_signed, instr.conversion.src_size); if (instr.conversion.abs_a) { op_a = "abs(" + op_a + ')'; } regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1, - 1); + 1, instr.alu.saturate_d, 0, instr.conversion.dest_size); break; } case OpCode::Id::I2F_R: { + ASSERT_MSG(instr.conversion.dest_size == Register::Size::Word, "Unimplemented"); ASSERT_MSG(!instr.conversion.selector, "Unimplemented"); - std::string op_a = - regs.GetRegisterAsInteger(instr.gpr20, 0, instr.conversion.is_input_signed); + std::string op_a = regs.GetRegisterAsInteger( + instr.gpr20, 0, instr.conversion.is_input_signed, instr.conversion.src_size); if (instr.conversion.abs_a) { op_a = "abs(" + op_a + ')'; @@ -1122,13 +1220,16 @@ private: break; } case OpCode::Id::F2F_R: { - ASSERT_MSG(!instr.saturate_a, "Unimplemented"); - + ASSERT_MSG(instr.conversion.dest_size == Register::Size::Word, "Unimplemented"); + ASSERT_MSG(instr.conversion.src_size == Register::Size::Word, "Unimplemented"); std::string op_a = regs.GetRegisterAsFloat(instr.gpr20); switch (instr.conversion.f2f.rounding) { case Tegra::Shader::F2fRoundingOp::None: break; + case Tegra::Shader::F2fRoundingOp::Round: + op_a = "roundEven(" + op_a + ')'; + break; case Tegra::Shader::F2fRoundingOp::Floor: op_a = "floor(" + op_a + ')'; break; @@ -1139,8 +1240,8 @@ private: op_a = "trunc(" + op_a + ')'; break; default: - NGLOG_CRITICAL(HW_GPU, "Unimplemented f2f rounding mode {}", - static_cast<u32>(instr.conversion.f2f.rounding.Value())); + LOG_CRITICAL(HW_GPU, "Unimplemented f2f rounding mode {}", + static_cast<u32>(instr.conversion.f2f.rounding.Value())); UNREACHABLE(); break; } @@ -1149,10 +1250,11 @@ private: op_a = "abs(" + op_a + ')'; } - regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); + regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, instr.alu.saturate_d); break; } case OpCode::Id::F2I_R: { + ASSERT_MSG(instr.conversion.src_size == Register::Size::Word, "Unimplemented"); std::string op_a = regs.GetRegisterAsFloat(instr.gpr20); if (instr.conversion.abs_a) { @@ -1172,8 +1274,8 @@ private: op_a = "trunc(" + op_a + ')'; break; default: - NGLOG_CRITICAL(HW_GPU, "Unimplemented f2i rounding mode {}", - static_cast<u32>(instr.conversion.f2i.rounding.Value())); + LOG_CRITICAL(HW_GPU, "Unimplemented f2i rounding mode {}", + static_cast<u32>(instr.conversion.f2i.rounding.Value())); UNREACHABLE(); break; } @@ -1185,11 +1287,11 @@ private: } regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1, - 1); + 1, false, 0, instr.conversion.dest_size); break; } default: { - NGLOG_CRITICAL(HW_GPU, "Unhandled conversion instruction: {}", opcode->GetName()); + LOG_CRITICAL(HW_GPU, "Unhandled conversion instruction: {}", opcode->GetName()); UNREACHABLE(); } } @@ -1224,8 +1326,8 @@ private: break; default: - NGLOG_CRITICAL(HW_GPU, "Unhandled type: {}", - static_cast<unsigned>(instr.ld_c.type.Value())); + LOG_CRITICAL(HW_GPU, "Unhandled type: {}", + static_cast<unsigned>(instr.ld_c.type.Value())); UNREACHABLE(); } break; @@ -1298,7 +1400,7 @@ private: break; } default: { - NGLOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {}", opcode->GetName()); + LOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {}", opcode->GetName()); UNREACHABLE(); } } @@ -1340,10 +1442,9 @@ private: std::string second_pred = GetPredicateCondition(instr.fsetp.pred39, instr.fsetp.neg_pred != 0); - std::string comparator = GetPredicateComparison(instr.fsetp.cond); std::string combiner = GetPredicateCombiner(instr.fsetp.op); - std::string predicate = '(' + op_a + ") " + comparator + " (" + op_b + ')'; + std::string predicate = GetPredicateComparison(instr.fsetp.cond, op_a, op_b); // Set the primary predicate to the result of Predicate OP SecondPredicate SetPredicate(instr.fsetp.pred3, '(' + predicate + ") " + combiner + " (" + second_pred + ')'); @@ -1378,10 +1479,9 @@ private: std::string second_pred = GetPredicateCondition(instr.isetp.pred39, instr.isetp.neg_pred != 0); - std::string comparator = GetPredicateComparison(instr.isetp.cond); std::string combiner = GetPredicateCombiner(instr.isetp.op); - std::string predicate = '(' + op_a + ") " + comparator + " (" + op_b + ')'; + std::string predicate = GetPredicateComparison(instr.isetp.cond, op_a, op_b); // Set the primary predicate to the result of Predicate OP SecondPredicate SetPredicate(instr.isetp.pred3, '(' + predicate + ") " + combiner + " (" + second_pred + ')'); @@ -1394,6 +1494,36 @@ private: } break; } + case OpCode::Type::PredicateSetPredicate: { + std::string op_a = + GetPredicateCondition(instr.psetp.pred12, instr.psetp.neg_pred12 != 0); + std::string op_b = + GetPredicateCondition(instr.psetp.pred29, instr.psetp.neg_pred29 != 0); + + using Tegra::Shader::Pred; + // We can't use the constant predicate as destination. + ASSERT(instr.psetp.pred3 != static_cast<u64>(Pred::UnusedIndex)); + + std::string second_pred = + GetPredicateCondition(instr.psetp.pred39, instr.psetp.neg_pred39 != 0); + + std::string combiner = GetPredicateCombiner(instr.psetp.op); + + std::string predicate = + '(' + op_a + ") " + GetPredicateCombiner(instr.psetp.cond) + " (" + op_b + ')'; + + // Set the primary predicate to the result of Predicate OP SecondPredicate + SetPredicate(instr.psetp.pred3, + '(' + predicate + ") " + combiner + " (" + second_pred + ')'); + + if (instr.psetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) { + // Set the secondary predicate to the result of !Predicate OP SecondPredicate, + // if enabled + SetPredicate(instr.psetp.pred0, + "!(" + predicate + ") " + combiner + " (" + second_pred + ')'); + } + break; + } case OpCode::Type::FloatSet: { std::string op_a = instr.fset.neg_a ? "-" : ""; op_a += regs.GetRegisterAsFloat(instr.gpr8); @@ -1428,11 +1558,10 @@ private: std::string second_pred = GetPredicateCondition(instr.fset.pred39, instr.fset.neg_pred != 0); - std::string comparator = GetPredicateComparison(instr.fset.cond); std::string combiner = GetPredicateCombiner(instr.fset.op); - std::string predicate = "(((" + op_a + ") " + comparator + " (" + op_b + ")) " + - combiner + " (" + second_pred + "))"; + std::string predicate = "((" + GetPredicateComparison(instr.fset.cond, op_a, op_b) + + ") " + combiner + " (" + second_pred + "))"; if (instr.fset.bf) { regs.SetRegisterToFloat(instr.gpr0, 0, predicate + " ? 1.0 : 0.0", 1, 1); @@ -1463,11 +1592,10 @@ private: std::string second_pred = GetPredicateCondition(instr.iset.pred39, instr.iset.neg_pred != 0); - std::string comparator = GetPredicateComparison(instr.iset.cond); std::string combiner = GetPredicateCombiner(instr.iset.op); - std::string predicate = "(((" + op_a + ") " + comparator + " (" + op_b + ")) " + - combiner + " (" + second_pred + "))"; + std::string predicate = "((" + GetPredicateComparison(instr.iset.cond, op_a, op_b) + + ") " + combiner + " (" + second_pred + "))"; if (instr.iset.bf) { regs.SetRegisterToFloat(instr.gpr0, 0, predicate + " ? 1.0 : 0.0", 1, 1); @@ -1518,8 +1646,15 @@ private: // can ignore this when generating GLSL code. break; } + case OpCode::Id::DEPBAR: + case OpCode::Id::SYNC: { + // TODO(Subv): Find out if we actually have to care about these instructions or if + // the GLSL compiler takes care of that for us. + LOG_WARNING(HW_GPU, "DEPBAR/SYNC instruction is stubbed"); + break; + } default: { - NGLOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName()); + LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName()); UNREACHABLE(); } } @@ -1646,7 +1781,10 @@ private: }; // namespace Decompiler std::string GetCommonDeclarations() { - return "bool exec_shader();"; + std::string declarations = "bool exec_shader();\n"; + declarations += "#define MAX_CONSTBUFFER_ELEMENTS " + + std::to_string(RasterizerOpenGL::MaxConstbufferSize / (sizeof(GLvec4))); + return declarations; } boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset, @@ -1656,7 +1794,7 @@ boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, GLSLGenerator generator(subroutines, program_code, main_offset, stage); return ProgramResult{generator.GetShaderCode(), generator.GetEntries()}; } catch (const DecompileFail& exception) { - NGLOG_ERROR(HW_GPU, "Shader decompilation failed: {}", exception.what()); + LOG_ERROR(HW_GPU, "Shader decompilation failed: {}", exception.what()); } return boost::none; } diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index b88d592b78..c1e6fac9f2 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -39,6 +39,10 @@ void main() { // Viewport can be flipped, which is unsupported by glViewport position.xy *= viewport_flip.xy; gl_Position = position; + + // TODO(bunnei): This is likely a hack, position.w should be interpolated as 1.0 + // For now, this is here to bring order in lieu of proper emulation + position.w = 1.0; } )"; out += program.first; diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp index 7c00beb33a..d7167b2989 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.cpp +++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp @@ -38,8 +38,8 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& sh const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; // TODO(bunnei): Support more than one viewport - viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0 : 1.0; - viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0 : 1.0; + viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f; + viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f; } } // namespace GLShader diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp index 8568fface0..3c087d6385 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.cpp +++ b/src/video_core/renderer_opengl/gl_shader_util.cpp @@ -27,7 +27,7 @@ GLuint LoadShader(const char* source, GLenum type) { } GLuint shader_id = glCreateShader(type); glShaderSource(shader_id, 1, &source, nullptr); - NGLOG_DEBUG(Render_OpenGL, "Compiling {} shader...", debug_type); + LOG_DEBUG(Render_OpenGL, "Compiling {} shader...", debug_type); glCompileShader(shader_id); GLint result = GL_FALSE; @@ -39,9 +39,9 @@ GLuint LoadShader(const char* source, GLenum type) { std::string shader_error(info_log_length, ' '); glGetShaderInfoLog(shader_id, info_log_length, nullptr, &shader_error[0]); if (result == GL_TRUE) { - NGLOG_DEBUG(Render_OpenGL, "{}", shader_error); + LOG_DEBUG(Render_OpenGL, "{}", shader_error); } else { - NGLOG_ERROR(Render_OpenGL, "Error compiling {} shader:\n{}", debug_type, shader_error); + LOG_ERROR(Render_OpenGL, "Error compiling {} shader:\n{}", debug_type, shader_error); } } return shader_id; diff --git a/src/video_core/renderer_opengl/gl_shader_util.h b/src/video_core/renderer_opengl/gl_shader_util.h index 2036a06a97..0e4d782e2b 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.h +++ b/src/video_core/renderer_opengl/gl_shader_util.h @@ -29,7 +29,7 @@ void LogShaderSource(T... shaders) { std::string source(source_length, ' '); glGetShaderSource(shader, source_length, nullptr, &source[0]); - NGLOG_INFO(Render_OpenGL, "Shader source {}", source); + LOG_INFO(Render_OpenGL, "Shader source {}", source); } } @@ -49,7 +49,7 @@ GLuint LoadShader(const char* source, GLenum type); template <typename... T> GLuint LoadProgram(bool separable_program, T... shaders) { // Link the program - NGLOG_DEBUG(Render_OpenGL, "Linking program..."); + LOG_DEBUG(Render_OpenGL, "Linking program..."); GLuint program_id = glCreateProgram(); @@ -71,9 +71,9 @@ GLuint LoadProgram(bool separable_program, T... shaders) { std::string program_error(info_log_length, ' '); glGetProgramInfoLog(program_id, info_log_length, nullptr, &program_error[0]); if (result == GL_TRUE) { - NGLOG_DEBUG(Render_OpenGL, "{}", program_error); + LOG_DEBUG(Render_OpenGL, "{}", program_error); } else { - NGLOG_ERROR(Render_OpenGL, "Error linking shader:\n{}", program_error); + LOG_ERROR(Render_OpenGL, "Error linking shader:\n{}", program_error); } } diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 44f0c8a010..2e8a422a85 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -48,24 +48,9 @@ OpenGLState::OpenGLState() { logic_op = GL_COPY; for (auto& texture_unit : texture_units) { - texture_unit.texture_2d = 0; - texture_unit.sampler = 0; - texture_unit.swizzle.r = GL_RED; - texture_unit.swizzle.g = GL_GREEN; - texture_unit.swizzle.b = GL_BLUE; - texture_unit.swizzle.a = GL_ALPHA; + texture_unit.Reset(); } - lighting_lut.texture_buffer = 0; - - fog_lut.texture_buffer = 0; - - proctex_lut.texture_buffer = 0; - proctex_diff_lut.texture_buffer = 0; - proctex_color_map.texture_buffer = 0; - proctex_alpha_map.texture_buffer = 0; - proctex_noise_lut.texture_buffer = 0; - draw.read_framebuffer = 0; draw.draw_framebuffer = 0; draw.vertex_array = 0; @@ -196,13 +181,13 @@ void OpenGLState::Apply() const { } // Textures - for (size_t i = 0; i < std::size(texture_units); ++i) { + for (int i = 0; i < std::size(texture_units); ++i) { if (texture_units[i].texture_2d != cur_state.texture_units[i].texture_2d) { glActiveTexture(TextureUnits::MaxwellTexture(i).Enum()); glBindTexture(GL_TEXTURE_2D, texture_units[i].texture_2d); } if (texture_units[i].sampler != cur_state.texture_units[i].sampler) { - glBindSampler(i, texture_units[i].sampler); + glBindSampler(static_cast<GLuint>(i), texture_units[i].sampler); } // Update the texture swizzle if (texture_units[i].swizzle.r != cur_state.texture_units[i].swizzle.r || @@ -223,54 +208,12 @@ void OpenGLState::Apply() const { if (current.enabled != new_state.enabled || current.bindpoint != new_state.bindpoint || current.ssbo != new_state.ssbo) { if (new_state.enabled) { - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, new_state.bindpoint, new_state.ssbo); + glBindBufferBase(GL_UNIFORM_BUFFER, new_state.bindpoint, new_state.ssbo); } } } } - // Lighting LUTs - if (lighting_lut.texture_buffer != cur_state.lighting_lut.texture_buffer) { - glActiveTexture(TextureUnits::LightingLUT.Enum()); - glBindTexture(GL_TEXTURE_BUFFER, lighting_lut.texture_buffer); - } - - // Fog LUT - if (fog_lut.texture_buffer != cur_state.fog_lut.texture_buffer) { - glActiveTexture(TextureUnits::FogLUT.Enum()); - glBindTexture(GL_TEXTURE_BUFFER, fog_lut.texture_buffer); - } - - // ProcTex Noise LUT - if (proctex_noise_lut.texture_buffer != cur_state.proctex_noise_lut.texture_buffer) { - glActiveTexture(TextureUnits::ProcTexNoiseLUT.Enum()); - glBindTexture(GL_TEXTURE_BUFFER, proctex_noise_lut.texture_buffer); - } - - // ProcTex Color Map - if (proctex_color_map.texture_buffer != cur_state.proctex_color_map.texture_buffer) { - glActiveTexture(TextureUnits::ProcTexColorMap.Enum()); - glBindTexture(GL_TEXTURE_BUFFER, proctex_color_map.texture_buffer); - } - - // ProcTex Alpha Map - if (proctex_alpha_map.texture_buffer != cur_state.proctex_alpha_map.texture_buffer) { - glActiveTexture(TextureUnits::ProcTexAlphaMap.Enum()); - glBindTexture(GL_TEXTURE_BUFFER, proctex_alpha_map.texture_buffer); - } - - // ProcTex LUT - if (proctex_lut.texture_buffer != cur_state.proctex_lut.texture_buffer) { - glActiveTexture(TextureUnits::ProcTexLUT.Enum()); - glBindTexture(GL_TEXTURE_BUFFER, proctex_lut.texture_buffer); - } - - // ProcTex Diff LUT - if (proctex_diff_lut.texture_buffer != cur_state.proctex_diff_lut.texture_buffer) { - glActiveTexture(TextureUnits::ProcTexDiffLUT.Enum()); - glBindTexture(GL_TEXTURE_BUFFER, proctex_diff_lut.texture_buffer); - } - // Framebuffer if (draw.read_framebuffer != cur_state.draw.read_framebuffer) { glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer); @@ -338,26 +281,12 @@ void OpenGLState::Apply() const { cur_state = *this; } -OpenGLState& OpenGLState::ResetTexture(GLuint handle) { +OpenGLState& OpenGLState::UnbindTexture(GLuint handle) { for (auto& unit : texture_units) { if (unit.texture_2d == handle) { - unit.texture_2d = 0; + unit.Unbind(); } } - if (lighting_lut.texture_buffer == handle) - lighting_lut.texture_buffer = 0; - if (fog_lut.texture_buffer == handle) - fog_lut.texture_buffer = 0; - if (proctex_noise_lut.texture_buffer == handle) - proctex_noise_lut.texture_buffer = 0; - if (proctex_color_map.texture_buffer == handle) - proctex_color_map.texture_buffer = 0; - if (proctex_alpha_map.texture_buffer == handle) - proctex_alpha_map.texture_buffer = 0; - if (proctex_lut.texture_buffer == handle) - proctex_lut.texture_buffer = 0; - if (proctex_diff_lut.texture_buffer == handle) - proctex_diff_lut.texture_buffer = 0; return *this; } diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index 839e50e935..3398d7c04e 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -91,35 +91,20 @@ public: GLint b; // GL_TEXTURE_SWIZZLE_B GLint a; // GL_TEXTURE_SWIZZLE_A } swizzle; - } texture_units[32]; - - struct { - GLuint texture_buffer; // GL_TEXTURE_BINDING_BUFFER - } lighting_lut; - - struct { - GLuint texture_buffer; // GL_TEXTURE_BINDING_BUFFER - } fog_lut; - - struct { - GLuint texture_buffer; // GL_TEXTURE_BINDING_BUFFER - } proctex_noise_lut; - struct { - GLuint texture_buffer; // GL_TEXTURE_BINDING_BUFFER - } proctex_color_map; - - struct { - GLuint texture_buffer; // GL_TEXTURE_BINDING_BUFFER - } proctex_alpha_map; - - struct { - GLuint texture_buffer; // GL_TEXTURE_BINDING_BUFFER - } proctex_lut; - - struct { - GLuint texture_buffer; // GL_TEXTURE_BINDING_BUFFER - } proctex_diff_lut; + void Unbind() { + texture_2d = 0; + swizzle.r = GL_RED; + swizzle.g = GL_GREEN; + swizzle.b = GL_BLUE; + swizzle.a = GL_ALPHA; + } + + void Reset() { + Unbind(); + sampler = 0; + } + } texture_units[32]; struct { GLuint read_framebuffer; // GL_READ_FRAMEBUFFER_BINDING @@ -165,7 +150,7 @@ public: void Apply() const; /// Resets any references to the given resource - OpenGLState& ResetTexture(GLuint handle); + OpenGLState& UnbindTexture(GLuint handle); OpenGLState& ResetSampler(GLuint handle); OpenGLState& ResetProgram(GLuint handle); OpenGLState& ResetPipeline(GLuint handle); diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index 2155fb019a..e19c3b280f 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h @@ -29,9 +29,13 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) { switch (attrib.size) { case Maxwell::VertexAttribute::Size::Size_8_8_8_8: return GL_UNSIGNED_BYTE; + case Maxwell::VertexAttribute::Size::Size_16_16: + return GL_UNSIGNED_SHORT; + case Maxwell::VertexAttribute::Size::Size_10_10_10_2: + return GL_UNSIGNED_INT_2_10_10_10_REV; } - NGLOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); + LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); UNREACHABLE(); return {}; } @@ -41,9 +45,13 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) { switch (attrib.size) { case Maxwell::VertexAttribute::Size::Size_8_8_8_8: return GL_BYTE; + case Maxwell::VertexAttribute::Size::Size_16_16: + return GL_SHORT; + case Maxwell::VertexAttribute::Size::Size_10_10_10_2: + return GL_INT_2_10_10_10_REV; } - NGLOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); + LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); UNREACHABLE(); return {}; } @@ -52,7 +60,7 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) { return GL_FLOAT; } - NGLOG_CRITICAL(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString()); + LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString()); UNREACHABLE(); return {}; } @@ -66,7 +74,7 @@ inline GLenum IndexFormat(Maxwell::IndexFormat index_format) { case Maxwell::IndexFormat::UnsignedInt: return GL_UNSIGNED_INT; } - NGLOG_CRITICAL(Render_OpenGL, "Unimplemented index_format={}", static_cast<u32>(index_format)); + LOG_CRITICAL(Render_OpenGL, "Unimplemented index_format={}", static_cast<u32>(index_format)); UNREACHABLE(); return {}; } @@ -78,7 +86,7 @@ inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) { case Maxwell::PrimitiveTopology::TriangleStrip: return GL_TRIANGLE_STRIP; } - NGLOG_CRITICAL(Render_OpenGL, "Unimplemented topology={}", static_cast<u32>(topology)); + LOG_CRITICAL(Render_OpenGL, "Unimplemented topology={}", static_cast<u32>(topology)); UNREACHABLE(); return {}; } @@ -90,8 +98,8 @@ inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode) { case Tegra::Texture::TextureFilter::Nearest: return GL_NEAREST; } - NGLOG_CRITICAL(Render_OpenGL, "Unimplemented texture filter mode={}", - static_cast<u32>(filter_mode)); + LOG_CRITICAL(Render_OpenGL, "Unimplemented texture filter mode={}", + static_cast<u32>(filter_mode)); UNREACHABLE(); return {}; } @@ -110,8 +118,7 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) { // manually mix them. However the shader part of this is not yet implemented. return GL_CLAMP_TO_BORDER; } - NGLOG_CRITICAL(Render_OpenGL, "Unimplemented texture wrap mode={}", - static_cast<u32>(wrap_mode)); + LOG_CRITICAL(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode)); UNREACHABLE(); return {}; } @@ -129,7 +136,7 @@ inline GLenum BlendEquation(Maxwell::Blend::Equation equation) { case Maxwell::Blend::Equation::Max: return GL_MAX; } - NGLOG_CRITICAL(Render_OpenGL, "Unimplemented blend equation={}", static_cast<u32>(equation)); + LOG_CRITICAL(Render_OpenGL, "Unimplemented blend equation={}", static_cast<u32>(equation)); UNREACHABLE(); return {}; } @@ -175,7 +182,7 @@ inline GLenum BlendFunc(Maxwell::Blend::Factor factor) { case Maxwell::Blend::Factor::OneMinusConstantAlpha: return GL_ONE_MINUS_CONSTANT_ALPHA; } - NGLOG_CRITICAL(Render_OpenGL, "Unimplemented blend factor={}", static_cast<u32>(factor)); + LOG_CRITICAL(Render_OpenGL, "Unimplemented blend factor={}", static_cast<u32>(factor)); UNREACHABLE(); return {}; } @@ -196,7 +203,65 @@ inline GLenum SwizzleSource(Tegra::Texture::SwizzleSource source) { case Tegra::Texture::SwizzleSource::OneFloat: return GL_ONE; } - NGLOG_CRITICAL(Render_OpenGL, "Unimplemented swizzle source={}", static_cast<u32>(source)); + LOG_CRITICAL(Render_OpenGL, "Unimplemented swizzle source={}", static_cast<u32>(source)); + UNREACHABLE(); + return {}; +} + +inline GLenum ComparisonOp(Maxwell::ComparisonOp comparison) { + switch (comparison) { + case Maxwell::ComparisonOp::Never: + case Maxwell::ComparisonOp::NeverOld: + return GL_NEVER; + case Maxwell::ComparisonOp::Less: + case Maxwell::ComparisonOp::LessOld: + return GL_LESS; + case Maxwell::ComparisonOp::Equal: + case Maxwell::ComparisonOp::EqualOld: + return GL_EQUAL; + case Maxwell::ComparisonOp::LessEqual: + case Maxwell::ComparisonOp::LessEqualOld: + return GL_LEQUAL; + case Maxwell::ComparisonOp::Greater: + case Maxwell::ComparisonOp::GreaterOld: + return GL_GREATER; + case Maxwell::ComparisonOp::NotEqual: + case Maxwell::ComparisonOp::NotEqualOld: + return GL_NOTEQUAL; + case Maxwell::ComparisonOp::GreaterEqual: + case Maxwell::ComparisonOp::GreaterEqualOld: + return GL_GEQUAL; + case Maxwell::ComparisonOp::Always: + case Maxwell::ComparisonOp::AlwaysOld: + return GL_ALWAYS; + } + LOG_CRITICAL(Render_OpenGL, "Unimplemented comparison op={}", static_cast<u32>(comparison)); + UNREACHABLE(); + return {}; +} + +inline GLenum FrontFace(Maxwell::Cull::FrontFace front_face) { + switch (front_face) { + case Maxwell::Cull::FrontFace::ClockWise: + return GL_CW; + case Maxwell::Cull::FrontFace::CounterClockWise: + return GL_CCW; + } + LOG_CRITICAL(Render_OpenGL, "Unimplemented front face cull={}", static_cast<u32>(front_face)); + UNREACHABLE(); + return {}; +} + +inline GLenum CullFace(Maxwell::Cull::CullFace cull_face) { + switch (cull_face) { + case Maxwell::Cull::CullFace::Front: + return GL_FRONT; + case Maxwell::Cull::CullFace::Back: + return GL_BACK; + case Maxwell::Cull::CullFace::FrontAndBack: + return GL_FRONT_AND_BACK; + } + LOG_CRITICAL(Render_OpenGL, "Unimplemented cull face={}", static_cast<u32>(cull_face)); UNREACHABLE(); return {}; } diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index f33766bfdf..00841e9375 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -150,7 +150,6 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf screen_info)) { // Reset the screen info's display texture to its own permanent texture screen_info.display_texture = screen_info.texture.resource.handle; - screen_info.display_texcoords = MathUtil::Rectangle<float>(0.f, 0.f, 1.f, 1.f); Memory::RasterizerFlushVirtualRegion(framebuffer_addr, size_in_bytes, Memory::FlushMode::Flush); @@ -302,8 +301,8 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x, right = texcoords.left; } else { // Other transformations are unsupported - NGLOG_CRITICAL(Render_OpenGL, "Unsupported framebuffer_transform_flags={}", - static_cast<u32>(framebuffer_transform_flags)); + LOG_CRITICAL(Render_OpenGL, "Unsupported framebuffer_transform_flags={}", + static_cast<u32>(framebuffer_transform_flags)); UNIMPLEMENTED(); } } @@ -405,14 +404,14 @@ static void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum switch (severity) { case GL_DEBUG_SEVERITY_HIGH: - NGLOG_ERROR(Render_OpenGL, format, str_source, str_type, id, message); + LOG_ERROR(Render_OpenGL, format, str_source, str_type, id, message); break; case GL_DEBUG_SEVERITY_MEDIUM: - NGLOG_WARNING(Render_OpenGL, format, str_source, str_type, id, message); + LOG_WARNING(Render_OpenGL, format, str_source, str_type, id, message); break; case GL_DEBUG_SEVERITY_NOTIFICATION: case GL_DEBUG_SEVERITY_LOW: - NGLOG_DEBUG(Render_OpenGL, format, str_source, str_type, id, message); + LOG_DEBUG(Render_OpenGL, format, str_source, str_type, id, message); break; } } @@ -430,9 +429,9 @@ bool RendererOpenGL::Init() { const char* gpu_vendor{reinterpret_cast<char const*>(glGetString(GL_VENDOR))}; const char* gpu_model{reinterpret_cast<char const*>(glGetString(GL_RENDERER))}; - NGLOG_INFO(Render_OpenGL, "GL_VERSION: {}", gl_version); - NGLOG_INFO(Render_OpenGL, "GL_VENDOR: {}", gpu_vendor); - NGLOG_INFO(Render_OpenGL, "GL_RENDERER: {}", gpu_model); + LOG_INFO(Render_OpenGL, "GL_VERSION: {}", gl_version); + LOG_INFO(Render_OpenGL, "GL_VENDOR: {}", gpu_vendor); + LOG_INFO(Render_OpenGL, "GL_RENDERER: {}", gpu_model); Core::Telemetry().AddField(Telemetry::FieldType::UserSystem, "GPU_Vendor", gpu_vendor); Core::Telemetry().AddField(Telemetry::FieldType::UserSystem, "GPU_Model", gpu_model); diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 2cc6d9a001..21f0d298ce 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -27,7 +27,7 @@ struct TextureInfo { /// Structure used for storing information about the display target for the Switch screen struct ScreenInfo { GLuint display_texture; - MathUtil::Rectangle<float> display_texcoords; + const MathUtil::Rectangle<float> display_texcoords{0.0f, 0.0f, 1.0f, 1.0f}; TextureInfo texture; }; diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp new file mode 100644 index 0000000000..3c4ad1c9d9 --- /dev/null +++ b/src/video_core/textures/astc.cpp @@ -0,0 +1,1646 @@ +// Copyright 2016 The University of North Carolina at Chapel Hill +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Please send all BUG REPORTS to <pavel@cs.unc.edu>. +// <http://gamma.cs.unc.edu/FasTC/> + +#include <algorithm> +#include <cassert> +#include <cstdint> +#include <cstring> +#include <vector> + +#include "video_core/textures/astc.h" + +class BitStream { +public: + BitStream(unsigned char* ptr, int nBits = 0, int start_offset = 0) + : m_BitsWritten(0), m_BitsRead(0), m_NumBits(nBits), m_CurByte(ptr), + m_NextBit(start_offset % 8), done(false) {} + + int GetBitsWritten() const { + return m_BitsWritten; + } + + ~BitStream() {} + + void WriteBitsR(unsigned int val, unsigned int nBits) { + for (unsigned int i = 0; i < nBits; i++) { + WriteBit((val >> (nBits - i - 1)) & 1); + } + } + + void WriteBits(unsigned int val, unsigned int nBits) { + for (unsigned int i = 0; i < nBits; i++) { + WriteBit((val >> i) & 1); + } + } + + int GetBitsRead() const { + return m_BitsRead; + } + + int ReadBit() { + + int bit = *m_CurByte >> m_NextBit++; + while (m_NextBit >= 8) { + m_NextBit -= 8; + m_CurByte++; + } + + m_BitsRead++; + return bit & 1; + } + + unsigned int ReadBits(unsigned int nBits) { + unsigned int ret = 0; + for (unsigned int i = 0; i < nBits; i++) { + ret |= (ReadBit() & 1) << i; + } + return ret; + } + +private: + void WriteBit(int b) { + + if (done) + return; + + const unsigned int mask = 1 << m_NextBit++; + + // clear the bit + *m_CurByte &= ~mask; + + // Write the bit, if necessary + if (b) + *m_CurByte |= mask; + + // Next byte? + if (m_NextBit >= 8) { + m_CurByte += 1; + m_NextBit = 0; + } + + done = done || ++m_BitsWritten >= m_NumBits; + } + + int m_BitsWritten; + const int m_NumBits; + unsigned char* m_CurByte; + int m_NextBit; + int m_BitsRead; + + bool done; +}; + +template <typename IntType> +class Bits { +private: + const IntType& m_Bits; + + // Don't copy + Bits() {} + Bits(const Bits&) {} + Bits& operator=(const Bits&) {} + +public: + explicit Bits(IntType& v) : m_Bits(v) {} + + uint8_t operator[](uint32_t bitPos) { + return static_cast<uint8_t>((m_Bits >> bitPos) & 1); + } + + IntType operator()(uint32_t start, uint32_t end) { + if (start == end) { + return (*this)[start]; + } else if (start > end) { + uint32_t t = start; + start = end; + end = t; + } + + uint64_t mask = (1 << (end - start + 1)) - 1; + return (m_Bits >> start) & mask; + } +}; + +enum EIntegerEncoding { eIntegerEncoding_JustBits, eIntegerEncoding_Quint, eIntegerEncoding_Trit }; + +class IntegerEncodedValue { +private: + const EIntegerEncoding m_Encoding; + const uint32_t m_NumBits; + uint32_t m_BitValue; + union { + uint32_t m_QuintValue; + uint32_t m_TritValue; + }; + +public: + // Jank, but we're not doing any heavy lifting in this class, so it's + // probably OK. It allows us to use these in std::vectors... + IntegerEncodedValue& operator=(const IntegerEncodedValue& other) { + new (this) IntegerEncodedValue(other); + return *this; + } + + IntegerEncodedValue(EIntegerEncoding encoding, uint32_t numBits) + : m_Encoding(encoding), m_NumBits(numBits) {} + + EIntegerEncoding GetEncoding() const { + return m_Encoding; + } + uint32_t BaseBitLength() const { + return m_NumBits; + } + + uint32_t GetBitValue() const { + return m_BitValue; + } + void SetBitValue(uint32_t val) { + m_BitValue = val; + } + + uint32_t GetTritValue() const { + return m_TritValue; + } + void SetTritValue(uint32_t val) { + m_TritValue = val; + } + + uint32_t GetQuintValue() const { + return m_QuintValue; + } + void SetQuintValue(uint32_t val) { + m_QuintValue = val; + } + + bool MatchesEncoding(const IntegerEncodedValue& other) { + return m_Encoding == other.m_Encoding && m_NumBits == other.m_NumBits; + } + + // Returns the number of bits required to encode nVals values. + uint32_t GetBitLength(uint32_t nVals) { + uint32_t totalBits = m_NumBits * nVals; + if (m_Encoding == eIntegerEncoding_Trit) { + totalBits += (nVals * 8 + 4) / 5; + } else if (m_Encoding == eIntegerEncoding_Quint) { + totalBits += (nVals * 7 + 2) / 3; + } + return totalBits; + } + + // Count the number of bits set in a number. + static inline uint32_t Popcnt(uint32_t n) { + uint32_t c; + for (c = 0; n; c++) { + n &= n - 1; + } + return c; + } + + // Returns a new instance of this struct that corresponds to the + // can take no more than maxval values + static IntegerEncodedValue CreateEncoding(uint32_t maxVal) { + while (maxVal > 0) { + uint32_t check = maxVal + 1; + + // Is maxVal a power of two? + if (!(check & (check - 1))) { + return IntegerEncodedValue(eIntegerEncoding_JustBits, Popcnt(maxVal)); + } + + // Is maxVal of the type 3*2^n - 1? + if ((check % 3 == 0) && !((check / 3) & ((check / 3) - 1))) { + return IntegerEncodedValue(eIntegerEncoding_Trit, Popcnt(check / 3 - 1)); + } + + // Is maxVal of the type 5*2^n - 1? + if ((check % 5 == 0) && !((check / 5) & ((check / 5) - 1))) { + return IntegerEncodedValue(eIntegerEncoding_Quint, Popcnt(check / 5 - 1)); + } + + // Apparently it can't be represented with a bounded integer sequence... + // just iterate. + maxVal--; + } + return IntegerEncodedValue(eIntegerEncoding_JustBits, 0); + } + + // Fills result with the values that are encoded in the given + // bitstream. We must know beforehand what the maximum possible + // value is, and how many values we're decoding. + static void DecodeIntegerSequence(std::vector<IntegerEncodedValue>& result, BitStream& bits, + uint32_t maxRange, uint32_t nValues) { + // Determine encoding parameters + IntegerEncodedValue val = IntegerEncodedValue::CreateEncoding(maxRange); + + // Start decoding + uint32_t nValsDecoded = 0; + while (nValsDecoded < nValues) { + switch (val.GetEncoding()) { + case eIntegerEncoding_Quint: + DecodeQuintBlock(bits, result, val.BaseBitLength()); + nValsDecoded += 3; + break; + + case eIntegerEncoding_Trit: + DecodeTritBlock(bits, result, val.BaseBitLength()); + nValsDecoded += 5; + break; + + case eIntegerEncoding_JustBits: + val.SetBitValue(bits.ReadBits(val.BaseBitLength())); + result.push_back(val); + nValsDecoded++; + break; + } + } + } + +private: + static void DecodeTritBlock(BitStream& bits, std::vector<IntegerEncodedValue>& result, + uint32_t nBitsPerValue) { + // Implement the algorithm in section C.2.12 + uint32_t m[5]; + uint32_t t[5]; + uint32_t T; + + // Read the trit encoded block according to + // table C.2.14 + m[0] = bits.ReadBits(nBitsPerValue); + T = bits.ReadBits(2); + m[1] = bits.ReadBits(nBitsPerValue); + T |= bits.ReadBits(2) << 2; + m[2] = bits.ReadBits(nBitsPerValue); + T |= bits.ReadBit() << 4; + m[3] = bits.ReadBits(nBitsPerValue); + T |= bits.ReadBits(2) << 5; + m[4] = bits.ReadBits(nBitsPerValue); + T |= bits.ReadBit() << 7; + + uint32_t C = 0; + + Bits<uint32_t> Tb(T); + if (Tb(2, 4) == 7) { + C = (Tb(5, 7) << 2) | Tb(0, 1); + t[4] = t[3] = 2; + } else { + C = Tb(0, 4); + if (Tb(5, 6) == 3) { + t[4] = 2; + t[3] = Tb[7]; + } else { + t[4] = Tb[7]; + t[3] = Tb(5, 6); + } + } + + Bits<uint32_t> Cb(C); + if (Cb(0, 1) == 3) { + t[2] = 2; + t[1] = Cb[4]; + t[0] = (Cb[3] << 1) | (Cb[2] & ~Cb[3]); + } else if (Cb(2, 3) == 3) { + t[2] = 2; + t[1] = 2; + t[0] = Cb(0, 1); + } else { + t[2] = Cb[4]; + t[1] = Cb(2, 3); + t[0] = (Cb[1] << 1) | (Cb[0] & ~Cb[1]); + } + + for (uint32_t i = 0; i < 5; i++) { + IntegerEncodedValue val(eIntegerEncoding_Trit, nBitsPerValue); + val.SetBitValue(m[i]); + val.SetTritValue(t[i]); + result.push_back(val); + } + } + + static void DecodeQuintBlock(BitStream& bits, std::vector<IntegerEncodedValue>& result, + uint32_t nBitsPerValue) { + // Implement the algorithm in section C.2.12 + uint32_t m[3]; + uint32_t q[3]; + uint32_t Q; + + // Read the trit encoded block according to + // table C.2.15 + m[0] = bits.ReadBits(nBitsPerValue); + Q = bits.ReadBits(3); + m[1] = bits.ReadBits(nBitsPerValue); + Q |= bits.ReadBits(2) << 3; + m[2] = bits.ReadBits(nBitsPerValue); + Q |= bits.ReadBits(2) << 5; + + Bits<uint32_t> Qb(Q); + if (Qb(1, 2) == 3 && Qb(5, 6) == 0) { + q[0] = q[1] = 4; + q[2] = (Qb[0] << 2) | ((Qb[4] & ~Qb[0]) << 1) | (Qb[3] & ~Qb[0]); + } else { + uint32_t C = 0; + if (Qb(1, 2) == 3) { + q[2] = 4; + C = (Qb(3, 4) << 3) | ((~Qb(5, 6) & 3) << 1) | Qb[0]; + } else { + q[2] = Qb(5, 6); + C = Qb(0, 4); + } + + Bits<uint32_t> Cb(C); + if (Cb(0, 2) == 5) { + q[1] = 4; + q[0] = Cb(3, 4); + } else { + q[1] = Cb(3, 4); + q[0] = Cb(0, 2); + } + } + + for (uint32_t i = 0; i < 3; i++) { + IntegerEncodedValue val(eIntegerEncoding_Quint, nBitsPerValue); + val.m_BitValue = m[i]; + val.m_QuintValue = q[i]; + result.push_back(val); + } + } +}; + +namespace ASTCC { + +struct TexelWeightParams { + uint32_t m_Width; + uint32_t m_Height; + bool m_bDualPlane; + uint32_t m_MaxWeight; + bool m_bError; + bool m_bVoidExtentLDR; + bool m_bVoidExtentHDR; + + TexelWeightParams() { + memset(this, 0, sizeof(*this)); + } + + uint32_t GetPackedBitSize() { + // How many indices do we have? + uint32_t nIdxs = m_Height * m_Width; + if (m_bDualPlane) { + nIdxs *= 2; + } + + return IntegerEncodedValue::CreateEncoding(m_MaxWeight).GetBitLength(nIdxs); + } + + uint32_t GetNumWeightValues() const { + uint32_t ret = m_Width * m_Height; + if (m_bDualPlane) { + ret *= 2; + } + return ret; + } +}; + +TexelWeightParams DecodeBlockInfo(BitStream& strm) { + TexelWeightParams params; + + // Read the entire block mode all at once + uint16_t modeBits = strm.ReadBits(11); + + // Does this match the void extent block mode? + if ((modeBits & 0x01FF) == 0x1FC) { + if (modeBits & 0x200) { + params.m_bVoidExtentHDR = true; + } else { + params.m_bVoidExtentLDR = true; + } + + // Next two bits must be one. + if (!(modeBits & 0x400) || !strm.ReadBit()) { + params.m_bError = true; + } + + return params; + } + + // First check if the last four bits are zero + if ((modeBits & 0xF) == 0) { + params.m_bError = true; + return params; + } + + // If the last two bits are zero, then if bits + // [6-8] are all ones, this is also reserved. + if ((modeBits & 0x3) == 0 && (modeBits & 0x1C0) == 0x1C0) { + params.m_bError = true; + return params; + } + + // Otherwise, there is no error... Figure out the layout + // of the block mode. Layout is determined by a number + // between 0 and 9 corresponding to table C.2.8 of the + // ASTC spec. + uint32_t layout = 0; + + if ((modeBits & 0x1) || (modeBits & 0x2)) { + // layout is in [0-4] + if (modeBits & 0x8) { + // layout is in [2-4] + if (modeBits & 0x4) { + // layout is in [3-4] + if (modeBits & 0x100) { + layout = 4; + } else { + layout = 3; + } + } else { + layout = 2; + } + } else { + // layout is in [0-1] + if (modeBits & 0x4) { + layout = 1; + } else { + layout = 0; + } + } + } else { + // layout is in [5-9] + if (modeBits & 0x100) { + // layout is in [7-9] + if (modeBits & 0x80) { + // layout is in [7-8] + assert((modeBits & 0x40) == 0U); + if (modeBits & 0x20) { + layout = 8; + } else { + layout = 7; + } + } else { + layout = 9; + } + } else { + // layout is in [5-6] + if (modeBits & 0x80) { + layout = 6; + } else { + layout = 5; + } + } + } + + assert(layout < 10); + + // Determine R + uint32_t R = !!(modeBits & 0x10); + if (layout < 5) { + R |= (modeBits & 0x3) << 1; + } else { + R |= (modeBits & 0xC) >> 1; + } + assert(2 <= R && R <= 7); + + // Determine width & height + switch (layout) { + case 0: { + uint32_t A = (modeBits >> 5) & 0x3; + uint32_t B = (modeBits >> 7) & 0x3; + params.m_Width = B + 4; + params.m_Height = A + 2; + break; + } + + case 1: { + uint32_t A = (modeBits >> 5) & 0x3; + uint32_t B = (modeBits >> 7) & 0x3; + params.m_Width = B + 8; + params.m_Height = A + 2; + break; + } + + case 2: { + uint32_t A = (modeBits >> 5) & 0x3; + uint32_t B = (modeBits >> 7) & 0x3; + params.m_Width = A + 2; + params.m_Height = B + 8; + break; + } + + case 3: { + uint32_t A = (modeBits >> 5) & 0x3; + uint32_t B = (modeBits >> 7) & 0x1; + params.m_Width = A + 2; + params.m_Height = B + 6; + break; + } + + case 4: { + uint32_t A = (modeBits >> 5) & 0x3; + uint32_t B = (modeBits >> 7) & 0x1; + params.m_Width = B + 2; + params.m_Height = A + 2; + break; + } + + case 5: { + uint32_t A = (modeBits >> 5) & 0x3; + params.m_Width = 12; + params.m_Height = A + 2; + break; + } + + case 6: { + uint32_t A = (modeBits >> 5) & 0x3; + params.m_Width = A + 2; + params.m_Height = 12; + break; + } + + case 7: { + params.m_Width = 6; + params.m_Height = 10; + break; + } + + case 8: { + params.m_Width = 10; + params.m_Height = 6; + break; + } + + case 9: { + uint32_t A = (modeBits >> 5) & 0x3; + uint32_t B = (modeBits >> 9) & 0x3; + params.m_Width = A + 6; + params.m_Height = B + 6; + break; + } + + default: + assert(!"Don't know this layout..."); + params.m_bError = true; + break; + } + + // Determine whether or not we're using dual planes + // and/or high precision layouts. + bool D = (layout != 9) && (modeBits & 0x400); + bool H = (layout != 9) && (modeBits & 0x200); + + if (H) { + const uint32_t maxWeights[6] = {9, 11, 15, 19, 23, 31}; + params.m_MaxWeight = maxWeights[R - 2]; + } else { + const uint32_t maxWeights[6] = {1, 2, 3, 4, 5, 7}; + params.m_MaxWeight = maxWeights[R - 2]; + } + + params.m_bDualPlane = D; + + return params; +} + +void FillVoidExtentLDR(BitStream& strm, uint32_t* const outBuf, uint32_t blockWidth, + uint32_t blockHeight) { + // Don't actually care about the void extent, just read the bits... + for (int i = 0; i < 4; ++i) { + strm.ReadBits(13); + } + + // Decode the RGBA components and renormalize them to the range [0, 255] + uint16_t r = strm.ReadBits(16); + uint16_t g = strm.ReadBits(16); + uint16_t b = strm.ReadBits(16); + uint16_t a = strm.ReadBits(16); + + uint32_t rgba = (r >> 8) | (g & 0xFF00) | (static_cast<uint32_t>(b) & 0xFF00) << 8 | + (static_cast<uint32_t>(a) & 0xFF00) << 16; + + for (uint32_t j = 0; j < blockHeight; j++) + for (uint32_t i = 0; i < blockWidth; i++) { + outBuf[j * blockWidth + i] = rgba; + } +} + +void FillError(uint32_t* outBuf, uint32_t blockWidth, uint32_t blockHeight) { + for (uint32_t j = 0; j < blockHeight; j++) + for (uint32_t i = 0; i < blockWidth; i++) { + outBuf[j * blockWidth + i] = 0xFFFF00FF; + } +} + +// Replicates low numBits such that [(toBit - 1):(toBit - 1 - fromBit)] +// is the same as [(numBits - 1):0] and repeats all the way down. +template <typename IntType> +IntType Replicate(const IntType& val, uint32_t numBits, uint32_t toBit) { + if (numBits == 0) + return 0; + if (toBit == 0) + return 0; + IntType v = val & ((1 << numBits) - 1); + IntType res = v; + uint32_t reslen = numBits; + while (reslen < toBit) { + uint32_t comp = 0; + if (numBits > toBit - reslen) { + uint32_t newshift = toBit - reslen; + comp = numBits - newshift; + numBits = newshift; + } + res <<= numBits; + res |= v >> comp; + reslen += numBits; + } + return res; +} + +class Pixel { +protected: + typedef int16_t ChannelType; + uint8_t m_BitDepth[4]; + int16_t color[4]; + +public: + Pixel() { + for (int i = 0; i < 4; i++) { + m_BitDepth[i] = 8; + color[i] = 0; + } + } + + Pixel(ChannelType a, ChannelType r, ChannelType g, ChannelType b, unsigned bitDepth = 8) { + for (int i = 0; i < 4; i++) + m_BitDepth[i] = bitDepth; + + color[0] = a; + color[1] = r; + color[2] = g; + color[3] = b; + } + + // Changes the depth of each pixel. This scales the values to + // the appropriate bit depth by either truncating the least + // significant bits when going from larger to smaller bit depth + // or by repeating the most significant bits when going from + // smaller to larger bit depths. + void ChangeBitDepth(const uint8_t (&depth)[4]) { + for (uint32_t i = 0; i < 4; i++) { + Component(i) = ChangeBitDepth(Component(i), m_BitDepth[i], depth[i]); + m_BitDepth[i] = depth[i]; + } + } + + template <typename IntType> + static float ConvertChannelToFloat(IntType channel, uint8_t bitDepth) { + float denominator = static_cast<float>((1 << bitDepth) - 1); + return static_cast<float>(channel) / denominator; + } + + // Changes the bit depth of a single component. See the comment + // above for how we do this. + static ChannelType ChangeBitDepth(Pixel::ChannelType val, uint8_t oldDepth, uint8_t newDepth) { + assert(newDepth <= 8); + assert(oldDepth <= 8); + + if (oldDepth == newDepth) { + // Do nothing + return val; + } else if (oldDepth == 0 && newDepth != 0) { + return (1 << newDepth) - 1; + } else if (newDepth > oldDepth) { + return Replicate(val, oldDepth, newDepth); + } else { + // oldDepth > newDepth + if (newDepth == 0) { + return 0xFF; + } else { + uint8_t bitsWasted = oldDepth - newDepth; + uint16_t v = static_cast<uint16_t>(val); + v = (v + (1 << (bitsWasted - 1))) >> bitsWasted; + v = ::std::min<uint16_t>(::std::max<uint16_t>(0, v), (1 << newDepth) - 1); + return static_cast<uint8_t>(v); + } + } + + assert(!"We shouldn't get here."); + return 0; + } + + const ChannelType& A() const { + return color[0]; + } + ChannelType& A() { + return color[0]; + } + const ChannelType& R() const { + return color[1]; + } + ChannelType& R() { + return color[1]; + } + const ChannelType& G() const { + return color[2]; + } + ChannelType& G() { + return color[2]; + } + const ChannelType& B() const { + return color[3]; + } + ChannelType& B() { + return color[3]; + } + const ChannelType& Component(uint32_t idx) const { + return color[idx]; + } + ChannelType& Component(uint32_t idx) { + return color[idx]; + } + + void GetBitDepth(uint8_t (&outDepth)[4]) const { + for (int i = 0; i < 4; i++) { + outDepth[i] = m_BitDepth[i]; + } + } + + // Take all of the components, transform them to their 8-bit variants, + // and then pack each channel into an R8G8B8A8 32-bit integer. We assume + // that the architecture is little-endian, so the alpha channel will end + // up in the most-significant byte. + uint32_t Pack() const { + Pixel eightBit(*this); + const uint8_t eightBitDepth[4] = {8, 8, 8, 8}; + eightBit.ChangeBitDepth(eightBitDepth); + + uint32_t r = 0; + r |= eightBit.A(); + r <<= 8; + r |= eightBit.B(); + r <<= 8; + r |= eightBit.G(); + r <<= 8; + r |= eightBit.R(); + return r; + } + + // Clamps the pixel to the range [0,255] + void ClampByte() { + for (uint32_t i = 0; i < 4; i++) { + color[i] = (color[i] < 0) ? 0 : ((color[i] > 255) ? 255 : color[i]); + } + } + + void MakeOpaque() { + A() = 255; + } +}; + +void DecodeColorValues(uint32_t* out, uint8_t* data, uint32_t* modes, const uint32_t nPartitions, + const uint32_t nBitsForColorData) { + // First figure out how many color values we have + uint32_t nValues = 0; + for (uint32_t i = 0; i < nPartitions; i++) { + nValues += ((modes[i] >> 2) + 1) << 1; + } + + // Then based on the number of values and the remaining number of bits, + // figure out the max value for each of them... + uint32_t range = 256; + while (--range > 0) { + IntegerEncodedValue val = IntegerEncodedValue::CreateEncoding(range); + uint32_t bitLength = val.GetBitLength(nValues); + if (bitLength <= nBitsForColorData) { + // Find the smallest possible range that matches the given encoding + while (--range > 0) { + IntegerEncodedValue newval = IntegerEncodedValue::CreateEncoding(range); + if (!newval.MatchesEncoding(val)) { + break; + } + } + + // Return to last matching range. + range++; + break; + } + } + + // We now have enough to decode our integer sequence. + std::vector<IntegerEncodedValue> decodedColorValues; + BitStream colorStream(data); + IntegerEncodedValue::DecodeIntegerSequence(decodedColorValues, colorStream, range, nValues); + + // Once we have the decoded values, we need to dequantize them to the 0-255 range + // This procedure is outlined in ASTC spec C.2.13 + uint32_t outIdx = 0; + std::vector<IntegerEncodedValue>::const_iterator itr; + for (itr = decodedColorValues.begin(); itr != decodedColorValues.end(); itr++) { + // Have we already decoded all that we need? + if (outIdx >= nValues) { + break; + } + + const IntegerEncodedValue& val = *itr; + uint32_t bitlen = val.BaseBitLength(); + uint32_t bitval = val.GetBitValue(); + + assert(bitlen >= 1); + + uint32_t A = 0, B = 0, C = 0, D = 0; + // A is just the lsb replicated 9 times. + A = Replicate(bitval & 1, 1, 9); + + switch (val.GetEncoding()) { + // Replicate bits + case eIntegerEncoding_JustBits: + out[outIdx++] = Replicate(bitval, bitlen, 8); + break; + + // Use algorithm in C.2.13 + case eIntegerEncoding_Trit: { + + D = val.GetTritValue(); + + switch (bitlen) { + case 1: { + C = 204; + } break; + + case 2: { + C = 93; + // B = b000b0bb0 + uint32_t b = (bitval >> 1) & 1; + B = (b << 8) | (b << 4) | (b << 2) | (b << 1); + } break; + + case 3: { + C = 44; + // B = cb000cbcb + uint32_t cb = (bitval >> 1) & 3; + B = (cb << 7) | (cb << 2) | cb; + } break; + + case 4: { + C = 22; + // B = dcb000dcb + uint32_t dcb = (bitval >> 1) & 7; + B = (dcb << 6) | dcb; + } break; + + case 5: { + C = 11; + // B = edcb000ed + uint32_t edcb = (bitval >> 1) & 0xF; + B = (edcb << 5) | (edcb >> 2); + } break; + + case 6: { + C = 5; + // B = fedcb000f + uint32_t fedcb = (bitval >> 1) & 0x1F; + B = (fedcb << 4) | (fedcb >> 4); + } break; + + default: + assert(!"Unsupported trit encoding for color values!"); + break; + } // switch(bitlen) + } // case eIntegerEncoding_Trit + break; + + case eIntegerEncoding_Quint: { + + D = val.GetQuintValue(); + + switch (bitlen) { + case 1: { + C = 113; + } break; + + case 2: { + C = 54; + // B = b0000bb00 + uint32_t b = (bitval >> 1) & 1; + B = (b << 8) | (b << 3) | (b << 2); + } break; + + case 3: { + C = 26; + // B = cb0000cbc + uint32_t cb = (bitval >> 1) & 3; + B = (cb << 7) | (cb << 1) | (cb >> 1); + } break; + + case 4: { + C = 13; + // B = dcb0000dc + uint32_t dcb = (bitval >> 1) & 7; + B = (dcb << 6) | (dcb >> 1); + } break; + + case 5: { + C = 6; + // B = edcb0000e + uint32_t edcb = (bitval >> 1) & 0xF; + B = (edcb << 5) | (edcb >> 3); + } break; + + default: + assert(!"Unsupported quint encoding for color values!"); + break; + } // switch(bitlen) + } // case eIntegerEncoding_Quint + break; + } // switch(val.GetEncoding()) + + if (val.GetEncoding() != eIntegerEncoding_JustBits) { + uint32_t T = D * C + B; + T ^= A; + T = (A & 0x80) | (T >> 2); + out[outIdx++] = T; + } + } + + // Make sure that each of our values is in the proper range... + for (uint32_t i = 0; i < nValues; i++) { + assert(out[i] <= 255); + } +} + +uint32_t UnquantizeTexelWeight(const IntegerEncodedValue& val) { + uint32_t bitval = val.GetBitValue(); + uint32_t bitlen = val.BaseBitLength(); + + uint32_t A = Replicate(bitval & 1, 1, 7); + uint32_t B = 0, C = 0, D = 0; + + uint32_t result = 0; + switch (val.GetEncoding()) { + case eIntegerEncoding_JustBits: + result = Replicate(bitval, bitlen, 6); + break; + + case eIntegerEncoding_Trit: { + D = val.GetTritValue(); + assert(D < 3); + + switch (bitlen) { + case 0: { + uint32_t results[3] = {0, 32, 63}; + result = results[D]; + } break; + + case 1: { + C = 50; + } break; + + case 2: { + C = 23; + uint32_t b = (bitval >> 1) & 1; + B = (b << 6) | (b << 2) | b; + } break; + + case 3: { + C = 11; + uint32_t cb = (bitval >> 1) & 3; + B = (cb << 5) | cb; + } break; + + default: + assert(!"Invalid trit encoding for texel weight"); + break; + } + } break; + + case eIntegerEncoding_Quint: { + D = val.GetQuintValue(); + assert(D < 5); + + switch (bitlen) { + case 0: { + uint32_t results[5] = {0, 16, 32, 47, 63}; + result = results[D]; + } break; + + case 1: { + C = 28; + } break; + + case 2: { + C = 13; + uint32_t b = (bitval >> 1) & 1; + B = (b << 6) | (b << 1); + } break; + + default: + assert(!"Invalid quint encoding for texel weight"); + break; + } + } break; + } + + if (val.GetEncoding() != eIntegerEncoding_JustBits && bitlen > 0) { + // Decode the value... + result = D * C + B; + result ^= A; + result = (A & 0x20) | (result >> 2); + } + + assert(result < 64); + + // Change from [0,63] to [0,64] + if (result > 32) { + result += 1; + } + + return result; +} + +void UnquantizeTexelWeights(uint32_t out[2][144], std::vector<IntegerEncodedValue>& weights, + const TexelWeightParams& params, const uint32_t blockWidth, + const uint32_t blockHeight) { + uint32_t weightIdx = 0; + uint32_t unquantized[2][144]; + std::vector<IntegerEncodedValue>::const_iterator itr; + for (itr = weights.begin(); itr != weights.end(); itr++) { + unquantized[0][weightIdx] = UnquantizeTexelWeight(*itr); + + if (params.m_bDualPlane) { + itr++; + unquantized[1][weightIdx] = UnquantizeTexelWeight(*itr); + if (itr == weights.end()) { + break; + } + } + + if (++weightIdx >= (params.m_Width * params.m_Height)) + break; + } + + // Do infill if necessary (Section C.2.18) ... + uint32_t Ds = (1024 + (blockWidth / 2)) / (blockWidth - 1); + uint32_t Dt = (1024 + (blockHeight / 2)) / (blockHeight - 1); + + const uint32_t kPlaneScale = params.m_bDualPlane ? 2U : 1U; + for (uint32_t plane = 0; plane < kPlaneScale; plane++) + for (uint32_t t = 0; t < blockHeight; t++) + for (uint32_t s = 0; s < blockWidth; s++) { + uint32_t cs = Ds * s; + uint32_t ct = Dt * t; + + uint32_t gs = (cs * (params.m_Width - 1) + 32) >> 6; + uint32_t gt = (ct * (params.m_Height - 1) + 32) >> 6; + + uint32_t js = gs >> 4; + uint32_t fs = gs & 0xF; + + uint32_t jt = gt >> 4; + uint32_t ft = gt & 0x0F; + + uint32_t w11 = (fs * ft + 8) >> 4; + uint32_t w10 = ft - w11; + uint32_t w01 = fs - w11; + uint32_t w00 = 16 - fs - ft + w11; + + uint32_t v0 = js + jt * params.m_Width; + +#define FIND_TEXEL(tidx, bidx) \ + uint32_t p##bidx = 0; \ + do { \ + if ((tidx) < (params.m_Width * params.m_Height)) { \ + p##bidx = unquantized[plane][(tidx)]; \ + } \ + } while (0) + + FIND_TEXEL(v0, 00); + FIND_TEXEL(v0 + 1, 01); + FIND_TEXEL(v0 + params.m_Width, 10); + FIND_TEXEL(v0 + params.m_Width + 1, 11); + +#undef FIND_TEXEL + + out[plane][t * blockWidth + s] = + (p00 * w00 + p01 * w01 + p10 * w10 + p11 * w11 + 8) >> 4; + } +} + +// Transfers a bit as described in C.2.14 +static inline void BitTransferSigned(int32_t& a, int32_t& b) { + b >>= 1; + b |= a & 0x80; + a >>= 1; + a &= 0x3F; + if (a & 0x20) + a -= 0x40; +} + +// Adds more precision to the blue channel as described +// in C.2.14 +static inline Pixel BlueContract(int32_t a, int32_t r, int32_t g, int32_t b) { + return Pixel(static_cast<int16_t>(a), static_cast<int16_t>((r + b) >> 1), + static_cast<int16_t>((g + b) >> 1), static_cast<int16_t>(b)); +} + +// Partition selection functions as specified in +// C.2.21 +static inline uint32_t hash52(uint32_t p) { + p ^= p >> 15; + p -= p << 17; + p += p << 7; + p += p << 4; + p ^= p >> 5; + p += p << 16; + p ^= p >> 7; + p ^= p >> 3; + p ^= p << 6; + p ^= p >> 17; + return p; +} + +static uint32_t SelectPartition(int32_t seed, int32_t x, int32_t y, int32_t z, + int32_t partitionCount, int32_t smallBlock) { + if (1 == partitionCount) + return 0; + + if (smallBlock) { + x <<= 1; + y <<= 1; + z <<= 1; + } + + seed += (partitionCount - 1) * 1024; + + uint32_t rnum = hash52(static_cast<uint32_t>(seed)); + uint8_t seed1 = static_cast<uint8_t>(rnum & 0xF); + uint8_t seed2 = static_cast<uint8_t>((rnum >> 4) & 0xF); + uint8_t seed3 = static_cast<uint8_t>((rnum >> 8) & 0xF); + uint8_t seed4 = static_cast<uint8_t>((rnum >> 12) & 0xF); + uint8_t seed5 = static_cast<uint8_t>((rnum >> 16) & 0xF); + uint8_t seed6 = static_cast<uint8_t>((rnum >> 20) & 0xF); + uint8_t seed7 = static_cast<uint8_t>((rnum >> 24) & 0xF); + uint8_t seed8 = static_cast<uint8_t>((rnum >> 28) & 0xF); + uint8_t seed9 = static_cast<uint8_t>((rnum >> 18) & 0xF); + uint8_t seed10 = static_cast<uint8_t>((rnum >> 22) & 0xF); + uint8_t seed11 = static_cast<uint8_t>((rnum >> 26) & 0xF); + uint8_t seed12 = static_cast<uint8_t>(((rnum >> 30) | (rnum << 2)) & 0xF); + + seed1 *= seed1; + seed2 *= seed2; + seed3 *= seed3; + seed4 *= seed4; + seed5 *= seed5; + seed6 *= seed6; + seed7 *= seed7; + seed8 *= seed8; + seed9 *= seed9; + seed10 *= seed10; + seed11 *= seed11; + seed12 *= seed12; + + int32_t sh1, sh2, sh3; + if (seed & 1) { + sh1 = (seed & 2) ? 4 : 5; + sh2 = (partitionCount == 3) ? 6 : 5; + } else { + sh1 = (partitionCount == 3) ? 6 : 5; + sh2 = (seed & 2) ? 4 : 5; + } + sh3 = (seed & 0x10) ? sh1 : sh2; + + seed1 >>= sh1; + seed2 >>= sh2; + seed3 >>= sh1; + seed4 >>= sh2; + seed5 >>= sh1; + seed6 >>= sh2; + seed7 >>= sh1; + seed8 >>= sh2; + seed9 >>= sh3; + seed10 >>= sh3; + seed11 >>= sh3; + seed12 >>= sh3; + + int32_t a = seed1 * x + seed2 * y + seed11 * z + (rnum >> 14); + int32_t b = seed3 * x + seed4 * y + seed12 * z + (rnum >> 10); + int32_t c = seed5 * x + seed6 * y + seed9 * z + (rnum >> 6); + int32_t d = seed7 * x + seed8 * y + seed10 * z + (rnum >> 2); + + a &= 0x3F; + b &= 0x3F; + c &= 0x3F; + d &= 0x3F; + + if (partitionCount < 4) + d = 0; + if (partitionCount < 3) + c = 0; + + if (a >= b && a >= c && a >= d) + return 0; + else if (b >= c && b >= d) + return 1; + else if (c >= d) + return 2; + return 3; +} + +static inline uint32_t Select2DPartition(int32_t seed, int32_t x, int32_t y, int32_t partitionCount, + int32_t smallBlock) { + return SelectPartition(seed, x, y, 0, partitionCount, smallBlock); +} + +// Section C.2.14 +void ComputeEndpoints(Pixel& ep1, Pixel& ep2, const uint32_t*& colorValues, + uint32_t colorEndpointMode) { +#define READ_UINT_VALUES(N) \ + uint32_t v[N]; \ + for (uint32_t i = 0; i < N; i++) { \ + v[i] = *(colorValues++); \ + } + +#define READ_INT_VALUES(N) \ + int32_t v[N]; \ + for (uint32_t i = 0; i < N; i++) { \ + v[i] = static_cast<int32_t>(*(colorValues++)); \ + } + + switch (colorEndpointMode) { + case 0: { + READ_UINT_VALUES(2) + ep1 = Pixel(0xFF, v[0], v[0], v[0]); + ep2 = Pixel(0xFF, v[1], v[1], v[1]); + } break; + + case 1: { + READ_UINT_VALUES(2) + uint32_t L0 = (v[0] >> 2) | (v[1] & 0xC0); + uint32_t L1 = std::max(L0 + (v[1] & 0x3F), 0xFFU); + ep1 = Pixel(0xFF, L0, L0, L0); + ep2 = Pixel(0xFF, L1, L1, L1); + } break; + + case 4: { + READ_UINT_VALUES(4) + ep1 = Pixel(v[2], v[0], v[0], v[0]); + ep2 = Pixel(v[3], v[1], v[1], v[1]); + } break; + + case 5: { + READ_INT_VALUES(4) + BitTransferSigned(v[1], v[0]); + BitTransferSigned(v[3], v[2]); + ep1 = Pixel(v[2], v[0], v[0], v[0]); + ep2 = Pixel(v[2] + v[3], v[0] + v[1], v[0] + v[1], v[0] + v[1]); + ep1.ClampByte(); + ep2.ClampByte(); + } break; + + case 6: { + READ_UINT_VALUES(4) + ep1 = Pixel(0xFF, v[0] * v[3] >> 8, v[1] * v[3] >> 8, v[2] * v[3] >> 8); + ep2 = Pixel(0xFF, v[0], v[1], v[2]); + } break; + + case 8: { + READ_UINT_VALUES(6) + if (v[1] + v[3] + v[5] >= v[0] + v[2] + v[4]) { + ep1 = Pixel(0xFF, v[0], v[2], v[4]); + ep2 = Pixel(0xFF, v[1], v[3], v[5]); + } else { + ep1 = BlueContract(0xFF, v[1], v[3], v[5]); + ep2 = BlueContract(0xFF, v[0], v[2], v[4]); + } + } break; + + case 9: { + READ_INT_VALUES(6) + BitTransferSigned(v[1], v[0]); + BitTransferSigned(v[3], v[2]); + BitTransferSigned(v[5], v[4]); + if (v[1] + v[3] + v[5] >= 0) { + ep1 = Pixel(0xFF, v[0], v[2], v[4]); + ep2 = Pixel(0xFF, v[0] + v[1], v[2] + v[3], v[4] + v[5]); + } else { + ep1 = BlueContract(0xFF, v[0] + v[1], v[2] + v[3], v[4] + v[5]); + ep2 = BlueContract(0xFF, v[0], v[2], v[4]); + } + ep1.ClampByte(); + ep2.ClampByte(); + } break; + + case 10: { + READ_UINT_VALUES(6) + ep1 = Pixel(v[4], v[0] * v[3] >> 8, v[1] * v[3] >> 8, v[2] * v[3] >> 8); + ep2 = Pixel(v[5], v[0], v[1], v[2]); + } break; + + case 12: { + READ_UINT_VALUES(8) + if (v[1] + v[3] + v[5] >= v[0] + v[2] + v[4]) { + ep1 = Pixel(v[6], v[0], v[2], v[4]); + ep2 = Pixel(v[7], v[1], v[3], v[5]); + } else { + ep1 = BlueContract(v[7], v[1], v[3], v[5]); + ep2 = BlueContract(v[6], v[0], v[2], v[4]); + } + } break; + + case 13: { + READ_INT_VALUES(8) + BitTransferSigned(v[1], v[0]); + BitTransferSigned(v[3], v[2]); + BitTransferSigned(v[5], v[4]); + BitTransferSigned(v[7], v[6]); + if (v[1] + v[3] + v[5] >= 0) { + ep1 = Pixel(v[6], v[0], v[2], v[4]); + ep2 = Pixel(v[7] + v[6], v[0] + v[1], v[2] + v[3], v[4] + v[5]); + } else { + ep1 = BlueContract(v[6] + v[7], v[0] + v[1], v[2] + v[3], v[4] + v[5]); + ep2 = BlueContract(v[6], v[0], v[2], v[4]); + } + ep1.ClampByte(); + ep2.ClampByte(); + } break; + + default: + assert(!"Unsupported color endpoint mode (is it HDR?)"); + break; + } + +#undef READ_UINT_VALUES +#undef READ_INT_VALUES +} + +void DecompressBlock(uint8_t inBuf[16], const uint32_t blockWidth, const uint32_t blockHeight, + uint32_t* outBuf) { + BitStream strm(inBuf); + TexelWeightParams weightParams = DecodeBlockInfo(strm); + + // Was there an error? + if (weightParams.m_bError) { + assert(!"Invalid block mode"); + FillError(outBuf, blockWidth, blockHeight); + return; + } + + if (weightParams.m_bVoidExtentLDR) { + FillVoidExtentLDR(strm, outBuf, blockWidth, blockHeight); + return; + } + + if (weightParams.m_bVoidExtentHDR) { + assert(!"HDR void extent blocks are unsupported!"); + FillError(outBuf, blockWidth, blockHeight); + return; + } + + if (weightParams.m_Width > blockWidth) { + assert(!"Texel weight grid width should be smaller than block width"); + FillError(outBuf, blockWidth, blockHeight); + return; + } + + if (weightParams.m_Height > blockHeight) { + assert(!"Texel weight grid height should be smaller than block height"); + FillError(outBuf, blockWidth, blockHeight); + return; + } + + // Read num partitions + uint32_t nPartitions = strm.ReadBits(2) + 1; + assert(nPartitions <= 4); + + if (nPartitions == 4 && weightParams.m_bDualPlane) { + assert(!"Dual plane mode is incompatible with four partition blocks"); + FillError(outBuf, blockWidth, blockHeight); + return; + } + + // Based on the number of partitions, read the color endpoint mode for + // each partition. + + // Determine partitions, partition index, and color endpoint modes + int32_t planeIdx = -1; + uint32_t partitionIndex; + uint32_t colorEndpointMode[4] = {0, 0, 0, 0}; + + // Define color data. + uint8_t colorEndpointData[16]; + memset(colorEndpointData, 0, sizeof(colorEndpointData)); + BitStream colorEndpointStream(colorEndpointData, 16 * 8, 0); + + // Read extra config data... + uint32_t baseCEM = 0; + if (nPartitions == 1) { + colorEndpointMode[0] = strm.ReadBits(4); + partitionIndex = 0; + } else { + partitionIndex = strm.ReadBits(10); + baseCEM = strm.ReadBits(6); + } + uint32_t baseMode = (baseCEM & 3); + + // Remaining bits are color endpoint data... + uint32_t nWeightBits = weightParams.GetPackedBitSize(); + int32_t remainingBits = 128 - nWeightBits - strm.GetBitsRead(); + + // Consider extra bits prior to texel data... + uint32_t extraCEMbits = 0; + if (baseMode) { + switch (nPartitions) { + case 2: + extraCEMbits += 2; + break; + case 3: + extraCEMbits += 5; + break; + case 4: + extraCEMbits += 8; + break; + default: + assert(false); + break; + } + } + remainingBits -= extraCEMbits; + + // Do we have a dual plane situation? + uint32_t planeSelectorBits = 0; + if (weightParams.m_bDualPlane) { + planeSelectorBits = 2; + } + remainingBits -= planeSelectorBits; + + // Read color data... + uint32_t colorDataBits = remainingBits; + while (remainingBits > 0) { + uint32_t nb = std::min(remainingBits, 8); + uint32_t b = strm.ReadBits(nb); + colorEndpointStream.WriteBits(b, nb); + remainingBits -= 8; + } + + // Read the plane selection bits + planeIdx = strm.ReadBits(planeSelectorBits); + + // Read the rest of the CEM + if (baseMode) { + uint32_t extraCEM = strm.ReadBits(extraCEMbits); + uint32_t CEM = (extraCEM << 6) | baseCEM; + CEM >>= 2; + + bool C[4] = {0}; + for (uint32_t i = 0; i < nPartitions; i++) { + C[i] = CEM & 1; + CEM >>= 1; + } + + uint8_t M[4] = {0}; + for (uint32_t i = 0; i < nPartitions; i++) { + M[i] = CEM & 3; + CEM >>= 2; + assert(M[i] <= 3); + } + + for (uint32_t i = 0; i < nPartitions; i++) { + colorEndpointMode[i] = baseMode; + if (!(C[i])) + colorEndpointMode[i] -= 1; + colorEndpointMode[i] <<= 2; + colorEndpointMode[i] |= M[i]; + } + } else if (nPartitions > 1) { + uint32_t CEM = baseCEM >> 2; + for (uint32_t i = 0; i < nPartitions; i++) { + colorEndpointMode[i] = CEM; + } + } + + // Make sure everything up till here is sane. + for (uint32_t i = 0; i < nPartitions; i++) { + assert(colorEndpointMode[i] < 16); + } + assert(strm.GetBitsRead() + weightParams.GetPackedBitSize() == 128); + + // Decode both color data and texel weight data + uint32_t colorValues[32]; // Four values, two endpoints, four maximum paritions + DecodeColorValues(colorValues, colorEndpointData, colorEndpointMode, nPartitions, + colorDataBits); + + Pixel endpoints[4][2]; + const uint32_t* colorValuesPtr = colorValues; + for (uint32_t i = 0; i < nPartitions; i++) { + ComputeEndpoints(endpoints[i][0], endpoints[i][1], colorValuesPtr, colorEndpointMode[i]); + } + + // Read the texel weight data.. + uint8_t texelWeightData[16]; + memcpy(texelWeightData, inBuf, sizeof(texelWeightData)); + + // Reverse everything + for (uint32_t i = 0; i < 8; i++) { +// Taken from http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith64Bits +#define REVERSE_BYTE(b) (((b)*0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32 + unsigned char a = static_cast<unsigned char>(REVERSE_BYTE(texelWeightData[i])); + unsigned char b = static_cast<unsigned char>(REVERSE_BYTE(texelWeightData[15 - i])); +#undef REVERSE_BYTE + + texelWeightData[i] = b; + texelWeightData[15 - i] = a; + } + + // Make sure that higher non-texel bits are set to zero + const uint32_t clearByteStart = (weightParams.GetPackedBitSize() >> 3) + 1; + texelWeightData[clearByteStart - 1] &= (1 << (weightParams.GetPackedBitSize() % 8)) - 1; + memset(texelWeightData + clearByteStart, 0, 16 - clearByteStart); + + std::vector<IntegerEncodedValue> texelWeightValues; + BitStream weightStream(texelWeightData); + + IntegerEncodedValue::DecodeIntegerSequence(texelWeightValues, weightStream, + weightParams.m_MaxWeight, + weightParams.GetNumWeightValues()); + + // Blocks can be at most 12x12, so we can have as many as 144 weights + uint32_t weights[2][144]; + UnquantizeTexelWeights(weights, texelWeightValues, weightParams, blockWidth, blockHeight); + + // Now that we have endpoints and weights, we can interpolate and generate + // the proper decoding... + for (uint32_t j = 0; j < blockHeight; j++) + for (uint32_t i = 0; i < blockWidth; i++) { + uint32_t partition = Select2DPartition(partitionIndex, i, j, nPartitions, + (blockHeight * blockWidth) < 32); + assert(partition < nPartitions); + + Pixel p; + for (uint32_t c = 0; c < 4; c++) { + uint32_t C0 = endpoints[partition][0].Component(c); + C0 = Replicate(C0, 8, 16); + uint32_t C1 = endpoints[partition][1].Component(c); + C1 = Replicate(C1, 8, 16); + + uint32_t plane = 0; + if (weightParams.m_bDualPlane && (((planeIdx + 1) & 3) == c)) { + plane = 1; + } + + uint32_t weight = weights[plane][j * blockWidth + i]; + uint32_t C = (C0 * (64 - weight) + C1 * weight + 32) / 64; + if (C == 65535) { + p.Component(c) = 255; + } else { + double Cf = static_cast<double>(C); + p.Component(c) = static_cast<uint16_t>(255.0 * (Cf / 65536.0) + 0.5); + } + } + + outBuf[j * blockWidth + i] = p.Pack(); + } +} + +} // namespace ASTCC + +namespace Tegra::Texture::ASTC { + +std::vector<uint8_t> Decompress(std::vector<uint8_t>& data, uint32_t width, uint32_t height, + uint32_t block_width, uint32_t block_height) { + uint32_t blockIdx = 0; + std::vector<uint8_t> outData; + outData.resize(height * width * 4); + for (uint32_t j = 0; j < height; j += block_height) { + for (uint32_t i = 0; i < width; i += block_width) { + + uint8_t* blockPtr = data.data() + blockIdx * 16; + + // Blocks can be at most 12x12 + uint32_t uncompData[144]; + ASTCC::DecompressBlock(blockPtr, block_width, block_height, uncompData); + + uint32_t decompWidth = std::min(block_width, width - i); + uint32_t decompHeight = std::min(block_height, height - j); + + uint8_t* outRow = outData.data() + (j * width + i) * 4; + for (uint32_t jj = 0; jj < decompHeight; jj++) { + memcpy(outRow + jj * width * 4, uncompData + jj * block_width, decompWidth * 4); + } + + blockIdx++; + } + } + + return outData; +} + +} // namespace Tegra::Texture::ASTC diff --git a/src/video_core/textures/astc.h b/src/video_core/textures/astc.h new file mode 100644 index 0000000000..f0d7c0e564 --- /dev/null +++ b/src/video_core/textures/astc.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <cstdint> +#include <vector> + +namespace Tegra::Texture::ASTC { + +std::vector<uint8_t> Decompress(std::vector<uint8_t>& data, uint32_t width, uint32_t height, + uint32_t block_width, uint32_t block_height); + +} // namespace Tegra::Texture::ASTC diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index 7bf9c4c4b1..b3937b2fe1 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp @@ -5,6 +5,7 @@ #include <cstring> #include "common/assert.h" #include "core/memory.h" +#include "video_core/gpu.h" #include "video_core/textures/decoders.h" #include "video_core/textures/texture.h" @@ -51,8 +52,10 @@ u32 BytesPerPixel(TextureFormat format) { return 8; case TextureFormat::DXT23: case TextureFormat::DXT45: + case TextureFormat::BC7U: // In this case a 'pixel' actually refers to a 4x4 tile. return 16; + case TextureFormat::ASTC_2D_4X4: case TextureFormat::A8R8G8B8: case TextureFormat::A2B10G10R10: case TextureFormat::BF10GF11RF11: @@ -64,6 +67,20 @@ u32 BytesPerPixel(TextureFormat format) { return 1; case TextureFormat::R16_G16_B16_A16: return 8; + case TextureFormat::R32_G32_B32_A32: + return 16; + default: + UNIMPLEMENTED_MSG("Format not implemented"); + break; + } +} + +static u32 DepthBytesPerPixel(DepthFormat format) { + switch (format) { + case DepthFormat::S8_Z24_UNORM: + case DepthFormat::Z24_S8_UNORM: + case DepthFormat::Z32_FLOAT: + return 4; default: UNIMPLEMENTED_MSG("Format not implemented"); break; @@ -82,6 +99,7 @@ std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width, case TextureFormat::DXT23: case TextureFormat::DXT45: case TextureFormat::DXN1: + case TextureFormat::BC7U: // In the DXT and DXN formats, each 4x4 tile is swizzled instead of just individual pixel // values. CopySwizzledData(width / 4, height / 4, bytes_per_pixel, bytes_per_pixel, data, @@ -93,7 +111,31 @@ std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width, case TextureFormat::B5G6R5: case TextureFormat::R8: case TextureFormat::R16_G16_B16_A16: + case TextureFormat::R32_G32_B32_A32: case TextureFormat::BF10GF11RF11: + case TextureFormat::ASTC_2D_4X4: + CopySwizzledData(width, height, bytes_per_pixel, bytes_per_pixel, data, + unswizzled_data.data(), true, block_height); + break; + default: + UNIMPLEMENTED_MSG("Format not implemented"); + break; + } + + return unswizzled_data; +} + +std::vector<u8> UnswizzleDepthTexture(VAddr address, DepthFormat format, u32 width, u32 height, + u32 block_height) { + u8* data = Memory::GetPointer(address); + u32 bytes_per_pixel = DepthBytesPerPixel(format); + + std::vector<u8> unswizzled_data(width * height * bytes_per_pixel); + + switch (format) { + case DepthFormat::S8_Z24_UNORM: + case DepthFormat::Z24_S8_UNORM: + case DepthFormat::Z32_FLOAT: CopySwizzledData(width, height, bytes_per_pixel, bytes_per_pixel, data, unswizzled_data.data(), true, block_height); break; @@ -115,12 +157,15 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat case TextureFormat::DXT23: case TextureFormat::DXT45: case TextureFormat::DXN1: + case TextureFormat::BC7U: + case TextureFormat::ASTC_2D_4X4: case TextureFormat::A8R8G8B8: case TextureFormat::A2B10G10R10: case TextureFormat::A1B5G5R5: case TextureFormat::B5G6R5: case TextureFormat::R8: case TextureFormat::BF10GF11RF11: + case TextureFormat::R32_G32_B32_A32: // TODO(Subv): For the time being just forward the same data without any decoding. rgba_data = texture_data; break; diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h index 2562c4b060..2b088c077e 100644 --- a/src/video_core/textures/decoders.h +++ b/src/video_core/textures/decoders.h @@ -17,6 +17,12 @@ namespace Texture { std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width, u32 height, u32 block_height = TICEntry::DefaultBlockHeight); +/** + * Unswizzles a swizzled depth texture without changing its format. + */ +std::vector<u8> UnswizzleDepthTexture(VAddr address, DepthFormat format, u32 width, u32 height, + u32 block_height = TICEntry::DefaultBlockHeight); + /// Copies texture data from a buffer and performs swizzling/unswizzling as necessary. void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, bool unswizzle, u32 block_height); diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index 89dc8ed1e6..289140f31a 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp @@ -24,9 +24,9 @@ bool Init(EmuWindow* emu_window) { g_renderer = std::make_unique<RendererOpenGL>(); g_renderer->SetWindow(g_emu_window); if (g_renderer->Init()) { - NGLOG_DEBUG(Render, "initialized OK"); + LOG_DEBUG(Render, "initialized OK"); } else { - NGLOG_CRITICAL(Render, "initialization failed !"); + LOG_CRITICAL(Render, "initialization failed !"); return false; } return true; @@ -36,7 +36,7 @@ bool Init(EmuWindow* emu_window) { void Shutdown() { g_renderer.reset(); - NGLOG_DEBUG(Render, "shutdown OK"); + LOG_DEBUG(Render, "shutdown OK"); } } // namespace VideoCore diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 5af3154d71..7de919a8eb 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -30,10 +30,10 @@ add_executable(yuzu debugger/graphics/graphics_breakpoints_p.h debugger/graphics/graphics_surface.cpp debugger/graphics/graphics_surface.h + debugger/console.cpp + debugger/console.h debugger/profiler.cpp debugger/profiler.h - debugger/registers.cpp - debugger/registers.h debugger/wait_tree.cpp debugger/wait_tree.h game_list.cpp @@ -60,7 +60,6 @@ set(UIS configuration/configure_graphics.ui configuration/configure_input.ui configuration/configure_system.ui - debugger/registers.ui hotkeys.ui main.ui ) @@ -84,6 +83,14 @@ if (APPLE) target_sources(yuzu PRIVATE ${MACOSX_ICON}) set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE TRUE) set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist) +elseif(WIN32) + # compile as a win32 gui application instead of a console application + target_link_libraries(yuzu PRIVATE Qt5::WinMain) + if(MSVC) + set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS") + elseif(MINGW) + set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "-mwindows") + endif() endif() create_target_directory_groups(yuzu) diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 5c17cd0d9c..833085559b 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -127,13 +127,14 @@ void GRenderWindow::moveContext() { } void GRenderWindow::SwapBuffers() { -#if !defined(QT_NO_DEBUG) - // Qt debug runtime prints a bogus warning on the console if you haven't called makeCurrent - // since the last time you called swapBuffers. This presumably means something if you're using - // QGLWidget the "regular" way, but in our multi-threaded use case is harmless since we never - // call doneCurrent in this thread. + // In our multi-threaded QGLWidget use case we shouldn't need to call `makeCurrent`, + // since we never call `doneCurrent` in this thread. + // 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. child->makeCurrent(); -#endif + child->swapBuffers(); } diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 8316db7082..a32134fbeb 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -84,6 +84,8 @@ void Config::ReadValues() { qt_config->beginGroup("Renderer"); Settings::values.resolution_factor = qt_config->value("resolution_factor", 1.0).toFloat(); Settings::values.toggle_framelimit = qt_config->value("toggle_framelimit", true).toBool(); + Settings::values.use_accurate_framebuffers = + qt_config->value("use_accurate_framebuffers", false).toBool(); Settings::values.bg_red = qt_config->value("bg_red", 0.0).toFloat(); Settings::values.bg_green = qt_config->value("bg_green", 0.0).toFloat(); @@ -158,6 +160,7 @@ void Config::ReadValues() { UISettings::values.confirm_before_closing = qt_config->value("confirmClose", true).toBool(); UISettings::values.first_start = qt_config->value("firstStart", true).toBool(); UISettings::values.callout_flags = qt_config->value("calloutFlags", 0).toUInt(); + UISettings::values.show_console = qt_config->value("showConsole", false).toBool(); qt_config->endGroup(); } @@ -184,6 +187,7 @@ void Config::SaveValues() { qt_config->beginGroup("Renderer"); qt_config->setValue("resolution_factor", (double)Settings::values.resolution_factor); qt_config->setValue("toggle_framelimit", Settings::values.toggle_framelimit); + qt_config->setValue("use_accurate_framebuffers", Settings::values.use_accurate_framebuffers); // Cast to double because Qt's written float values are not human-readable qt_config->setValue("bg_red", (double)Settings::values.bg_red); @@ -243,7 +247,7 @@ void Config::SaveValues() { qt_config->setValue("confirmClose", UISettings::values.confirm_before_closing); qt_config->setValue("firstStart", UISettings::values.first_start); qt_config->setValue("calloutFlags", UISettings::values.callout_flags); - + qt_config->setValue("showConsole", UISettings::values.show_console); qt_config->endGroup(); } diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index a45edd5109..241db4ae33 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp @@ -2,13 +2,26 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <QDesktopServices> +#include <QUrl> +#include "common/file_util.h" +#include "common/logging/backend.h" +#include "common/logging/filter.h" +#include "common/logging/log.h" +#include "core/core.h" #include "core/settings.h" #include "ui_configure_debug.h" #include "yuzu/configuration/configure_debug.h" +#include "yuzu/debugger/console.h" +#include "yuzu/ui_settings.h" ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureDebug) { ui->setupUi(this); this->setConfiguration(); + connect(ui->open_log_button, &QPushButton::pressed, []() { + QString path = QString::fromStdString(FileUtil::GetUserPath(D_LOGS_IDX)); + QDesktopServices::openUrl(QUrl::fromLocalFile(path)); + }); } ConfigureDebug::~ConfigureDebug() {} @@ -17,10 +30,19 @@ void ConfigureDebug::setConfiguration() { ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub); ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub); ui->gdbport_spinbox->setValue(Settings::values.gdbstub_port); + ui->toggle_console->setEnabled(!Core::System::GetInstance().IsPoweredOn()); + ui->toggle_console->setChecked(UISettings::values.show_console); + ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter)); } void ConfigureDebug::applyConfiguration() { Settings::values.use_gdbstub = ui->toggle_gdbstub->isChecked(); Settings::values.gdbstub_port = ui->gdbport_spinbox->value(); + UISettings::values.show_console = ui->toggle_console->isChecked(); + Settings::values.log_filter = ui->log_filter_edit->text().toStdString(); + Debugger::ToggleConsole(); + Log::Filter filter; + filter.ParseFilterString(Settings::values.log_filter); + Log::SetGlobalFilter(filter); Settings::Apply(); } diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index a10bea2f4f..118e91cf1b 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui @@ -73,6 +73,47 @@ </layout> </item> <item> + <widget class="QGroupBox" name="groupBox_2"> + <property name="title"> + <string>Logging</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Global Log Filter</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="log_filter_edit"/> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QCheckBox" name="toggle_console"> + <property name="text"> + <string>Show Log Console (Windows Only)</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="open_log_button"> + <property name="text"> + <string>Open Log Location</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> <spacer name="verticalSpacer"> <property name="orientation"> <enum>Qt::Vertical</enum> diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 47b9b6e952..7664880d53 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp @@ -59,11 +59,13 @@ void ConfigureGraphics::setConfiguration() { ui->resolution_factor_combobox->setCurrentIndex( static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor))); ui->toggle_framelimit->setChecked(Settings::values.toggle_framelimit); + ui->use_accurate_framebuffers->setChecked(Settings::values.use_accurate_framebuffers); } void ConfigureGraphics::applyConfiguration() { Settings::values.resolution_factor = ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex())); Settings::values.toggle_framelimit = ui->toggle_framelimit->isChecked(); + Settings::values.use_accurate_framebuffers = ui->use_accurate_framebuffers->isChecked(); Settings::Apply(); } diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index 366931a9ac..7d092df035 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui @@ -30,6 +30,13 @@ </widget> </item> <item> + <widget class="QCheckBox" name="use_accurate_framebuffers"> + <property name="text"> + <string>Use accurate framebuffers (slow)</string> + </property> + </widget> + </item> + <item> <layout class="QHBoxLayout" name="horizontalLayout"> <item> <widget class="QLabel" name="label"> diff --git a/src/yuzu/debugger/console.cpp b/src/yuzu/debugger/console.cpp new file mode 100644 index 0000000000..e3d2d975f8 --- /dev/null +++ b/src/yuzu/debugger/console.cpp @@ -0,0 +1,45 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#ifdef _WIN32 +#include <windows.h> + +#include <wincon.h> +#endif + +#include "common/logging/backend.h" +#include "yuzu/debugger/console.h" +#include "yuzu/ui_settings.h" + +namespace Debugger { +void ToggleConsole() { +#if defined(_WIN32) && !defined(_DEBUG) + FILE* temp; + if (UISettings::values.show_console) { + if (AllocConsole()) { + // The first parameter for freopen_s is a out parameter, so we can just ignore it + freopen_s(&temp, "CONIN$", "r", stdin); + freopen_s(&temp, "CONOUT$", "w", stdout); + freopen_s(&temp, "CONOUT$", "w", stderr); + Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>()); + } + } else { + if (FreeConsole()) { + // In order to close the console, we have to also detach the streams on it. + // Just redirect them to NUL if there is no console window + Log::RemoveBackend(Log::ColorConsoleBackend::Name()); + freopen_s(&temp, "NUL", "r", stdin); + freopen_s(&temp, "NUL", "w", stdout); + freopen_s(&temp, "NUL", "w", stderr); + } + } +#else + if (UISettings::values.show_console) { + Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>()); + } else { + Log::RemoveBackend(Log::ColorConsoleBackend::Name()); + } +#endif +} +} // namespace Debugger diff --git a/src/yuzu/debugger/console.h b/src/yuzu/debugger/console.h new file mode 100644 index 0000000000..d1990c496d --- /dev/null +++ b/src/yuzu/debugger/console.h @@ -0,0 +1,14 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Debugger { + +/** + * Uses the WINAPI to hide or show the stderr console. This function is a placeholder until we can + * get a real qt logging window which would work for all platforms. + */ +void ToggleConsole(); +} // namespace Debugger
\ No newline at end of file diff --git a/src/yuzu/debugger/registers.cpp b/src/yuzu/debugger/registers.cpp deleted file mode 100644 index 178cc65a79..0000000000 --- a/src/yuzu/debugger/registers.cpp +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <QTreeWidgetItem> -#include "core/arm/arm_interface.h" -#include "core/core.h" -#include "yuzu/debugger/registers.h" -#include "yuzu/util/util.h" - -RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) { - cpu_regs_ui.setupUi(this); - - tree = cpu_regs_ui.treeWidget; - tree->addTopLevelItem(core_registers = new QTreeWidgetItem(QStringList(tr("Registers")))); - tree->addTopLevelItem(vfp_registers = new QTreeWidgetItem(QStringList(tr("VFP Registers")))); - tree->addTopLevelItem(vfp_system_registers = - new QTreeWidgetItem(QStringList(tr("VFP System Registers")))); - tree->addTopLevelItem(cpsr = new QTreeWidgetItem(QStringList("CPSR"))); - - for (int i = 0; i < 16; ++i) { - QTreeWidgetItem* child = new QTreeWidgetItem(QStringList(QString("R[%1]").arg(i))); - core_registers->addChild(child); - } - - for (int i = 0; i < 32; ++i) { - QTreeWidgetItem* child = new QTreeWidgetItem(QStringList(QString("S[%1]").arg(i))); - vfp_registers->addChild(child); - } - - QFont font = GetMonospaceFont(); - - CreateCPSRChildren(); - CreateVFPSystemRegisterChildren(); - - // Set Registers to display in monospace font - for (int i = 0; i < core_registers->childCount(); ++i) - core_registers->child(i)->setFont(1, font); - - for (int i = 0; i < vfp_registers->childCount(); ++i) - vfp_registers->child(i)->setFont(1, font); - - for (int i = 0; i < vfp_system_registers->childCount(); ++i) { - vfp_system_registers->child(i)->setFont(1, font); - for (int x = 0; x < vfp_system_registers->child(i)->childCount(); ++x) { - vfp_system_registers->child(i)->child(x)->setFont(1, font); - } - } - // Set CSPR to display in monospace font - cpsr->setFont(1, font); - for (int i = 0; i < cpsr->childCount(); ++i) { - cpsr->child(i)->setFont(1, font); - for (int x = 0; x < cpsr->child(i)->childCount(); ++x) { - cpsr->child(i)->child(x)->setFont(1, font); - } - } - setEnabled(false); -} - -void RegistersWidget::OnDebugModeEntered() { - if (!Core::System::GetInstance().IsPoweredOn()) - return; - - for (int i = 0; i < core_registers->childCount(); ++i) - core_registers->child(i)->setText( - 1, QString("0x%1").arg(Core::CurrentArmInterface().GetReg(i), 8, 16, QLatin1Char('0'))); - - UpdateCPSRValues(); -} - -void RegistersWidget::OnDebugModeLeft() {} - -void RegistersWidget::OnEmulationStarting(EmuThread* emu_thread) { - setEnabled(true); -} - -void RegistersWidget::OnEmulationStopping() { - // Reset widget text - for (int i = 0; i < core_registers->childCount(); ++i) - core_registers->child(i)->setText(1, QString("")); - - for (int i = 0; i < vfp_registers->childCount(); ++i) - vfp_registers->child(i)->setText(1, QString("")); - - for (int i = 0; i < cpsr->childCount(); ++i) - cpsr->child(i)->setText(1, QString("")); - - cpsr->setText(1, QString("")); - - // FPSCR - for (int i = 0; i < vfp_system_registers->child(0)->childCount(); ++i) - vfp_system_registers->child(0)->child(i)->setText(1, QString("")); - - // FPEXC - for (int i = 0; i < vfp_system_registers->child(1)->childCount(); ++i) - vfp_system_registers->child(1)->child(i)->setText(1, QString("")); - - vfp_system_registers->child(0)->setText(1, QString("")); - vfp_system_registers->child(1)->setText(1, QString("")); - vfp_system_registers->child(2)->setText(1, QString("")); - vfp_system_registers->child(3)->setText(1, QString("")); - - setEnabled(false); -} - -void RegistersWidget::CreateCPSRChildren() { - cpsr->addChild(new QTreeWidgetItem(QStringList("M"))); - cpsr->addChild(new QTreeWidgetItem(QStringList("T"))); - cpsr->addChild(new QTreeWidgetItem(QStringList("F"))); - cpsr->addChild(new QTreeWidgetItem(QStringList("I"))); - cpsr->addChild(new QTreeWidgetItem(QStringList("A"))); - cpsr->addChild(new QTreeWidgetItem(QStringList("E"))); - cpsr->addChild(new QTreeWidgetItem(QStringList("IT"))); - cpsr->addChild(new QTreeWidgetItem(QStringList("GE"))); - cpsr->addChild(new QTreeWidgetItem(QStringList("DNM"))); - cpsr->addChild(new QTreeWidgetItem(QStringList("J"))); - cpsr->addChild(new QTreeWidgetItem(QStringList("Q"))); - cpsr->addChild(new QTreeWidgetItem(QStringList("V"))); - cpsr->addChild(new QTreeWidgetItem(QStringList("C"))); - cpsr->addChild(new QTreeWidgetItem(QStringList("Z"))); - cpsr->addChild(new QTreeWidgetItem(QStringList("N"))); -} - -void RegistersWidget::UpdateCPSRValues() { - const u32 cpsr_val = Core::CurrentArmInterface().GetCPSR(); - - cpsr->setText(1, QString("0x%1").arg(cpsr_val, 8, 16, QLatin1Char('0'))); - cpsr->child(0)->setText( - 1, QString("b%1").arg(cpsr_val & 0x1F, 5, 2, QLatin1Char('0'))); // M - Mode - cpsr->child(1)->setText(1, QString::number((cpsr_val >> 5) & 1)); // T - State - cpsr->child(2)->setText(1, QString::number((cpsr_val >> 6) & 1)); // F - FIQ disable - cpsr->child(3)->setText(1, QString::number((cpsr_val >> 7) & 1)); // I - IRQ disable - cpsr->child(4)->setText(1, QString::number((cpsr_val >> 8) & 1)); // A - Imprecise abort - cpsr->child(5)->setText(1, QString::number((cpsr_val >> 9) & 1)); // E - Data endianness - cpsr->child(6)->setText(1, - QString::number((cpsr_val >> 10) & 0x3F)); // IT - If-Then state (DNM) - cpsr->child(7)->setText(1, - QString::number((cpsr_val >> 16) & 0xF)); // GE - Greater-than-or-Equal - cpsr->child(8)->setText(1, QString::number((cpsr_val >> 20) & 0xF)); // DNM - Do not modify - cpsr->child(9)->setText(1, QString::number((cpsr_val >> 24) & 1)); // J - Jazelle - cpsr->child(10)->setText(1, QString::number((cpsr_val >> 27) & 1)); // Q - Saturation - cpsr->child(11)->setText(1, QString::number((cpsr_val >> 28) & 1)); // V - Overflow - cpsr->child(12)->setText(1, QString::number((cpsr_val >> 29) & 1)); // C - Carry/Borrow/Extend - cpsr->child(13)->setText(1, QString::number((cpsr_val >> 30) & 1)); // Z - Zero - cpsr->child(14)->setText(1, QString::number((cpsr_val >> 31) & 1)); // N - Negative/Less than -} - -void RegistersWidget::CreateVFPSystemRegisterChildren() { - QTreeWidgetItem* const fpscr = new QTreeWidgetItem(QStringList("FPSCR")); - fpscr->addChild(new QTreeWidgetItem(QStringList("IOC"))); - fpscr->addChild(new QTreeWidgetItem(QStringList("DZC"))); - fpscr->addChild(new QTreeWidgetItem(QStringList("OFC"))); - fpscr->addChild(new QTreeWidgetItem(QStringList("UFC"))); - fpscr->addChild(new QTreeWidgetItem(QStringList("IXC"))); - fpscr->addChild(new QTreeWidgetItem(QStringList("IDC"))); - fpscr->addChild(new QTreeWidgetItem(QStringList("IOE"))); - fpscr->addChild(new QTreeWidgetItem(QStringList("DZE"))); - fpscr->addChild(new QTreeWidgetItem(QStringList("OFE"))); - fpscr->addChild(new QTreeWidgetItem(QStringList("UFE"))); - fpscr->addChild(new QTreeWidgetItem(QStringList("IXE"))); - fpscr->addChild(new QTreeWidgetItem(QStringList("IDE"))); - fpscr->addChild(new QTreeWidgetItem(QStringList(tr("Vector Length")))); - fpscr->addChild(new QTreeWidgetItem(QStringList(tr("Vector Stride")))); - fpscr->addChild(new QTreeWidgetItem(QStringList(tr("Rounding Mode")))); - fpscr->addChild(new QTreeWidgetItem(QStringList("FZ"))); - fpscr->addChild(new QTreeWidgetItem(QStringList("DN"))); - fpscr->addChild(new QTreeWidgetItem(QStringList("V"))); - fpscr->addChild(new QTreeWidgetItem(QStringList("C"))); - fpscr->addChild(new QTreeWidgetItem(QStringList("Z"))); - fpscr->addChild(new QTreeWidgetItem(QStringList("N"))); - - QTreeWidgetItem* const fpexc = new QTreeWidgetItem(QStringList("FPEXC")); - fpexc->addChild(new QTreeWidgetItem(QStringList("IOC"))); - fpexc->addChild(new QTreeWidgetItem(QStringList("OFC"))); - fpexc->addChild(new QTreeWidgetItem(QStringList("UFC"))); - fpexc->addChild(new QTreeWidgetItem(QStringList("INV"))); - fpexc->addChild(new QTreeWidgetItem(QStringList(tr("Vector Iteration Count")))); - fpexc->addChild(new QTreeWidgetItem(QStringList("FP2V"))); - fpexc->addChild(new QTreeWidgetItem(QStringList("EN"))); - fpexc->addChild(new QTreeWidgetItem(QStringList("EX"))); - - vfp_system_registers->addChild(fpscr); - vfp_system_registers->addChild(fpexc); - vfp_system_registers->addChild(new QTreeWidgetItem(QStringList("FPINST"))); - vfp_system_registers->addChild(new QTreeWidgetItem(QStringList("FPINST2"))); -} - -void RegistersWidget::UpdateVFPSystemRegisterValues() { - UNIMPLEMENTED(); -} diff --git a/src/yuzu/debugger/registers.h b/src/yuzu/debugger/registers.h deleted file mode 100644 index 55bda5b590..0000000000 --- a/src/yuzu/debugger/registers.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <QDockWidget> -#include "ui_registers.h" - -class QTreeWidget; -class QTreeWidgetItem; -class EmuThread; - -class RegistersWidget : public QDockWidget { - Q_OBJECT - -public: - explicit RegistersWidget(QWidget* parent = nullptr); - -public slots: - void OnDebugModeEntered(); - void OnDebugModeLeft(); - - void OnEmulationStarting(EmuThread* emu_thread); - void OnEmulationStopping(); - -private: - void CreateCPSRChildren(); - void UpdateCPSRValues(); - - void CreateVFPSystemRegisterChildren(); - void UpdateVFPSystemRegisterValues(); - - Ui::ARMRegisters cpu_regs_ui; - - QTreeWidget* tree; - - QTreeWidgetItem* core_registers; - QTreeWidgetItem* vfp_registers; - QTreeWidgetItem* vfp_system_registers; - QTreeWidgetItem* cpsr; -}; diff --git a/src/yuzu/debugger/registers.ui b/src/yuzu/debugger/registers.ui deleted file mode 100644 index c81ae03f9a..0000000000 --- a/src/yuzu/debugger/registers.ui +++ /dev/null @@ -1,40 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>ARMRegisters</class> - <widget class="QDockWidget" name="ARMRegisters"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>400</width> - <height>300</height> - </rect> - </property> - <property name="windowTitle"> - <string>ARM Registers</string> - </property> - <widget class="QWidget" name="dockWidgetContents"> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <widget class="QTreeWidget" name="treeWidget"> - <property name="alternatingRowColors"> - <bool>true</bool> - </property> - <column> - <property name="text"> - <string>Register</string> - </property> - </column> - <column> - <property name="text"> - <string>Value</string> - </property> - </column> - </widget> - </item> - </layout> - </widget> - </widget> - <resources/> - <connections/> -</ui> diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index 017bef13c5..7101b381e7 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp @@ -213,6 +213,9 @@ QString WaitTreeThread::GetText() const { case THREADSTATUS_WAIT_MUTEX: status = tr("waiting for mutex"); break; + case THREADSTATUS_WAIT_ARB: + status = tr("waiting for address arbiter"); + break; case THREADSTATUS_DORMANT: status = tr("dormant"); break; @@ -240,6 +243,7 @@ QColor WaitTreeThread::GetColor() const { case THREADSTATUS_WAIT_SYNCH_ALL: case THREADSTATUS_WAIT_SYNCH_ANY: case THREADSTATUS_WAIT_MUTEX: + case THREADSTATUS_WAIT_ARB: return QColor(Qt::GlobalColor::red); case THREADSTATUS_DORMANT: return QColor(Qt::GlobalColor::darkCyan); diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index bbd681eaeb..5a708dc73b 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include <QApplication> +#include <QDir> #include <QFileInfo> #include <QHeaderView> #include <QKeyEvent> @@ -264,8 +265,17 @@ void GameList::ValidateEntry(const QModelIndex& item) { if (file_path.isEmpty()) return; std::string std_file_path(file_path.toStdString()); - if (!FileUtil::Exists(std_file_path) || FileUtil::IsDirectory(std_file_path)) + if (!FileUtil::Exists(std_file_path)) return; + if (FileUtil::IsDirectory(std_file_path)) { + QDir dir(std_file_path.c_str()); + QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files); + if (matching_main.size() == 1) { + emit GameChosen(dir.path() + DIR_SEP + matching_main[0]); + } + return; + } + // Users usually want to run a diffrent game after closing one search_field->clear(); emit GameChosen(file_path); @@ -315,8 +325,7 @@ void GameList::PopupContextMenu(const QPoint& menu_location) { void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) { if (!FileUtil::Exists(dir_path.toStdString()) || !FileUtil::IsDirectory(dir_path.toStdString())) { - NGLOG_ERROR(Frontend, "Could not find game list folder at {}", - dir_path.toLocal8Bit().data()); + LOG_ERROR(Frontend, "Could not find game list folder at {}", dir_path.toLocal8Bit().data()); search_field->setFilterResult(0, 0); return; } @@ -356,16 +365,29 @@ void GameList::LoadInterfaceLayout() { item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder()); } -const QStringList GameList::supported_file_extensions = {"nso", "nro"}; +const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca"}; static bool HasSupportedFileExtension(const std::string& file_name) { QFileInfo file = QFileInfo(file_name.c_str()); return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive); } +static bool IsExtractedNCAMain(const std::string& file_name) { + return QFileInfo(file_name.c_str()).fileName() == "main"; +} + +static QString FormatGameName(const std::string& physical_name) { + QFileInfo file_info(physical_name.c_str()); + if (IsExtractedNCAMain(physical_name)) { + return file_info.dir().path(); + } else { + return QString::fromStdString(physical_name); + } +} + void GameList::RefreshGameDirectory() { if (!UISettings::values.gamedir.isEmpty() && current_worker != nullptr) { - NGLOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); + LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); search_field->clear(); PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); } @@ -380,7 +402,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign return false; // Breaks the callback loop. bool is_dir = FileUtil::IsDirectory(physical_name); - if (!is_dir && HasSupportedFileExtension(physical_name)) { + if (!is_dir && + (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(physical_name); if (!loader) return true; @@ -392,7 +415,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign loader->ReadProgramId(program_id); emit EntryReady({ - new GameListItemPath(QString::fromStdString(physical_name), smdh, program_id), + new GameListItemPath(FormatGameName(physical_name), smdh, program_id), new GameListItem( QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), new GameListItemSize(FileUtil::GetSize(physical_name)), diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 3038bd6da4..05a8ae6d21 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -13,6 +13,7 @@ #include <QMessageBox> #include <QtGui> #include <QtWidgets> +#include "common/common_paths.h" #include "common/logging/backend.h" #include "common/logging/filter.h" #include "common/logging/log.h" @@ -30,10 +31,10 @@ #include "yuzu/bootmanager.h" #include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_dialog.h" +#include "yuzu/debugger/console.h" #include "yuzu/debugger/graphics/graphics_breakpoints.h" #include "yuzu/debugger/graphics/graphics_surface.h" #include "yuzu/debugger/profiler.h" -#include "yuzu/debugger/registers.h" #include "yuzu/debugger/wait_tree.h" #include "yuzu/game_list.h" #include "yuzu/hotkeys.h" @@ -169,15 +170,6 @@ void GMainWindow::InitializeDebugWidgets() { debug_menu->addAction(microProfileDialog->toggleViewAction()); #endif - registersWidget = new RegistersWidget(this); - addDockWidget(Qt::RightDockWidgetArea, registersWidget); - registersWidget->hide(); - debug_menu->addAction(registersWidget->toggleViewAction()); - connect(this, &GMainWindow::EmulationStarting, registersWidget, - &RegistersWidget::OnEmulationStarting); - connect(this, &GMainWindow::EmulationStopping, registersWidget, - &RegistersWidget::OnEmulationStopping); - graphicsBreakpointsWidget = new GraphicsBreakPointsWidget(debug_context, this); addDockWidget(Qt::RightDockWidgetArea, graphicsBreakpointsWidget); graphicsBreakpointsWidget->hide(); @@ -270,6 +262,7 @@ void GMainWindow::RestoreUIState() { ui.action_Show_Status_Bar->setChecked(UISettings::values.show_status_bar); statusBar()->setVisible(ui.action_Show_Status_Bar->isChecked()); + Debugger::ToggleConsole(); } void GMainWindow::ConnectWidgetEvents() { @@ -288,6 +281,7 @@ void GMainWindow::ConnectWidgetEvents() { void GMainWindow::ConnectMenuEvents() { // File connect(ui.action_Load_File, &QAction::triggered, this, &GMainWindow::OnMenuLoadFile); + connect(ui.action_Load_Folder, &QAction::triggered, this, &GMainWindow::OnMenuLoadFolder); connect(ui.action_Select_Game_List_Root, &QAction::triggered, this, &GMainWindow::OnMenuSelectGameListRoot); connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close); @@ -342,13 +336,11 @@ bool GMainWindow::SupportsRequiredGLExtensions() { unsupported_ext.append("ARB_program_interface_query"); if (!GLAD_GL_ARB_separate_shader_objects) unsupported_ext.append("ARB_separate_shader_objects"); - if (!GLAD_GL_ARB_shader_storage_buffer_object) - unsupported_ext.append("ARB_shader_storage_buffer_object"); if (!GLAD_GL_ARB_vertex_attrib_binding) unsupported_ext.append("ARB_vertex_attrib_binding"); for (const QString& ext : unsupported_ext) - NGLOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString()); + LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString()); return unsupported_ext.empty(); } @@ -385,17 +377,17 @@ bool GMainWindow::LoadROM(const QString& filename) { if (result != Core::System::ResultStatus::Success) { switch (result) { case Core::System::ResultStatus::ErrorGetLoader: - NGLOG_CRITICAL(Frontend, "Failed to obtain loader for {}!", filename.toStdString()); + LOG_CRITICAL(Frontend, "Failed to obtain loader for {}!", filename.toStdString()); QMessageBox::critical(this, tr("Error while loading ROM!"), tr("The ROM format is not supported.")); break; case Core::System::ResultStatus::ErrorUnsupportedArch: - NGLOG_CRITICAL(Frontend, "Unsupported architecture detected!", filename.toStdString()); + LOG_CRITICAL(Frontend, "Unsupported architecture detected!", filename.toStdString()); QMessageBox::critical(this, tr("Error while loading ROM!"), tr("The ROM uses currently unusable 32-bit architecture")); break; case Core::System::ResultStatus::ErrorSystemMode: - NGLOG_CRITICAL(Frontend, "Failed to load ROM!"); + LOG_CRITICAL(Frontend, "Failed to load ROM!"); QMessageBox::critical(this, tr("Error while loading ROM!"), tr("Could not determine the system mode.")); break; @@ -445,7 +437,7 @@ bool GMainWindow::LoadROM(const QString& filename) { } void GMainWindow::BootGame(const QString& filename) { - NGLOG_INFO(Frontend, "yuzu starting..."); + LOG_INFO(Frontend, "yuzu starting..."); StoreRecentFile(filename); // Put the filename on top of the list if (!LoadROM(filename)) @@ -460,17 +452,12 @@ void GMainWindow::BootGame(const QString& filename) { connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views // before the CPU continues - connect(emu_thread.get(), &EmuThread::DebugModeEntered, registersWidget, - &RegistersWidget::OnDebugModeEntered, Qt::BlockingQueuedConnection); connect(emu_thread.get(), &EmuThread::DebugModeEntered, waitTreeWidget, &WaitTreeWidget::OnDebugModeEntered, Qt::BlockingQueuedConnection); - connect(emu_thread.get(), &EmuThread::DebugModeLeft, registersWidget, - &RegistersWidget::OnDebugModeLeft, Qt::BlockingQueuedConnection); connect(emu_thread.get(), &EmuThread::DebugModeLeft, waitTreeWidget, &WaitTreeWidget::OnDebugModeLeft, Qt::BlockingQueuedConnection); // Update the GUI - registersWidget->OnDebugModeEntered(); if (ui.action_Single_Window_Mode->isChecked()) { game_list->hide(); } @@ -565,6 +552,8 @@ void GMainWindow::OnMenuLoadFile() { for (const auto& piece : game_list->supported_file_extensions) extensions += "*." + piece + " "; + extensions += "main "; + QString file_filter = tr("Switch Executable") + " (" + extensions + ")"; file_filter += ";;" + tr("All Files (*.*)"); @@ -577,6 +566,18 @@ void GMainWindow::OnMenuLoadFile() { } } +void GMainWindow::OnMenuLoadFolder() { + QDir dir = QFileDialog::getExistingDirectory(this, tr("Open Extracted ROM Directory")); + + QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files); + if (matching_main.size() == 1) { + BootGame(dir.path() + DIR_SEP + matching_main[0]); + } else { + QMessageBox::warning(this, tr("Invalid Directory Selected"), + tr("The directory you have selected does not contain a 'main' file.")); + } +} + void GMainWindow::OnMenuSelectGameListRoot() { QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory")); if (!dir_path.isEmpty()) { @@ -883,7 +884,7 @@ void GMainWindow::UpdateUITheme() { QString theme_uri(":" + UISettings::values.theme + "/style.qss"); QFile f(theme_uri); if (!f.exists()) { - NGLOG_ERROR(Frontend, "Unable to set style, stylesheet file not found"); + LOG_ERROR(Frontend, "Unable to set style, stylesheet file not found"); } else { f.open(QFile::ReadOnly | QFile::Text); QTextStream ts(&f); @@ -907,8 +908,7 @@ void GMainWindow::UpdateUITheme() { #endif int main(int argc, char* argv[]) { - Log::Filter log_filter(Log::Level::Info); - Log::SetFilter(&log_filter); + Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>()); MicroProfileOnThreadCreate("Frontend"); SCOPE_EXIT({ MicroProfileShutdown(); }); @@ -926,7 +926,12 @@ int main(int argc, char* argv[]) { GMainWindow main_window; // After settings have been loaded by GMainWindow, apply the filter + Log::Filter log_filter; log_filter.ParseFilterString(Settings::values.log_filter); + Log::SetGlobalFilter(log_filter); + FileUtil::CreateFullPath(FileUtil::GetUserPath(D_LOGS_IDX)); + Log::AddBackend( + std::make_unique<Log::FileBackend>(FileUtil::GetUserPath(D_LOGS_IDX) + LOG_FILE)); main_window.show(); return app.exec(); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index ac3024d8a1..074bba3f9c 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -19,7 +19,6 @@ class GraphicsSurfaceWidget; class GRenderWindow; class MicroProfileDialog; class ProfilerWidget; -class RegistersWidget; class WaitTreeWidget; namespace Tegra { @@ -124,6 +123,7 @@ private slots: void OnGameListLoadFile(QString game_path); void OnGameListOpenSaveFolder(u64 program_id); void OnMenuLoadFile(); + void OnMenuLoadFolder(); /// Called whenever a user selects the "File->Select Game List Root" menu item void OnMenuSelectGameListRoot(); void OnMenuRecentFile(); @@ -163,7 +163,6 @@ private: // Debugger panes ProfilerWidget* profilerWidget; MicroProfileDialog* microProfileDialog; - RegistersWidget* registersWidget; GraphicsBreakPointsWidget* graphicsBreakpointsWidget; GraphicsSurfaceWidget* graphicsSurfaceWidget; WaitTreeWidget* waitTreeWidget; diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 0fcd93cc25..22c4cad08c 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -58,6 +58,7 @@ </property> </widget> <addaction name="action_Load_File"/> + <addaction name="action_Load_Folder"/> <addaction name="separator"/> <addaction name="action_Select_Game_List_Root"/> <addaction name="menu_recent_files"/> @@ -106,6 +107,11 @@ <string>Load File...</string> </property> </action> + <action name="action_Load_Folder"> + <property name="text"> + <string>Load Folder...</string> + </property> + </action> <action name="action_Load_Symbol_Map"> <property name="text"> <string>Load Symbol Map...</string> diff --git a/src/yuzu/ui_settings.h b/src/yuzu/ui_settings.h index 8e215a002a..2286c25594 100644 --- a/src/yuzu/ui_settings.h +++ b/src/yuzu/ui_settings.h @@ -51,6 +51,9 @@ struct Values { std::vector<Shortcut> shortcuts; uint32_t callout_flags; + + // logging + bool show_console; }; extern Values values; diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index ee6e4d658f..3a311b69ff 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -27,17 +27,17 @@ bool Config::LoadINI(const std::string& default_contents, bool retry) { const char* location = this->sdl2_config_loc.c_str(); if (sdl2_config->ParseError() < 0) { if (retry) { - NGLOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", location); + LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", location); FileUtil::CreateFullPath(location); FileUtil::WriteStringToFile(true, default_contents, location); sdl2_config = std::make_unique<INIReader>(location); // Reopen file return LoadINI(default_contents, false); } - NGLOG_ERROR(Config, "Failed."); + LOG_ERROR(Config, "Failed."); return false; } - NGLOG_INFO(Config, "Successfully loaded {}", location); + LOG_INFO(Config, "Successfully loaded {}", location); return true; } @@ -98,6 +98,8 @@ void Config::ReadValues() { (float)sdl2_config->GetReal("Renderer", "resolution_factor", 1.0); Settings::values.toggle_framelimit = sdl2_config->GetBoolean("Renderer", "toggle_framelimit", true); + Settings::values.use_accurate_framebuffers = + sdl2_config->GetBoolean("Renderer", "use_accurate_framebuffers", false); Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0); Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0); diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index 1c438c3f5b..71d2e040fa 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h @@ -102,6 +102,10 @@ resolution_factor = # 0 (default): Off, 1: On use_vsync = +# Whether to use accurate framebuffers +# 0 (default): Off (fast), 1 : On (slow) +use_accurate_framebuffers = + # The clear color for the renderer. What shows up on the sides of the bottom screen. # Must be in range of 0.0-1.0. Defaults to 1.0 for all. bg_red = @@ -162,7 +166,7 @@ use_virtual_sd = # 1 (default): Yes, 0: No use_docked_mode = -# The system region that Citra will use during emulation +# The system region that yuzu will use during emulation # -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan region_value = diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index cfd8eb7e62..e6f0bbe8f3 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp @@ -62,19 +62,19 @@ void EmuWindow_SDL2::Fullscreen() { return; } - NGLOG_ERROR(Frontend, "Fullscreening failed: {}", SDL_GetError()); + LOG_ERROR(Frontend, "Fullscreening failed: {}", SDL_GetError()); // Try a different fullscreening method - NGLOG_INFO(Frontend, "Attempting to use borderless fullscreen..."); + LOG_INFO(Frontend, "Attempting to use borderless fullscreen..."); if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN_DESKTOP) == 0) { return; } - NGLOG_ERROR(Frontend, "Borderless fullscreening failed: {}", SDL_GetError()); + LOG_ERROR(Frontend, "Borderless fullscreening failed: {}", SDL_GetError()); // Fallback algorithm: Maximise window. // Works on all systems (unless something is seriously wrong), so no fallback for this one. - NGLOG_INFO(Frontend, "Falling back on a maximised window..."); + LOG_INFO(Frontend, "Falling back on a maximised window..."); SDL_MaximizeWindow(render_window); } @@ -91,7 +91,7 @@ bool EmuWindow_SDL2::SupportsRequiredGLExtensions() { unsupported_ext.push_back("ARB_vertex_attrib_binding"); for (const std::string& ext : unsupported_ext) - NGLOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext); + LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext); return unsupported_ext.empty(); } @@ -103,7 +103,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { // Initialize the window if (SDL_Init(SDL_INIT_VIDEO) < 0) { - NGLOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); + LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); exit(1); } @@ -126,7 +126,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); if (render_window == nullptr) { - NGLOG_CRITICAL(Frontend, "Failed to create SDL2 window! Exiting..."); + LOG_CRITICAL(Frontend, "Failed to create SDL2 window! Exiting..."); exit(1); } @@ -137,17 +137,17 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { gl_context = SDL_GL_CreateContext(render_window); if (gl_context == nullptr) { - NGLOG_CRITICAL(Frontend, "Failed to create SDL2 GL context! Exiting..."); + LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context! Exiting..."); exit(1); } if (!gladLoadGLLoader(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) { - NGLOG_CRITICAL(Frontend, "Failed to initialize GL functions! Exiting..."); + LOG_CRITICAL(Frontend, "Failed to initialize GL functions! Exiting..."); exit(1); } if (!SupportsRequiredGLExtensions()) { - NGLOG_CRITICAL(Frontend, "GPU does not support all required OpenGL extensions! Exiting..."); + LOG_CRITICAL(Frontend, "GPU does not support all required OpenGL extensions! Exiting..."); exit(1); } diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 95e568b7bd..8ddd202d8c 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -7,6 +7,7 @@ #include <string> #include <thread> +#include "common/common_paths.h" #include "common/logging/backend.h" #include "common/logging/filter.h" #include "common/logging/log.h" @@ -69,7 +70,7 @@ int main(int argc, char** argv) { auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w); if (argv_w == nullptr) { - NGLOG_CRITICAL(Frontend, "Failed to get command line arguments"); + LOG_CRITICAL(Frontend, "Failed to get command line arguments"); return -1; } #endif @@ -102,7 +103,7 @@ int main(int argc, char** argv) { break; case 'f': fullscreen = true; - NGLOG_INFO(Frontend, "Starting in fullscreen mode..."); + LOG_INFO(Frontend, "Starting in fullscreen mode..."); break; case 'h': PrintHelp(argv[0]); @@ -126,13 +127,18 @@ int main(int argc, char** argv) { #endif Log::Filter log_filter(Log::Level::Debug); - Log::SetFilter(&log_filter); + Log::SetGlobalFilter(log_filter); + + Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>()); + FileUtil::CreateFullPath(FileUtil::GetUserPath(D_LOGS_IDX)); + Log::AddBackend( + std::make_unique<Log::FileBackend>(FileUtil::GetUserPath(D_LOGS_IDX) + LOG_FILE)); MicroProfileOnThreadCreate("EmuThread"); SCOPE_EXIT({ MicroProfileShutdown(); }); if (filepath.empty()) { - NGLOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified"); + LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified"); return -1; } @@ -153,28 +159,28 @@ int main(int argc, char** argv) { switch (load_result) { case Core::System::ResultStatus::ErrorGetLoader: - NGLOG_CRITICAL(Frontend, "Failed to obtain loader for %s!", filepath.c_str()); + LOG_CRITICAL(Frontend, "Failed to obtain loader for %s!", filepath.c_str()); return -1; case Core::System::ResultStatus::ErrorLoader: - NGLOG_CRITICAL(Frontend, "Failed to load ROM!"); + LOG_CRITICAL(Frontend, "Failed to load ROM!"); return -1; case Core::System::ResultStatus::ErrorLoader_ErrorEncrypted: - NGLOG_CRITICAL(Frontend, "The game that you are trying to load must be decrypted before " - "being used with yuzu. \n\n For more information on dumping and " - "decrypting games, please refer to: " - "https://yuzu-emu.org/wiki/dumping-game-cartridges/"); + LOG_CRITICAL(Frontend, "The game that you are trying to load must be decrypted before " + "being used with yuzu. \n\n For more information on dumping and " + "decrypting games, please refer to: " + "https://yuzu-emu.org/wiki/dumping-game-cartridges/"); return -1; case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat: - NGLOG_CRITICAL(Frontend, "Error while loading ROM: The ROM format is not supported."); + LOG_CRITICAL(Frontend, "Error while loading ROM: The ROM format is not supported."); return -1; case Core::System::ResultStatus::ErrorNotInitialized: - NGLOG_CRITICAL(Frontend, "CPUCore not initialized"); + LOG_CRITICAL(Frontend, "CPUCore not initialized"); return -1; case Core::System::ResultStatus::ErrorSystemMode: - NGLOG_CRITICAL(Frontend, "Failed to determine system mode!"); + LOG_CRITICAL(Frontend, "Failed to determine system mode!"); return -1; case Core::System::ResultStatus::ErrorVideoCore: - NGLOG_CRITICAL(Frontend, "VideoCore not initialized"); + LOG_CRITICAL(Frontend, "VideoCore not initialized"); return -1; case Core::System::ResultStatus::Success: break; // Expected case |