diff options
144 files changed, 1483 insertions, 878 deletions
diff --git a/.travis/macos/deps.sh b/.travis/macos/deps.sh index 1a547c060..faeafa216 100755 --- a/.travis/macos/deps.sh +++ b/.travis/macos/deps.sh @@ -1,5 +1,6 @@ #!/bin/sh -ex brew update -brew install dylibbundler p7zip qt5 sdl2 ccache +brew install p7zip qt5 sdl2 ccache brew outdated cmake || brew upgrade cmake +pip3 install macpack diff --git a/.travis/macos/upload.sh b/.travis/macos/upload.sh index 9ba95086b..66e3455ff 100755 --- a/.travis/macos/upload.sh +++ b/.travis/macos/upload.sh @@ -11,92 +11,19 @@ mkdir "$REV_NAME" cp build/bin/yuzu-cmd "$REV_NAME" cp -r build/bin/yuzu.app "$REV_NAME" -# move qt libs into app bundle for deployment -$(brew --prefix)/opt/qt5/bin/macdeployqt "${REV_NAME}/yuzu.app" +# move libs into folder for deployment +macpack "${REV_NAME}/yuzu.app/Contents/MacOS/yuzu" -d "../Frameworks" +# move qt frameworks into app bundle for deployment +$(brew --prefix)/opt/qt5/bin/macdeployqt "${REV_NAME}/yuzu.app" -executable="${REV_NAME}/yuzu.app/Contents/MacOS/yuzu" -# move SDL2 libs into folder for deployment -dylibbundler -b -x "${REV_NAME}/yuzu-cmd" -cd -d "${REV_NAME}/libs" -p "@executable_path/libs/" - -# Make the changes to make the yuzu app standalone (i.e. not dependent on the current brew installation). -# To do this, the absolute references to each and every QT framework must be re-written to point to the local frameworks -# (in the Contents/Frameworks folder). -# The "install_name_tool" is used to do so. - -# Coreutils is a hack to coerce Homebrew to point to the absolute Cellar path (symlink dereferenced). i.e: -# ls -l /usr/local/opt/qt5:: /usr/local/opt/qt5 -> ../Cellar/qt5/5.6.1-1 -# grealpath ../Cellar/qt5/5.6.1-1:: /usr/local/Cellar/qt5/5.6.1-1 -brew install coreutils || brew upgrade coreutils || true - -REV_NAME_ALT=$REV_NAME/ -# grealpath is located in coreutils, there is no "realpath" for OS X :( -QT_BREWS_PATH=$(grealpath "$(brew --prefix qt5)") -BREW_PATH=$(brew --prefix) -QT_VERSION_NUM=5 - -$BREW_PATH/opt/qt5/bin/macdeployqt "${REV_NAME_ALT}yuzu.app" \ - -executable="${REV_NAME_ALT}yuzu.app/Contents/MacOS/yuzu" - -# These are the files that macdeployqt packed into Contents/Frameworks/ - we don't want those, so we replace them. -declare -a macos_libs=("QtCore" "QtWidgets" "QtGui" "QtOpenGL" "QtPrintSupport") - -for macos_lib in "${macos_libs[@]}" -do - SC_FRAMEWORK_PART=$macos_lib.framework/Versions/$QT_VERSION_NUM/$macos_lib - # Replace macdeployqt versions of the Frameworks with our own (from /usr/local/opt/qt5/lib/) - cp "$BREW_PATH/opt/qt5/lib/$SC_FRAMEWORK_PART" "${REV_NAME_ALT}yuzu.app/Contents/Frameworks/$SC_FRAMEWORK_PART" - - # Replace references within the embedded Framework files with "internal" versions. - for macos_lib2 in "${macos_libs[@]}" - do - # Since brew references both the non-symlinked and symlink paths of QT5, it needs to be duplicated. - # /usr/local/Cellar/qt5/5.6.1-1/lib and /usr/local/opt/qt5/lib both resolve to the same files. - # So the two lines below are effectively duplicates when resolved as a path, but as strings, they aren't. - RM_FRAMEWORK_PART=$macos_lib2.framework/Versions/$QT_VERSION_NUM/$macos_lib2 - install_name_tool -change \ - $QT_BREWS_PATH/lib/$RM_FRAMEWORK_PART \ - @executable_path/../Frameworks/$RM_FRAMEWORK_PART \ - "${REV_NAME_ALT}yuzu.app/Contents/Frameworks/$SC_FRAMEWORK_PART" - install_name_tool -change \ - "$BREW_PATH/opt/qt5/lib/$RM_FRAMEWORK_PART" \ - @executable_path/../Frameworks/$RM_FRAMEWORK_PART \ - "${REV_NAME_ALT}yuzu.app/Contents/Frameworks/$SC_FRAMEWORK_PART" - done -done - -# Handles `This application failed to start because it could not find or load the Qt platform plugin "cocoa"` -# Which manifests itself as: -# "Exception Type: EXC_CRASH (SIGABRT) | Exception Codes: 0x0000000000000000, 0x0000000000000000 | Exception Note: EXC_CORPSE_NOTIFY" -# There may be more dylibs needed to be fixed... -declare -a macos_plugins=("Plugins/platforms/libqcocoa.dylib") - -for macos_lib in "${macos_plugins[@]}" -do - install_name_tool -id @executable_path/../$macos_lib "${REV_NAME_ALT}yuzu.app/Contents/$macos_lib" - for macos_lib2 in "${macos_libs[@]}" - do - RM_FRAMEWORK_PART=$macos_lib2.framework/Versions/$QT_VERSION_NUM/$macos_lib2 - install_name_tool -change \ - $QT_BREWS_PATH/lib/$RM_FRAMEWORK_PART \ - @executable_path/../Frameworks/$RM_FRAMEWORK_PART \ - "${REV_NAME_ALT}yuzu.app/Contents/$macos_lib" - install_name_tool -change \ - "$BREW_PATH/opt/qt5/lib/$RM_FRAMEWORK_PART" \ - @executable_path/../Frameworks/$RM_FRAMEWORK_PART \ - "${REV_NAME_ALT}yuzu.app/Contents/$macos_lib" - done -done - -for macos_lib in "${macos_libs[@]}" -do - # Debugging info for Travis-CI - otool -L "${REV_NAME_ALT}yuzu.app/Contents/Frameworks/$macos_lib.framework/Versions/$QT_VERSION_NUM/$macos_lib" -done +# move libs into folder for deployment +macpack "${REV_NAME}/yuzu-cmd" -d "libs" # Make the yuzu.app application launch a debugging terminal. # Store away the actual binary -mv ${REV_NAME_ALT}yuzu.app/Contents/MacOS/yuzu ${REV_NAME_ALT}yuzu.app/Contents/MacOS/yuzu-bin +mv ${REV_NAME}/yuzu.app/Contents/MacOS/yuzu ${REV_NAME}/yuzu.app/Contents/MacOS/yuzu-bin -cat > ${REV_NAME_ALT}yuzu.app/Contents/MacOS/yuzu <<EOL +cat > ${REV_NAME}/yuzu.app/Contents/MacOS/yuzu <<EOL #!/usr/bin/env bash cd "\`dirname "\$0"\`" chmod +x yuzu-bin @@ -105,6 +32,9 @@ EOL # Content that will serve as the launching script for yuzu (within the .app folder) # Make the launching script executable -chmod +x ${REV_NAME_ALT}yuzu.app/Contents/MacOS/yuzu +chmod +x ${REV_NAME}/yuzu.app/Contents/MacOS/yuzu + +# Verify loader instructions +find "$REV_NAME" -exec otool -L {} \; . .travis/common/post-upload.sh diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 850ce8006..5639021d3 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -91,6 +91,8 @@ add_library(common STATIC logging/log.h logging/text_formatter.cpp logging/text_formatter.h + lz4_compression.cpp + lz4_compression.h math_util.h memory_hook.cpp memory_hook.h @@ -136,3 +138,4 @@ endif() create_target_directory_groups(common) target_link_libraries(common PUBLIC Boost::boost fmt microprofile) +target_link_libraries(common PRIVATE lz4_static) diff --git a/src/common/bit_util.h b/src/common/bit_util.h index a4f9ed4aa..d032df413 100644 --- a/src/common/bit_util.h +++ b/src/common/bit_util.h @@ -32,7 +32,7 @@ inline u32 CountLeadingZeroes32(u32 value) { return 32; } -inline u64 CountLeadingZeroes64(u64 value) { +inline u32 CountLeadingZeroes64(u64 value) { unsigned long leading_zero = 0; if (_BitScanReverse64(&leading_zero, value) != 0) { @@ -47,15 +47,15 @@ inline u32 CountLeadingZeroes32(u32 value) { return 32; } - return __builtin_clz(value); + return static_cast<u32>(__builtin_clz(value)); } -inline u64 CountLeadingZeroes64(u64 value) { +inline u32 CountLeadingZeroes64(u64 value) { if (value == 0) { return 64; } - return __builtin_clzll(value); + return static_cast<u32>(__builtin_clzll(value)); } #endif @@ -70,7 +70,7 @@ inline u32 CountTrailingZeroes32(u32 value) { return 32; } -inline u64 CountTrailingZeroes64(u64 value) { +inline u32 CountTrailingZeroes64(u64 value) { unsigned long trailing_zero = 0; if (_BitScanForward64(&trailing_zero, value) != 0) { @@ -85,15 +85,15 @@ inline u32 CountTrailingZeroes32(u32 value) { return 32; } - return __builtin_ctz(value); + return static_cast<u32>(__builtin_ctz(value)); } -inline u64 CountTrailingZeroes64(u64 value) { +inline u32 CountTrailingZeroes64(u64 value) { if (value == 0) { return 64; } - return __builtin_ctzll(value); + return static_cast<u32>(__builtin_ctzll(value)); } #endif diff --git a/src/common/detached_tasks.cpp b/src/common/detached_tasks.cpp index a347d9e02..f268d6021 100644 --- a/src/common/detached_tasks.cpp +++ b/src/common/detached_tasks.cpp @@ -16,22 +16,22 @@ DetachedTasks::DetachedTasks() { } void DetachedTasks::WaitForAllTasks() { - std::unique_lock<std::mutex> lock(mutex); + std::unique_lock lock{mutex}; cv.wait(lock, [this]() { return count == 0; }); } DetachedTasks::~DetachedTasks() { - std::unique_lock<std::mutex> lock(mutex); + std::unique_lock lock{mutex}; ASSERT(count == 0); instance = nullptr; } void DetachedTasks::AddTask(std::function<void()> task) { - std::unique_lock<std::mutex> lock(instance->mutex); + std::unique_lock lock{instance->mutex}; ++instance->count; std::thread([task{std::move(task)}]() { task(); - std::unique_lock<std::mutex> lock(instance->mutex); + std::unique_lock lock{instance->mutex}; --instance->count; std::notify_all_at_thread_exit(instance->cv, std::move(lock)); }) diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 4462ff3fb..a03179520 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -46,12 +46,12 @@ public: } void AddBackend(std::unique_ptr<Backend> backend) { - std::lock_guard<std::mutex> lock(writing_mutex); + std::lock_guard lock{writing_mutex}; backends.push_back(std::move(backend)); } void RemoveBackend(std::string_view backend_name) { - std::lock_guard<std::mutex> lock(writing_mutex); + std::lock_guard lock{writing_mutex}; const auto it = std::remove_if(backends.begin(), backends.end(), [&backend_name](const auto& i) { return backend_name == i->GetName(); }); @@ -80,7 +80,7 @@ private: backend_thread = std::thread([&] { Entry entry; auto write_logs = [&](Entry& e) { - std::lock_guard<std::mutex> lock(writing_mutex); + std::lock_guard lock{writing_mutex}; for (const auto& backend : backends) { backend->Write(e); } diff --git a/src/common/lz4_compression.cpp b/src/common/lz4_compression.cpp new file mode 100644 index 000000000..ade6759bb --- /dev/null +++ b/src/common/lz4_compression.cpp @@ -0,0 +1,76 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <lz4hc.h> + +#include "common/assert.h" +#include "common/lz4_compression.h" + +namespace Common::Compression { + +std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size) { + ASSERT_MSG(source_size <= LZ4_MAX_INPUT_SIZE, "Source size exceeds LZ4 maximum input size"); + + const auto source_size_int = static_cast<int>(source_size); + const int max_compressed_size = LZ4_compressBound(source_size_int); + std::vector<u8> compressed(max_compressed_size); + + const int compressed_size = LZ4_compress_default(reinterpret_cast<const char*>(source), + reinterpret_cast<char*>(compressed.data()), + source_size_int, max_compressed_size); + + if (compressed_size <= 0) { + // Compression failed + return {}; + } + + compressed.resize(compressed_size); + + return compressed; +} + +std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size, + s32 compression_level) { + ASSERT_MSG(source_size <= LZ4_MAX_INPUT_SIZE, "Source size exceeds LZ4 maximum input size"); + + compression_level = std::clamp(compression_level, LZ4HC_CLEVEL_MIN, LZ4HC_CLEVEL_MAX); + + const auto source_size_int = static_cast<int>(source_size); + const int max_compressed_size = LZ4_compressBound(source_size_int); + std::vector<u8> compressed(max_compressed_size); + + const int compressed_size = LZ4_compress_HC( + reinterpret_cast<const char*>(source), reinterpret_cast<char*>(compressed.data()), + source_size_int, max_compressed_size, compression_level); + + if (compressed_size <= 0) { + // Compression failed + return {}; + } + + compressed.resize(compressed_size); + + return compressed; +} + +std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size) { + return CompressDataLZ4HC(source, source_size, LZ4HC_CLEVEL_MAX); +} + +std::vector<u8> DecompressDataLZ4(const std::vector<u8>& compressed, + std::size_t uncompressed_size) { + std::vector<u8> uncompressed(uncompressed_size); + const int size_check = LZ4_decompress_safe(reinterpret_cast<const char*>(compressed.data()), + reinterpret_cast<char*>(uncompressed.data()), + static_cast<int>(compressed.size()), + static_cast<int>(uncompressed.size())); + if (static_cast<int>(uncompressed_size) != size_check) { + // Decompression failed + return {}; + } + return uncompressed; +} + +} // namespace Common::Compression diff --git a/src/common/lz4_compression.h b/src/common/lz4_compression.h new file mode 100644 index 000000000..fe2231a6c --- /dev/null +++ b/src/common/lz4_compression.h @@ -0,0 +1,55 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <vector> + +#include "common/common_types.h" + +namespace Common::Compression { + +/** + * Compresses a source memory region with LZ4 and returns the compressed data in a vector. + * + * @param source the uncompressed source memory region. + * @param source_size the size in bytes of the uncompressed source memory region. + * + * @return the compressed data. + */ +std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size); + +/** + * Utilizes the LZ4 subalgorithm LZ4HC with the specified compression level. Higher compression + * levels result in a smaller compressed size, but require more CPU time for compression. The + * compression level has almost no impact on decompression speed. Data compressed with LZ4HC can + * also be decompressed with the default LZ4 decompression. + * + * @param source the uncompressed source memory region. + * @param source_size the size in bytes of the uncompressed source memory region. + * @param compression_level the used compression level. Should be between 3 and 12. + * + * @return the compressed data. + */ +std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size, s32 compression_level); + +/** + * Utilizes the LZ4 subalgorithm LZ4HC with the highest possible compression level. + * + * @param source the uncompressed source memory region. + * @param source_size the size in bytes of the uncompressed source memory region. + * + * @return the compressed data. + */ +std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size); + +/** + * Decompresses a source memory region with LZ4 and returns the uncompressed data in a vector. + * + * @param compressed the compressed source memory region. + * @param uncompressed_size the size in bytes of the uncompressed data. + * + * @return the decompressed data. + */ +std::vector<u8> DecompressDataLZ4(const std::vector<u8>& compressed, std::size_t uncompressed_size); + +} // namespace Common::Compression
\ No newline at end of file diff --git a/src/common/multi_level_queue.h b/src/common/multi_level_queue.h index 2b61b91e0..9cb448f56 100644 --- a/src/common/multi_level_queue.h +++ b/src/common/multi_level_queue.h @@ -72,7 +72,7 @@ public: u64 prios = mlq.used_priorities; prios &= ~((1ULL << (current_priority + 1)) - 1); if (prios == 0) { - current_priority = mlq.depth(); + current_priority = static_cast<u32>(mlq.depth()); } else { current_priority = CountTrailingZeroes64(prios); it = GetBeginItForPrio(); diff --git a/src/common/thread.cpp b/src/common/thread.cpp index 5144c0d9f..fe7a420cc 100644 --- a/src/common/thread.cpp +++ b/src/common/thread.cpp @@ -27,18 +27,6 @@ namespace Common { #ifdef _MSC_VER -void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) { - SetThreadAffinityMask(thread, mask); -} - -void SetCurrentThreadAffinity(u32 mask) { - SetThreadAffinityMask(GetCurrentThread(), mask); -} - -void SwitchCurrentThread() { - SwitchToThread(); -} - // Sets the debugger-visible name of the current thread. // Uses undocumented (actually, it is now documented) trick. // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsdebug/html/vxtsksettingthreadname.asp @@ -70,31 +58,6 @@ void SetCurrentThreadName(const char* name) { #else // !MSVC_VER, so must be POSIX threads -void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) { -#ifdef __APPLE__ - thread_policy_set(pthread_mach_thread_np(thread), THREAD_AFFINITY_POLICY, (integer_t*)&mask, 1); -#elif (defined __linux__ || defined __FreeBSD__) && !(defined ANDROID) - cpu_set_t cpu_set; - CPU_ZERO(&cpu_set); - - for (int i = 0; i != sizeof(mask) * 8; ++i) - if ((mask >> i) & 1) - CPU_SET(i, &cpu_set); - - pthread_setaffinity_np(thread, sizeof(cpu_set), &cpu_set); -#endif -} - -void SetCurrentThreadAffinity(u32 mask) { - SetThreadAffinity(pthread_self(), mask); -} - -#ifndef _WIN32 -void SwitchCurrentThread() { - usleep(1000 * 1); -} -#endif - // MinGW with the POSIX threading model does not support pthread_setname_np #if !defined(_WIN32) || defined(_MSC_VER) void SetCurrentThreadName(const char* name) { diff --git a/src/common/thread.h b/src/common/thread.h index 2cf74452d..0cfd98be6 100644 --- a/src/common/thread.h +++ b/src/common/thread.h @@ -9,14 +9,13 @@ #include <cstddef> #include <mutex> #include <thread> -#include "common/common_types.h" namespace Common { class Event { public: void Set() { - std::lock_guard<std::mutex> lk(mutex); + std::lock_guard lk{mutex}; if (!is_set) { is_set = true; condvar.notify_one(); @@ -24,14 +23,14 @@ public: } void Wait() { - std::unique_lock<std::mutex> lk(mutex); + std::unique_lock lk{mutex}; condvar.wait(lk, [&] { return is_set; }); is_set = false; } template <class Clock, class Duration> bool WaitUntil(const std::chrono::time_point<Clock, Duration>& time) { - std::unique_lock<std::mutex> lk(mutex); + std::unique_lock lk{mutex}; if (!condvar.wait_until(lk, time, [this] { return is_set; })) return false; is_set = false; @@ -39,7 +38,7 @@ public: } void Reset() { - std::unique_lock<std::mutex> lk(mutex); + std::unique_lock lk{mutex}; // no other action required, since wait loops on the predicate and any lingering signal will // get cleared on the first iteration is_set = false; @@ -57,7 +56,7 @@ public: /// Blocks until all "count" threads have called Sync() void Sync() { - std::unique_lock<std::mutex> lk(mutex); + std::unique_lock lk{mutex}; const std::size_t current_generation = generation; if (++waiting == count) { @@ -78,9 +77,6 @@ private: std::size_t generation = 0; // Incremented once each time the barrier is used }; -void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask); -void SetCurrentThreadAffinity(u32 mask); -void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms void SetCurrentThreadName(const char* name); } // namespace Common diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h index 821e8536a..e714ba5b3 100644 --- a/src/common/threadsafe_queue.h +++ b/src/common/threadsafe_queue.h @@ -78,7 +78,7 @@ public: T PopWait() { if (Empty()) { - std::unique_lock<std::mutex> lock(cv_mutex); + std::unique_lock lock{cv_mutex}; cv.wait(lock, [this]() { return !Empty(); }); } T t; @@ -137,7 +137,7 @@ public: template <typename Arg> void Push(Arg&& t) { - std::lock_guard<std::mutex> lock(write_lock); + std::lock_guard lock{write_lock}; spsc_queue.Push(t); } diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 9e23afe85..c59107102 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -458,7 +458,7 @@ add_library(core STATIC create_target_directory_groups(core) target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) -target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn open_source_archives) +target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt mbedtls opus unicorn open_source_archives) if (ENABLE_WEB_SERVICE) target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE) target_link_libraries(core PRIVATE web_service) diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index 4fdc12f11..f64e4c6a6 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp @@ -26,7 +26,6 @@ using Vector = Dynarmic::A64::Vector; class ARM_Dynarmic_Callbacks : public Dynarmic::A64::UserCallbacks { public: explicit ARM_Dynarmic_Callbacks(ARM_Dynarmic& parent) : parent(parent) {} - ~ARM_Dynarmic_Callbacks() = default; u8 MemoryRead8(u64 vaddr) override { return Memory::Read8(vaddr); diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h index aada1e862..81e0b4ac0 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.h +++ b/src/core/arm/dynarmic/arm_dynarmic.h @@ -29,7 +29,7 @@ class ARM_Dynarmic final : public ARM_Interface { public: ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor, std::size_t core_index); - ~ARM_Dynarmic(); + ~ARM_Dynarmic() override; void MapBackingMemory(VAddr address, std::size_t size, u8* memory, Kernel::VMAPermission perms) override; @@ -76,7 +76,7 @@ private: class DynarmicExclusiveMonitor final : public ExclusiveMonitor { public: explicit DynarmicExclusiveMonitor(std::size_t core_count); - ~DynarmicExclusiveMonitor(); + ~DynarmicExclusiveMonitor() override; void SetExclusive(std::size_t core_index, VAddr addr) override; void ClearExclusive() override; diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index a542a098b..27309280c 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp @@ -192,12 +192,13 @@ void ARM_Unicorn::ExecuteInstructions(int num_instructions) { CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions)); core_timing.AddTicks(num_instructions); if (GDBStub::IsServerEnabled()) { - if (last_bkpt_hit) { + if (last_bkpt_hit && last_bkpt.type == GDBStub::BreakpointType::Execute) { uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address); } + Kernel::Thread* thread = Kernel::GetCurrentThread(); SaveContext(thread->GetContext()); - if (last_bkpt_hit || GDBStub::GetCpuStepFlag()) { + if (last_bkpt_hit || GDBStub::IsMemoryBreak() || GDBStub::GetCpuStepFlag()) { last_bkpt_hit = false; GDBStub::Break(); GDBStub::SendTrap(thread, 5); diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h index dbd6955ea..1e44f0736 100644 --- a/src/core/arm/unicorn/arm_unicorn.h +++ b/src/core/arm/unicorn/arm_unicorn.h @@ -18,7 +18,7 @@ namespace Core { class ARM_Unicorn final : public ARM_Interface { public: explicit ARM_Unicorn(Timing::CoreTiming& core_timing); - ~ARM_Unicorn(); + ~ARM_Unicorn() override; void MapBackingMemory(VAddr address, std::size_t size, u8* memory, Kernel::VMAPermission perms) override; @@ -50,7 +50,7 @@ private: uc_engine* uc{}; Timing::CoreTiming& core_timing; GDBStub::BreakpointAddress last_bkpt{}; - bool last_bkpt_hit; + bool last_bkpt_hit = false; }; } // namespace Core diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp index 1eefed6d0..e75741db0 100644 --- a/src/core/core_cpu.cpp +++ b/src/core/core_cpu.cpp @@ -22,7 +22,7 @@ namespace Core { void CpuBarrier::NotifyEnd() { - std::unique_lock<std::mutex> lock(mutex); + std::unique_lock lock{mutex}; end = true; condition.notify_all(); } @@ -34,7 +34,7 @@ bool CpuBarrier::Rendezvous() { } if (!end) { - std::unique_lock<std::mutex> lock(mutex); + std::unique_lock lock{mutex}; --cores_waiting; if (!cores_waiting) { @@ -131,7 +131,7 @@ void Cpu::Reschedule() { reschedule_pending = false; // Lock the global kernel mutex when we manipulate the HLE state - std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); + std::lock_guard lock{HLE::g_hle_lock}; scheduler->Reschedule(); } diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp index 83c184750..60ea9ad12 100644 --- a/src/core/file_sys/control_metadata.cpp +++ b/src/core/file_sys/control_metadata.cpp @@ -67,7 +67,7 @@ std::string NACP::GetDeveloperName(Language language) const { } u64 NACP::GetTitleId() const { - return raw.title_id; + return raw.save_data_owner_id; } u64 NACP::GetDLCBaseTitleId() const { @@ -80,11 +80,11 @@ std::string NACP::GetVersionString() const { } u64 NACP::GetDefaultNormalSaveSize() const { - return raw.normal_save_data_size; + return raw.user_account_save_data_size; } u64 NACP::GetDefaultJournalSaveSize() const { - return raw.journal_sava_data_size; + return raw.user_account_save_data_journal_size; } std::vector<u8> NACP::GetRawBytes() const { diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h index 7b9cdc910..280710ddf 100644 --- a/src/core/file_sys/control_metadata.h +++ b/src/core/file_sys/control_metadata.h @@ -38,23 +38,35 @@ struct RawNACP { u8 video_capture_mode; bool data_loss_confirmation; INSERT_PADDING_BYTES(1); - u64_le title_id; + u64_le presence_group_id; std::array<u8, 0x20> rating_age; std::array<char, 0x10> version_string; u64_le dlc_base_title_id; - u64_le title_id_2; - u64_le normal_save_data_size; - u64_le journal_sava_data_size; - INSERT_PADDING_BYTES(0x18); - u64_le product_code; + u64_le save_data_owner_id; + u64_le user_account_save_data_size; + u64_le user_account_save_data_journal_size; + u64_le device_save_data_size; + u64_le device_save_data_journal_size; + u64_le bcat_delivery_cache_storage_size; + char application_error_code_category[8]; std::array<u64_le, 0x8> local_communication; u8 logo_type; u8 logo_handling; bool runtime_add_on_content_install; INSERT_PADDING_BYTES(5); - u64_le title_id_update; - std::array<u8, 0x40> bcat_passphrase; - INSERT_PADDING_BYTES(0xEC0); + u64_le seed_for_pseudo_device_id; + std::array<u8, 0x41> bcat_passphrase; + INSERT_PADDING_BYTES(7); + u64_le user_account_save_data_max_size; + u64_le user_account_save_data_max_journal_size; + u64_le device_save_data_max_size; + u64_le device_save_data_max_journal_size; + u64_le temporary_storage_size; + u64_le cache_storage_size; + u64_le cache_storage_journal_size; + u64_le cache_storage_data_and_journal_max_size; + u64_le cache_storage_max_index; + INSERT_PADDING_BYTES(0xE70); }; static_assert(sizeof(RawNACP) == 0x4000, "RawNACP has incorrect size."); diff --git a/src/core/file_sys/fsmitm_romfsbuild.cpp b/src/core/file_sys/fsmitm_romfsbuild.cpp index 47b7526c7..d126ae8dd 100644 --- a/src/core/file_sys/fsmitm_romfsbuild.cpp +++ b/src/core/file_sys/fsmitm_romfsbuild.cpp @@ -23,6 +23,7 @@ */ #include <cstring> +#include <string_view> #include "common/alignment.h" #include "common/assert.h" #include "core/file_sys/fsmitm_romfsbuild.h" @@ -97,7 +98,8 @@ struct RomFSBuildFileContext { VirtualFile source; }; -static u32 romfs_calc_path_hash(u32 parent, std::string path, u32 start, std::size_t path_len) { +static u32 romfs_calc_path_hash(u32 parent, std::string_view path, u32 start, + std::size_t path_len) { u32 hash = parent ^ 123456789; for (u32 i = 0; i < path_len; i++) { hash = (hash >> 5) | (hash << 27); diff --git a/src/core/file_sys/nca_metadata.cpp b/src/core/file_sys/nca_metadata.cpp index 6f34b7836..93d0df6b9 100644 --- a/src/core/file_sys/nca_metadata.cpp +++ b/src/core/file_sys/nca_metadata.cpp @@ -10,14 +10,6 @@ namespace FileSys { -bool operator>=(TitleType lhs, TitleType rhs) { - return static_cast<std::size_t>(lhs) >= static_cast<std::size_t>(rhs); -} - -bool operator<=(TitleType lhs, TitleType rhs) { - return static_cast<std::size_t>(lhs) <= static_cast<std::size_t>(rhs); -} - CNMT::CNMT(VirtualFile file) { if (file->ReadObject(&header) != sizeof(CNMTHeader)) return; diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h index a05d155f4..50bf38471 100644 --- a/src/core/file_sys/nca_metadata.h +++ b/src/core/file_sys/nca_metadata.h @@ -29,9 +29,6 @@ enum class TitleType : u8 { DeltaTitle = 0x83, }; -bool operator>=(TitleType lhs, TitleType rhs); -bool operator<=(TitleType lhs, TitleType rhs); - enum class ContentRecordType : u8 { Meta = 0, Program = 1, diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index d3e00437f..d863253f8 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp @@ -3,7 +3,6 @@ // Refer to the license.txt file included. #include <cstddef> -#include <cstring> #include <vector> #include "common/logging/log.h" @@ -17,28 +16,30 @@ ProgramMetadata::ProgramMetadata() = default; ProgramMetadata::~ProgramMetadata() = default; Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) { - std::size_t total_size = static_cast<std::size_t>(file->GetSize()); - if (total_size < sizeof(Header)) + const std::size_t total_size = file->GetSize(); + if (total_size < sizeof(Header)) { return Loader::ResultStatus::ErrorBadNPDMHeader; + } - // TODO(DarkLordZach): Use ReadObject when Header/AcidHeader becomes trivially copyable. - std::vector<u8> npdm_header_data = file->ReadBytes(sizeof(Header)); - if (sizeof(Header) != npdm_header_data.size()) + if (sizeof(Header) != file->ReadObject(&npdm_header)) { return Loader::ResultStatus::ErrorBadNPDMHeader; - std::memcpy(&npdm_header, npdm_header_data.data(), sizeof(Header)); + } - std::vector<u8> acid_header_data = file->ReadBytes(sizeof(AcidHeader), npdm_header.acid_offset); - if (sizeof(AcidHeader) != acid_header_data.size()) + if (sizeof(AcidHeader) != file->ReadObject(&acid_header, npdm_header.acid_offset)) { return Loader::ResultStatus::ErrorBadACIDHeader; - std::memcpy(&acid_header, acid_header_data.data(), sizeof(AcidHeader)); + } - if (sizeof(AciHeader) != file->ReadObject(&aci_header, npdm_header.aci_offset)) + if (sizeof(AciHeader) != file->ReadObject(&aci_header, npdm_header.aci_offset)) { return Loader::ResultStatus::ErrorBadACIHeader; + } - if (sizeof(FileAccessControl) != file->ReadObject(&acid_file_access, acid_header.fac_offset)) + if (sizeof(FileAccessControl) != file->ReadObject(&acid_file_access, acid_header.fac_offset)) { return Loader::ResultStatus::ErrorBadFileAccessControl; - if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset)) + } + + if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset)) { return Loader::ResultStatus::ErrorBadFileAccessHeader; + } aci_kernel_capabilities.resize(aci_header.kac_size / sizeof(u32)); const u64 read_size = aci_header.kac_size; diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h index 0033ba347..7de5b9cf9 100644 --- a/src/core/file_sys/program_metadata.h +++ b/src/core/file_sys/program_metadata.h @@ -58,7 +58,6 @@ public: void Print() const; private: - // TODO(DarkLordZach): BitField is not trivially copyable. struct Header { std::array<char, 4> magic; std::array<u8, 8> reserved; @@ -85,7 +84,6 @@ private: static_assert(sizeof(Header) == 0x80, "NPDM header structure size is wrong"); - // TODO(DarkLordZach): BitField is not trivially copyable. struct AcidHeader { std::array<u8, 0x100> signature; std::array<u8, 0x100> nca_modulus; diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index 1913dc956..7974b031d 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp @@ -16,8 +16,10 @@ namespace FileSys { constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size"; std::string SaveDataDescriptor::DebugInfo() const { - return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}]", - static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id); + return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}, " + "rank={}, index={}]", + static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id, + static_cast<u8>(rank), index); } SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) { @@ -28,7 +30,7 @@ SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save SaveDataFactory::~SaveDataFactory() = default; -ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescriptor meta) { +ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) { if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) { if (meta.zero_1 != 0) { LOG_WARNING(Service_FS, diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h index 3a1caf292..b73654571 100644 --- a/src/core/file_sys/savedata_factory.h +++ b/src/core/file_sys/savedata_factory.h @@ -32,12 +32,19 @@ enum class SaveDataType : u8 { CacheStorage = 5, }; +enum class SaveDataRank : u8 { + Primary, + Secondary, +}; + struct SaveDataDescriptor { u64_le title_id; u128 user_id; u64_le save_id; SaveDataType type; - INSERT_PADDING_BYTES(7); + SaveDataRank rank; + u16_le index; + INSERT_PADDING_BYTES(4); u64_le zero_1; u64_le zero_2; u64_le zero_3; @@ -57,7 +64,7 @@ public: explicit SaveDataFactory(VirtualDir dir); ~SaveDataFactory(); - ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta); + ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta); VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const; diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index e29afd630..1320bbe77 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp @@ -30,7 +30,7 @@ private: explicit Device(std::weak_ptr<TouchState>&& touch_state) : touch_state(touch_state) {} std::tuple<float, float, bool> GetStatus() const override { if (auto state = touch_state.lock()) { - std::lock_guard<std::mutex> guard(state->mutex); + std::lock_guard guard{state->mutex}; return std::make_tuple(state->touch_x, state->touch_y, state->touch_pressed); } return std::make_tuple(0.0f, 0.0f, false); @@ -81,7 +81,7 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) { if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) return; - std::lock_guard<std::mutex> guard(touch_state->mutex); + std::lock_guard guard{touch_state->mutex}; touch_state->touch_x = static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) / (framebuffer_layout.screen.right - framebuffer_layout.screen.left); touch_state->touch_y = static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) / @@ -91,7 +91,7 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) { } void EmuWindow::TouchReleased() { - std::lock_guard<std::mutex> guard(touch_state->mutex); + std::lock_guard guard{touch_state->mutex}; touch_state->touch_pressed = false; touch_state->touch_x = 0; touch_state->touch_y = 0; diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index dafb32aae..afa812598 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -1030,7 +1030,7 @@ static void Step() { /// Tell the CPU if we hit a memory breakpoint. bool IsMemoryBreak() { - if (IsConnected()) { + if (!IsConnected()) { return false; } diff --git a/src/core/hle/kernel/code_set.h b/src/core/hle/kernel/code_set.h index 834fd23d2..879957dcb 100644 --- a/src/core/hle/kernel/code_set.h +++ b/src/core/hle/kernel/code_set.h @@ -5,7 +5,6 @@ #pragma once #include <cstddef> -#include <memory> #include <vector> #include "common/common_types.h" @@ -78,7 +77,7 @@ struct CodeSet final { } /// The overall data that backs this code set. - std::shared_ptr<std::vector<u8>> memory; + std::vector<u8> memory; /// The segments that comprise this code set. std::array<Segment, 3> segments; diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 3b73be67b..3f14bfa86 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -34,7 +34,7 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_ const auto& system = Core::System::GetInstance(); // Lock the global kernel mutex when we enter the kernel HLE. - std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); + std::lock_guard lock{HLE::g_hle_lock}; SharedPtr<Thread> thread = system.Kernel().RetrieveThreadFromWakeupCallbackHandleTable(proper_handle); @@ -115,7 +115,7 @@ struct KernelCore::Impl { // Creates the default system resource limit void InitializeSystemResourceLimit(KernelCore& kernel) { - system_resource_limit = ResourceLimit::Create(kernel, "System"); + system_resource_limit = ResourceLimit::Create(kernel); // If setting the default system values fails, then something seriously wrong has occurred. ASSERT(system_resource_limit->SetLimitValue(ResourceType::PhysicalMemory, 0x200000000) @@ -191,6 +191,10 @@ const Process* KernelCore::CurrentProcess() const { return impl->current_process; } +const std::vector<SharedPtr<Process>>& KernelCore::GetProcessList() const { + return impl->process_list; +} + void KernelCore::AddNamedPort(std::string name, SharedPtr<ClientPort> port) { impl->named_ports.emplace(std::move(name), std::move(port)); } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 03ea5b659..6b8738599 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -72,6 +72,9 @@ public: /// Retrieves a const pointer to the current process. const Process* CurrentProcess() const; + /// Retrieves the list of processes. + const std::vector<SharedPtr<Process>>& GetProcessList() const; + /// Adds a port to the named port table void AddNamedPort(std::string name, SharedPtr<ClientPort> port); diff --git a/src/core/hle/kernel/object.cpp b/src/core/hle/kernel/object.cpp index 217144efc..10431e94c 100644 --- a/src/core/hle/kernel/object.cpp +++ b/src/core/hle/kernel/object.cpp @@ -24,7 +24,6 @@ bool Object::IsWaitable() const { case HandleType::WritableEvent: case HandleType::SharedMemory: case HandleType::TransferMemory: - case HandleType::AddressArbiter: case HandleType::ResourceLimit: case HandleType::ClientPort: case HandleType::ClientSession: diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h index 3f6baa094..332876c27 100644 --- a/src/core/hle/kernel/object.h +++ b/src/core/hle/kernel/object.h @@ -25,7 +25,6 @@ enum class HandleType : u32 { TransferMemory, Thread, Process, - AddressArbiter, ResourceLimit, ClientPort, ServerPort, diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 0d782e4ba..041267318 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -5,6 +5,7 @@ #include <algorithm> #include <memory> #include <random> +#include "common/alignment.h" #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" @@ -75,6 +76,18 @@ SharedPtr<ResourceLimit> Process::GetResourceLimit() const { return resource_limit; } +u64 Process::GetTotalPhysicalMemoryUsed() const { + return vm_manager.GetCurrentHeapSize() + main_thread_stack_size + code_memory_size; +} + +void Process::RegisterThread(const Thread* thread) { + thread_list.push_back(thread); +} + +void Process::UnregisterThread(const Thread* thread) { + thread_list.remove(thread); +} + ResultCode Process::ClearSignalState() { if (status == ProcessStatus::Exited) { LOG_ERROR(Kernel, "called on a terminated process instance."); @@ -107,14 +120,17 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) { return handle_table.SetSize(capabilities.GetHandleTableSize()); } -void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { +void Process::Run(VAddr entry_point, s32 main_thread_priority, u64 stack_size) { + // The kernel always ensures that the given stack size is page aligned. + main_thread_stack_size = Common::AlignUp(stack_size, Memory::PAGE_SIZE); + // Allocate and map the main thread stack // TODO(bunnei): This is heap area that should be allocated by the kernel and not mapped as part // of the user address space. + const VAddr mapping_address = vm_manager.GetTLSIORegionEndAddress() - main_thread_stack_size; vm_manager - .MapMemoryBlock(vm_manager.GetTLSIORegionEndAddress() - stack_size, - std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size, - MemoryState::Stack) + .MapMemoryBlock(mapping_address, std::make_shared<std::vector<u8>>(main_thread_stack_size), + 0, main_thread_stack_size, MemoryState::Stack) .Unwrap(); vm_manager.LogLayout(); @@ -210,11 +226,13 @@ void Process::FreeTLSSlot(VAddr tls_address) { } void Process::LoadModule(CodeSet module_, VAddr base_addr) { + const auto memory = std::make_shared<std::vector<u8>>(std::move(module_.memory)); + const auto MapSegment = [&](const CodeSet::Segment& segment, VMAPermission permissions, MemoryState memory_state) { const auto vma = vm_manager - .MapMemoryBlock(segment.addr + base_addr, module_.memory, - segment.offset, segment.size, memory_state) + .MapMemoryBlock(segment.addr + base_addr, memory, segment.offset, + segment.size, memory_state) .Unwrap(); vm_manager.Reprotect(vma, permissions); }; @@ -224,6 +242,8 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) { MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeData); MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeData); + code_memory_size += module_.memory.size(); + // Clear instruction cache in CPU JIT system.InvalidateCpuInstructionCaches(); } @@ -237,7 +257,7 @@ void Process::Acquire(Thread* thread) { ASSERT_MSG(!ShouldWait(thread), "Object unavailable!"); } -bool Process::ShouldWait(Thread* thread) const { +bool Process::ShouldWait(const Thread* thread) const { return !is_signaled; } diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index a0217d3d8..f060f2a3b 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -7,6 +7,7 @@ #include <array> #include <bitset> #include <cstddef> +#include <list> #include <string> #include <vector> #include <boost/container/static_vector.hpp> @@ -186,6 +187,22 @@ public: return random_entropy.at(index); } + /// Retrieves the total physical memory used by this process in bytes. + u64 GetTotalPhysicalMemoryUsed() const; + + /// Gets the list of all threads created with this process as their owner. + const std::list<const Thread*>& GetThreadList() const { + return thread_list; + } + + /// Registers a thread as being created under this process, + /// adding it to this process' thread list. + void RegisterThread(const Thread* thread); + + /// Unregisters a thread from this process, removing it + /// from this process' thread list. + void UnregisterThread(const Thread* thread); + /// Clears the signaled state of the process if and only if it's signaled. /// /// @pre The process must not be already terminated. If this is called on a @@ -210,7 +227,7 @@ public: /** * Applies address space changes and launches the process main thread. */ - void Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size); + void Run(VAddr entry_point, s32 main_thread_priority, u64 stack_size); /** * Prepares a process for termination by stopping all of its threads @@ -234,7 +251,7 @@ private: ~Process() override; /// Checks if the specified thread should wait until this process is available. - bool ShouldWait(Thread* thread) const override; + bool ShouldWait(const Thread* thread) const override; /// Acquires/locks this process for the specified thread if it's available. void Acquire(Thread* thread) override; @@ -247,6 +264,12 @@ private: /// Memory manager for this process. Kernel::VMManager vm_manager; + /// Size of the main thread's stack in bytes. + u64 main_thread_stack_size = 0; + + /// Size of the loaded code memory in bytes. + u64 code_memory_size = 0; + /// Current status of the process ProcessStatus status; @@ -299,6 +322,9 @@ private: /// Random values for svcGetInfo RandomEntropy std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy; + /// List of threads that are running with this process as their owner. + std::list<const Thread*> thread_list; + /// System context Core::System& system; diff --git a/src/core/hle/kernel/readable_event.cpp b/src/core/hle/kernel/readable_event.cpp index 0e5083f70..c2b798a4e 100644 --- a/src/core/hle/kernel/readable_event.cpp +++ b/src/core/hle/kernel/readable_event.cpp @@ -14,7 +14,7 @@ namespace Kernel { ReadableEvent::ReadableEvent(KernelCore& kernel) : WaitObject{kernel} {} ReadableEvent::~ReadableEvent() = default; -bool ReadableEvent::ShouldWait(Thread* thread) const { +bool ReadableEvent::ShouldWait(const Thread* thread) const { return !signaled; } diff --git a/src/core/hle/kernel/readable_event.h b/src/core/hle/kernel/readable_event.h index 77a9c362c..2eb9dcbb7 100644 --- a/src/core/hle/kernel/readable_event.h +++ b/src/core/hle/kernel/readable_event.h @@ -36,7 +36,7 @@ public: return HANDLE_TYPE; } - bool ShouldWait(Thread* thread) const override; + bool ShouldWait(const Thread* thread) const override; void Acquire(Thread* thread) override; /// Unconditionally clears the readable event's state. diff --git a/src/core/hle/kernel/resource_limit.cpp b/src/core/hle/kernel/resource_limit.cpp index 2f9695005..173f69915 100644 --- a/src/core/hle/kernel/resource_limit.cpp +++ b/src/core/hle/kernel/resource_limit.cpp @@ -16,11 +16,8 @@ constexpr std::size_t ResourceTypeToIndex(ResourceType type) { ResourceLimit::ResourceLimit(KernelCore& kernel) : Object{kernel} {} ResourceLimit::~ResourceLimit() = default; -SharedPtr<ResourceLimit> ResourceLimit::Create(KernelCore& kernel, std::string name) { - SharedPtr<ResourceLimit> resource_limit(new ResourceLimit(kernel)); - - resource_limit->name = std::move(name); - return resource_limit; +SharedPtr<ResourceLimit> ResourceLimit::Create(KernelCore& kernel) { + return new ResourceLimit(kernel); } s64 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const { diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h index 59dc11c22..70e09858a 100644 --- a/src/core/hle/kernel/resource_limit.h +++ b/src/core/hle/kernel/resource_limit.h @@ -31,16 +31,14 @@ constexpr bool IsValidResourceType(ResourceType type) { class ResourceLimit final : public Object { public: - /** - * Creates a resource limit object. - */ - static SharedPtr<ResourceLimit> Create(KernelCore& kernel, std::string name = "Unknown"); + /// Creates a resource limit object. + static SharedPtr<ResourceLimit> Create(KernelCore& kernel); std::string GetTypeName() const override { return "ResourceLimit"; } std::string GetName() const override { - return name; + return GetTypeName(); } static const HandleType HANDLE_TYPE = HandleType::ResourceLimit; @@ -95,9 +93,6 @@ private: ResourceArray limits{}; /// Current resource limit values. ResourceArray values{}; - - /// Name of resource limit object. - std::string name; }; } // namespace Kernel diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 6d0f13ecf..ac501bf7f 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -29,7 +29,7 @@ Scheduler::~Scheduler() { } bool Scheduler::HaveReadyThreads() const { - std::lock_guard<std::mutex> lock(scheduler_mutex); + std::lock_guard lock{scheduler_mutex}; return !ready_queue.empty(); } @@ -132,7 +132,7 @@ void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) { } void Scheduler::Reschedule() { - std::lock_guard<std::mutex> lock(scheduler_mutex); + std::lock_guard lock{scheduler_mutex}; Thread* cur = GetCurrentThread(); Thread* next = PopNextReadyThread(); @@ -148,35 +148,35 @@ void Scheduler::Reschedule() { SwitchContext(next); } -void Scheduler::AddThread(SharedPtr<Thread> thread, u32 priority) { - std::lock_guard<std::mutex> lock(scheduler_mutex); +void Scheduler::AddThread(SharedPtr<Thread> thread) { + std::lock_guard lock{scheduler_mutex}; thread_list.push_back(std::move(thread)); } void Scheduler::RemoveThread(Thread* thread) { - std::lock_guard<std::mutex> lock(scheduler_mutex); + std::lock_guard lock{scheduler_mutex}; thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread), thread_list.end()); } void Scheduler::ScheduleThread(Thread* thread, u32 priority) { - std::lock_guard<std::mutex> lock(scheduler_mutex); + std::lock_guard lock{scheduler_mutex}; ASSERT(thread->GetStatus() == ThreadStatus::Ready); ready_queue.add(thread, priority); } void Scheduler::UnscheduleThread(Thread* thread, u32 priority) { - std::lock_guard<std::mutex> lock(scheduler_mutex); + std::lock_guard lock{scheduler_mutex}; ASSERT(thread->GetStatus() == ThreadStatus::Ready); ready_queue.remove(thread, priority); } void Scheduler::SetThreadPriority(Thread* thread, u32 priority) { - std::lock_guard<std::mutex> lock(scheduler_mutex); + std::lock_guard lock{scheduler_mutex}; if (thread->GetPriority() == priority) { return; } @@ -187,7 +187,7 @@ void Scheduler::SetThreadPriority(Thread* thread, u32 priority) { } Thread* Scheduler::GetNextSuggestedThread(u32 core, u32 maximum_priority) const { - std::lock_guard<std::mutex> lock(scheduler_mutex); + std::lock_guard lock{scheduler_mutex}; const u32 mask = 1U << core; for (auto* thread : ready_queue) { diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index 44baeb713..b29bf7be8 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h @@ -38,7 +38,7 @@ public: u64 GetLastContextSwitchTicks() const; /// Adds a new thread to the scheduler - void AddThread(SharedPtr<Thread> thread, u32 priority); + void AddThread(SharedPtr<Thread> thread); /// Removes a thread from the scheduler void RemoveThread(Thread* thread); diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp index 0e1515c89..708fdf9e1 100644 --- a/src/core/hle/kernel/server_port.cpp +++ b/src/core/hle/kernel/server_port.cpp @@ -30,7 +30,7 @@ void ServerPort::AppendPendingSession(SharedPtr<ServerSession> pending_session) pending_sessions.push_back(std::move(pending_session)); } -bool ServerPort::ShouldWait(Thread* thread) const { +bool ServerPort::ShouldWait(const Thread* thread) const { // If there are no pending sessions, we wait until a new one is added. return pending_sessions.empty(); } diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h index 9bc667cf2..76293cb8b 100644 --- a/src/core/hle/kernel/server_port.h +++ b/src/core/hle/kernel/server_port.h @@ -75,7 +75,7 @@ public: /// waiting to be accepted by this port. void AppendPendingSession(SharedPtr<ServerSession> pending_session); - bool ShouldWait(Thread* thread) const override; + bool ShouldWait(const Thread* thread) const override; void Acquire(Thread* thread) override; private: diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 4d8a337a7..40cec143e 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -46,7 +46,7 @@ ResultVal<SharedPtr<ServerSession>> ServerSession::Create(KernelCore& kernel, st return MakeResult(std::move(server_session)); } -bool ServerSession::ShouldWait(Thread* thread) const { +bool ServerSession::ShouldWait(const Thread* thread) const { // Closed sessions should never wait, an error will be returned from svcReplyAndReceive. if (parent->client == nullptr) return false; diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index aea4ccfeb..3429a326f 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h @@ -41,6 +41,10 @@ public: return "ServerSession"; } + std::string GetName() const override { + return name; + } + static const HandleType HANDLE_TYPE = HandleType::ServerSession; HandleType GetHandleType() const override { return HANDLE_TYPE; @@ -82,7 +86,7 @@ public: */ ResultCode HandleSyncRequest(SharedPtr<Thread> thread); - bool ShouldWait(Thread* thread) const override; + bool ShouldWait(const Thread* thread) const override; void Acquire(Thread* thread) override; diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index 62861da36..f15c5ee36 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -9,7 +9,6 @@ #include "core/hle/kernel/errors.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/shared_memory.h" -#include "core/memory.h" namespace Kernel { @@ -119,7 +118,15 @@ ResultCode SharedMemory::Map(Process& target_process, VAddr address, MemoryPermi ConvertPermissions(permissions)); } -ResultCode SharedMemory::Unmap(Process& target_process, VAddr address) { +ResultCode SharedMemory::Unmap(Process& target_process, VAddr address, u64 unmap_size) { + if (unmap_size != size) { + LOG_ERROR(Kernel, + "Invalid size passed to Unmap. Size must be equal to the size of the " + "memory managed. Shared memory size=0x{:016X}, Unmap size=0x{:016X}", + size, unmap_size); + return ERR_INVALID_SIZE; + } + // TODO(Subv): Verify what happens if the application tries to unmap an address that is not // mapped to a SharedMemory. return target_process.VMManager().UnmapRange(address, size); diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index dab2a6bea..37e18c443 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h @@ -104,11 +104,17 @@ public: /** * Unmaps a shared memory block from the specified address in system memory + * * @param target_process Process from which to unmap the memory block. - * @param address Address in system memory where the shared memory block is mapped + * @param address Address in system memory where the shared memory block is mapped. + * @param unmap_size The amount of bytes to unmap from this shared memory instance. + * * @return Result code of the unmap operation + * + * @pre The given size to unmap must be the same size as the amount of memory managed by + * the SharedMemory instance itself, otherwise ERR_INVALID_SIZE will be returned. */ - ResultCode Unmap(Process& target_process, VAddr address); + ResultCode Unmap(Process& target_process, VAddr address, u64 unmap_size); /** * Gets a pointer to the shared memory block diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 11796e5e5..2fd07ab34 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -709,7 +709,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) HeapRegionBaseAddr = 4, HeapRegionSize = 5, TotalMemoryUsage = 6, - TotalHeapUsage = 7, + TotalPhysicalMemoryUsed = 7, IsCurrentProcessBeingDebugged = 8, RegisterResourceLimit = 9, IdleTickCount = 10, @@ -745,7 +745,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) case GetInfoType::NewMapRegionBaseAddr: case GetInfoType::NewMapRegionSize: case GetInfoType::TotalMemoryUsage: - case GetInfoType::TotalHeapUsage: + case GetInfoType::TotalPhysicalMemoryUsed: case GetInfoType::IsVirtualAddressMemoryEnabled: case GetInfoType::PersonalMmHeapUsage: case GetInfoType::TitleId: @@ -805,8 +805,8 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) *result = process->VMManager().GetTotalMemoryUsage(); return RESULT_SUCCESS; - case GetInfoType::TotalHeapUsage: - *result = process->VMManager().GetCurrentHeapSize(); + case GetInfoType::TotalPhysicalMemoryUsed: + *result = process->GetTotalPhysicalMemoryUsed(); return RESULT_SUCCESS; case GetInfoType::IsVirtualAddressMemoryEnabled: @@ -1140,7 +1140,7 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 return ERR_INVALID_MEMORY_RANGE; } - return shared_memory->Unmap(*current_process, addr); + return shared_memory->Unmap(*current_process, addr, size); } static ResultCode QueryProcessMemory(VAddr memory_info_address, VAddr page_info_address, @@ -1339,6 +1339,20 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}", mutex_addr, condition_variable_addr, thread_handle, nano_seconds); + if (Memory::IsKernelVirtualAddress(mutex_addr)) { + LOG_ERROR( + Kernel_SVC, + "Given mutex address must not be within the kernel address space. address=0x{:016X}", + mutex_addr); + return ERR_INVALID_ADDRESS_STATE; + } + + if (!Common::IsWordAligned(mutex_addr)) { + LOG_ERROR(Kernel_SVC, "Given mutex address must be word-aligned. address=0x{:016X}", + mutex_addr); + return ERR_INVALID_ADDRESS; + } + auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess(); const auto& handle_table = current_process->GetHandleTable(); SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); @@ -1983,6 +1997,83 @@ static ResultCode SetResourceLimitLimitValue(Handle resource_limit, u32 resource return RESULT_SUCCESS; } +static ResultCode GetProcessList(u32* out_num_processes, VAddr out_process_ids, + u32 out_process_ids_size) { + LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{:016X}, out_process_ids_size={}", + out_process_ids, out_process_ids_size); + + // If the supplied size is negative or greater than INT32_MAX / sizeof(u64), bail. + if ((out_process_ids_size & 0xF0000000) != 0) { + LOG_ERROR(Kernel_SVC, + "Supplied size outside [0, 0x0FFFFFFF] range. out_process_ids_size={}", + out_process_ids_size); + return ERR_OUT_OF_RANGE; + } + + const auto& kernel = Core::System::GetInstance().Kernel(); + const auto& vm_manager = kernel.CurrentProcess()->VMManager(); + const auto total_copy_size = out_process_ids_size * sizeof(u64); + + if (out_process_ids_size > 0 && + !vm_manager.IsWithinAddressSpace(out_process_ids, total_copy_size)) { + LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}", + out_process_ids, out_process_ids + total_copy_size); + return ERR_INVALID_ADDRESS_STATE; + } + + const auto& process_list = kernel.GetProcessList(); + const auto num_processes = process_list.size(); + const auto copy_amount = std::min(std::size_t{out_process_ids_size}, num_processes); + + for (std::size_t i = 0; i < copy_amount; ++i) { + Memory::Write64(out_process_ids, process_list[i]->GetProcessID()); + out_process_ids += sizeof(u64); + } + + *out_num_processes = static_cast<u32>(num_processes); + return RESULT_SUCCESS; +} + +ResultCode GetThreadList(u32* out_num_threads, VAddr out_thread_ids, u32 out_thread_ids_size, + Handle debug_handle) { + // TODO: Handle this case when debug events are supported. + UNIMPLEMENTED_IF(debug_handle != InvalidHandle); + + LOG_DEBUG(Kernel_SVC, "called. out_thread_ids=0x{:016X}, out_thread_ids_size={}", + out_thread_ids, out_thread_ids_size); + + // If the size is negative or larger than INT32_MAX / sizeof(u64) + if ((out_thread_ids_size & 0xF0000000) != 0) { + LOG_ERROR(Kernel_SVC, "Supplied size outside [0, 0x0FFFFFFF] range. size={}", + out_thread_ids_size); + return ERR_OUT_OF_RANGE; + } + + const auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess(); + const auto& vm_manager = current_process->VMManager(); + const auto total_copy_size = out_thread_ids_size * sizeof(u64); + + if (out_thread_ids_size > 0 && + !vm_manager.IsWithinAddressSpace(out_thread_ids, total_copy_size)) { + LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}", + out_thread_ids, out_thread_ids + total_copy_size); + return ERR_INVALID_ADDRESS_STATE; + } + + const auto& thread_list = current_process->GetThreadList(); + const auto num_threads = thread_list.size(); + const auto copy_amount = std::min(std::size_t{out_thread_ids_size}, num_threads); + + auto list_iter = thread_list.cbegin(); + for (std::size_t i = 0; i < copy_amount; ++i, ++list_iter) { + Memory::Write64(out_thread_ids, (*list_iter)->GetThreadID()); + out_thread_ids += sizeof(u64); + } + + *out_num_threads = static_cast<u32>(num_threads); + return RESULT_SUCCESS; +} + namespace { struct FunctionDef { using Func = void(); @@ -2095,8 +2186,8 @@ static const FunctionDef SVC_Table[] = { {0x62, nullptr, "TerminateDebugProcess"}, {0x63, nullptr, "GetDebugEvent"}, {0x64, nullptr, "ContinueDebugEvent"}, - {0x65, nullptr, "GetProcessList"}, - {0x66, nullptr, "GetThreadList"}, + {0x65, SvcWrap<GetProcessList>, "GetProcessList"}, + {0x66, SvcWrap<GetThreadList>, "GetThreadList"}, {0x67, nullptr, "GetDebugThreadContext"}, {0x68, nullptr, "SetDebugThreadContext"}, {0x69, nullptr, "QueryDebugProcessMemory"}, @@ -2138,7 +2229,7 @@ void CallSVC(u32 immediate) { MICROPROFILE_SCOPE(Kernel_SVC); // Lock the global kernel mutex when we enter the kernel HLE. - std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); + std::lock_guard lock{HLE::g_hle_lock}; const FunctionDef* info = GetSVCInfo(immediate); if (info) { diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index 2a2c2c5ea..b3733680f 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -78,6 +78,14 @@ void SvcWrap() { FuncReturn(retval); } +template <ResultCode func(u32*, u64, u32)> +void SvcWrap() { + u32 param_1 = 0; + const u32 retval = func(¶m_1, Param(1), static_cast<u32>(Param(2))).raw; + Core::CurrentArmInterface().SetReg(1, param_1); + FuncReturn(retval); +} + template <ResultCode func(u64*, u32)> void SvcWrap() { u64 param_1 = 0; diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index e5853c46f..1b891f632 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -28,7 +28,7 @@ namespace Kernel { -bool Thread::ShouldWait(Thread* thread) const { +bool Thread::ShouldWait(const Thread* thread) const { return status != ThreadStatus::Dead; } @@ -62,6 +62,8 @@ void Thread::Stop() { } wait_objects.clear(); + owner_process->UnregisterThread(this); + // Mark the TLS slot in the thread's page as free. owner_process->FreeTLSSlot(tls_address); } @@ -199,9 +201,11 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap(); thread->owner_process = &owner_process; thread->scheduler = &system.Scheduler(processor_id); - thread->scheduler->AddThread(thread, priority); + thread->scheduler->AddThread(thread); thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread); + thread->owner_process->RegisterThread(thread.get()); + // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used // to initialize the context ResetThreadContext(thread->context, stack_top, entry_point, arg); @@ -229,16 +233,16 @@ void Thread::SetWaitSynchronizationOutput(s32 output) { context.cpu_registers[1] = output; } -s32 Thread::GetWaitObjectIndex(WaitObject* object) const { +s32 Thread::GetWaitObjectIndex(const WaitObject* object) const { ASSERT_MSG(!wait_objects.empty(), "Thread is not waiting for anything"); - auto match = std::find(wait_objects.rbegin(), wait_objects.rend(), object); + const auto match = std::find(wait_objects.rbegin(), wait_objects.rend(), object); return static_cast<s32>(std::distance(match, wait_objects.rend()) - 1); } VAddr Thread::GetCommandBufferAddress() const { // Offset from the start of TLS at which the IPC command buffer begins. - static constexpr int CommandHeaderOffset = 0x80; - return GetTLSAddress() + CommandHeaderOffset; + constexpr u64 command_header_offset = 0x80; + return GetTLSAddress() + command_header_offset; } void Thread::SetStatus(ThreadStatus new_status) { @@ -352,7 +356,7 @@ void Thread::ChangeScheduler() { if (*new_processor_id != processor_id) { // Remove thread from previous core's scheduler scheduler->RemoveThread(this); - next_scheduler.AddThread(this, current_priority); + next_scheduler.AddThread(this); } processor_id = *new_processor_id; @@ -367,7 +371,7 @@ void Thread::ChangeScheduler() { system.CpuCore(processor_id).PrepareReschedule(); } -bool Thread::AllWaitObjectsReady() { +bool Thread::AllWaitObjectsReady() const { return std::none_of( wait_objects.begin(), wait_objects.end(), [this](const SharedPtr<WaitObject>& object) { return object->ShouldWait(this); }); diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 9c684758c..73e5d1bb4 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -111,7 +111,7 @@ public: return HANDLE_TYPE; } - bool ShouldWait(Thread* thread) const override; + bool ShouldWait(const Thread* thread) const override; void Acquire(Thread* thread) override; /** @@ -205,7 +205,7 @@ public: * object in the list. * @param object Object to query the index of. */ - s32 GetWaitObjectIndex(WaitObject* object) const; + s32 GetWaitObjectIndex(const WaitObject* object) const; /** * Stops a thread, invalidating it from further use @@ -299,7 +299,7 @@ public: } /// Determines whether all the objects this thread is waiting on are ready. - bool AllWaitObjectsReady(); + bool AllWaitObjectsReady() const; const MutexWaitingThreads& GetMutexWaitingThreads() const { return wait_mutex_threads; diff --git a/src/core/hle/kernel/transfer_memory.cpp b/src/core/hle/kernel/transfer_memory.cpp index 23228e1b5..26c4e5e67 100644 --- a/src/core/hle/kernel/transfer_memory.cpp +++ b/src/core/hle/kernel/transfer_memory.cpp @@ -14,8 +14,8 @@ namespace Kernel { TransferMemory::TransferMemory(KernelCore& kernel) : Object{kernel} {} TransferMemory::~TransferMemory() = default; -SharedPtr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr base_address, - size_t size, MemoryPermission permissions) { +SharedPtr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr base_address, u64 size, + MemoryPermission permissions) { SharedPtr<TransferMemory> transfer_memory{new TransferMemory(kernel)}; transfer_memory->base_address = base_address; @@ -26,7 +26,15 @@ SharedPtr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr base_ return transfer_memory; } -ResultCode TransferMemory::MapMemory(VAddr address, size_t size, MemoryPermission permissions) { +const u8* TransferMemory::GetPointer() const { + return backing_block.get()->data(); +} + +u64 TransferMemory::GetSize() const { + return memory_size; +} + +ResultCode TransferMemory::MapMemory(VAddr address, u64 size, MemoryPermission permissions) { if (memory_size != size) { return ERR_INVALID_SIZE; } @@ -39,13 +47,13 @@ ResultCode TransferMemory::MapMemory(VAddr address, size_t size, MemoryPermissio return ERR_INVALID_STATE; } + backing_block = std::make_shared<std::vector<u8>>(size); + const auto map_state = owner_permissions == MemoryPermission::None ? MemoryState::TransferMemoryIsolated : MemoryState::TransferMemory; auto& vm_manager = owner_process->VMManager(); - const auto map_result = vm_manager.MapMemoryBlock( - address, std::make_shared<std::vector<u8>>(size), 0, size, map_state); - + const auto map_result = vm_manager.MapMemoryBlock(address, backing_block, 0, size, map_state); if (map_result.Failed()) { return map_result.Code(); } @@ -54,7 +62,7 @@ ResultCode TransferMemory::MapMemory(VAddr address, size_t size, MemoryPermissio return RESULT_SUCCESS; } -ResultCode TransferMemory::UnmapMemory(VAddr address, size_t size) { +ResultCode TransferMemory::UnmapMemory(VAddr address, u64 size) { if (memory_size != size) { return ERR_INVALID_SIZE; } diff --git a/src/core/hle/kernel/transfer_memory.h b/src/core/hle/kernel/transfer_memory.h index ec294951e..a140b1e2b 100644 --- a/src/core/hle/kernel/transfer_memory.h +++ b/src/core/hle/kernel/transfer_memory.h @@ -4,6 +4,9 @@ #pragma once +#include <memory> +#include <vector> + #include "core/hle/kernel/object.h" union ResultCode; @@ -25,7 +28,7 @@ class TransferMemory final : public Object { public: static constexpr HandleType HANDLE_TYPE = HandleType::TransferMemory; - static SharedPtr<TransferMemory> Create(KernelCore& kernel, VAddr base_address, size_t size, + static SharedPtr<TransferMemory> Create(KernelCore& kernel, VAddr base_address, u64 size, MemoryPermission permissions); TransferMemory(const TransferMemory&) = delete; @@ -46,6 +49,12 @@ public: return HANDLE_TYPE; } + /// Gets a pointer to the backing block of this instance. + const u8* GetPointer() const; + + /// Gets the size of the memory backing this instance in bytes. + u64 GetSize() const; + /// Attempts to map transfer memory with the given range and memory permissions. /// /// @param address The base address to being mapping memory at. @@ -56,7 +65,7 @@ public: /// the same values that were given when creating the transfer memory /// instance. /// - ResultCode MapMemory(VAddr address, size_t size, MemoryPermission permissions); + ResultCode MapMemory(VAddr address, u64 size, MemoryPermission permissions); /// Unmaps the transfer memory with the given range /// @@ -66,17 +75,20 @@ public: /// @pre The given address and size must be the same as the ones used /// to create the transfer memory instance. /// - ResultCode UnmapMemory(VAddr address, size_t size); + ResultCode UnmapMemory(VAddr address, u64 size); private: explicit TransferMemory(KernelCore& kernel); ~TransferMemory() override; + /// Memory block backing this instance. + std::shared_ptr<std::vector<u8>> backing_block; + /// The base address for the memory managed by this instance. VAddr base_address = 0; /// Size of the memory, in bytes, that this instance manages. - size_t memory_size = 0; + u64 memory_size = 0; /// The memory permissions that are applied to this instance. MemoryPermission owner_permissions{}; diff --git a/src/core/hle/kernel/wait_object.h b/src/core/hle/kernel/wait_object.h index 5987fb971..04464a51a 100644 --- a/src/core/hle/kernel/wait_object.h +++ b/src/core/hle/kernel/wait_object.h @@ -24,7 +24,7 @@ public: * @param thread The thread about which we're deciding. * @return True if the current thread should wait due to this object being unavailable */ - virtual bool ShouldWait(Thread* thread) const = 0; + virtual bool ShouldWait(const Thread* thread) const = 0; /// Acquire/lock the object for the specified thread if it is available virtual void Acquire(Thread* thread) = 0; diff --git a/src/core/hle/result.h b/src/core/hle/result.h index ab84f5ddc..8a3701151 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -119,10 +119,6 @@ union ResultCode { BitField<0, 9, ErrorModule> module; BitField<9, 13, u32> description; - // The last bit of `level` is checked by apps and the kernel to determine if a result code is an - // error - BitField<31, 1, u32> is_error; - constexpr explicit ResultCode(u32 raw) : raw(raw) {} constexpr ResultCode(ErrorModule module_, u32 description_) diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 9c44e27c6..85271d418 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -13,7 +13,7 @@ #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/readable_event.h" -#include "core/hle/kernel/shared_memory.h" +#include "core/hle/kernel/transfer_memory.h" #include "core/hle/kernel/writable_event.h" #include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/am/am.h" @@ -239,8 +239,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger {0, nullptr, "Exit"}, {1, &ISelfController::LockExit, "LockExit"}, {2, &ISelfController::UnlockExit, "UnlockExit"}, - {3, nullptr, "EnterFatalSection"}, - {4, nullptr, "LeaveFatalSection"}, + {3, &ISelfController::EnterFatalSection, "EnterFatalSection"}, + {4, &ISelfController::LeaveFatalSection, "LeaveFatalSection"}, {9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"}, {10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"}, {11, &ISelfController::SetOperationModeChangedNotification, "SetOperationModeChangedNotification"}, @@ -285,41 +285,54 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger ISelfController::~ISelfController() = default; -void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) { - // Takes 3 input u8s with each field located immediately after the previous - // u8, these are bool flags. No output. +void ISelfController::LockExit(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_AM, "(STUBBED) called"); - IPC::RequestParser rp{ctx}; + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} - struct FocusHandlingModeParams { - u8 unknown0; - u8 unknown1; - u8 unknown2; - }; - auto flags = rp.PopRaw<FocusHandlingModeParams>(); +void ISelfController::UnlockExit(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } -void ISelfController::SetRestartMessageEnabled(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); +void ISelfController::EnterFatalSection(Kernel::HLERequestContext& ctx) { + ++num_fatal_sections_entered; + LOG_DEBUG(Service_AM, "called. Num fatal sections entered: {}", num_fatal_sections_entered); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } -void ISelfController::SetPerformanceModeChangedNotification(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; +void ISelfController::LeaveFatalSection(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called."); - bool flag = rp.Pop<bool>(); - LOG_WARNING(Service_AM, "(STUBBED) called flag={}", flag); + // Entry and exit of fatal sections must be balanced. + if (num_fatal_sections_entered == 0) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode{ErrorModule::AM, 512}); + return; + } + + --num_fatal_sections_entered; IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } +void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + launchable_event.writable->Signal(); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(launchable_event.readable); +} + void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_AM, "(STUBBED) called"); @@ -337,40 +350,52 @@ void ISelfController::SetOperationModeChangedNotification(Kernel::HLERequestCont rb.Push(RESULT_SUCCESS); } -void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) { - // Takes 3 input u8s with each field located immediately after the previous - // u8, these are bool flags. No output. +void ISelfController::SetPerformanceModeChangedNotification(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - bool enabled = rp.Pop<bool>(); - LOG_WARNING(Service_AM, "(STUBBED) called enabled={}", enabled); + bool flag = rp.Pop<bool>(); + LOG_WARNING(Service_AM, "(STUBBED) called flag={}", flag); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } -void ISelfController::LockExit(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); +void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) { + // Takes 3 input u8s with each field located immediately after the previous + // u8, these are bool flags. No output. + IPC::RequestParser rp{ctx}; + + struct FocusHandlingModeParams { + u8 unknown0; + u8 unknown1; + u8 unknown2; + }; + const auto flags = rp.PopRaw<FocusHandlingModeParams>(); + + LOG_WARNING(Service_AM, "(STUBBED) called. unknown0={}, unknown1={}, unknown2={}", + flags.unknown0, flags.unknown1, flags.unknown2); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } -void ISelfController::UnlockExit(Kernel::HLERequestContext& ctx) { +void ISelfController::SetRestartMessageEnabled(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_AM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } -void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); +void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) { + // Takes 3 input u8s with each field located immediately after the previous + // u8, these are bool flags. No output. + IPC::RequestParser rp{ctx}; - launchable_event.writable->Signal(); + bool enabled = rp.Pop<bool>(); + LOG_WARNING(Service_AM, "(STUBBED) called enabled={}", enabled); - IPC::ResponseBuilder rb{ctx, 2, 1}; + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(launchable_event.readable); } void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx) { @@ -907,19 +932,19 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex rp.SetCurrentOffset(3); const auto handle{rp.Pop<Kernel::Handle>()}; - const auto shared_mem = - Core::System::GetInstance().CurrentProcess()->GetHandleTable().Get<Kernel::SharedMemory>( + const auto transfer_mem = + Core::System::GetInstance().CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>( handle); - if (shared_mem == nullptr) { + if (transfer_mem == nullptr) { LOG_ERROR(Service_AM, "shared_mem is a nullpr for handle={:08X}", handle); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultCode(-1)); return; } - const u8* mem_begin = shared_mem->GetPointer(); - const u8* mem_end = mem_begin + shared_mem->GetSize(); + const u8* const mem_begin = transfer_mem->GetPointer(); + const u8* const mem_end = mem_begin + transfer_mem->GetSize(); std::vector<u8> memory{mem_begin, mem_end}; IPC::ResponseBuilder rb{ctx, 2, 0, 1}; diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 565dd8e9e..991b7d47c 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -117,17 +117,19 @@ public: ~ISelfController() override; private: - void SetFocusHandlingMode(Kernel::HLERequestContext& ctx); - void SetRestartMessageEnabled(Kernel::HLERequestContext& ctx); - void SetPerformanceModeChangedNotification(Kernel::HLERequestContext& ctx); - void SetOperationModeChangedNotification(Kernel::HLERequestContext& ctx); - void SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx); void LockExit(Kernel::HLERequestContext& ctx); void UnlockExit(Kernel::HLERequestContext& ctx); + void EnterFatalSection(Kernel::HLERequestContext& ctx); + void LeaveFatalSection(Kernel::HLERequestContext& ctx); void GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx); + void SetScreenShotPermission(Kernel::HLERequestContext& ctx); + void SetOperationModeChangedNotification(Kernel::HLERequestContext& ctx); + void SetPerformanceModeChangedNotification(Kernel::HLERequestContext& ctx); + void SetFocusHandlingMode(Kernel::HLERequestContext& ctx); + void SetRestartMessageEnabled(Kernel::HLERequestContext& ctx); + void SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx); void SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx); void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx); - void SetScreenShotPermission(Kernel::HLERequestContext& ctx); void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx); void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); @@ -135,6 +137,7 @@ private: std::shared_ptr<NVFlinger::NVFlinger> nvflinger; Kernel::EventPair launchable_event; u32 idle_time_detection_extension = 0; + u64 num_fatal_sections_entered = 0; }; class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp index 088410564..e5daefdde 100644 --- a/src/core/hle/service/audio/audin_u.cpp +++ b/src/core/hle/service/audio/audin_u.cpp @@ -2,9 +2,6 @@ // 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/audin_u.h" namespace Service::Audio { @@ -33,7 +30,6 @@ public: RegisterHandlers(functions); } - ~IAudioIn() = default; }; AudInU::AudInU() : ServiceFramework("audin:u") { diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 21f5e64c7..39acb7b23 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -150,7 +150,6 @@ private: void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called {}", ctx.Description()); - IPC::RequestParser rp{ctx}; const u64 max_count{ctx.GetWriteBufferSize() / sizeof(u64)}; const auto released_buffers{audio_core.GetTagsAndReleaseBuffers(stream, max_count)}; @@ -194,12 +193,9 @@ private: void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); - IPC::RequestParser rp{ctx}; - ctx.WriteBuffer(DefaultDevice); IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(RESULT_SUCCESS); rb.Push<u32>(1); // Amount of audio devices } diff --git a/src/core/hle/service/audio/audrec_u.cpp b/src/core/hle/service/audio/audrec_u.cpp index 6956a2e64..1a5aed9ed 100644 --- a/src/core/hle/service/audio/audrec_u.cpp +++ b/src/core/hle/service/audio/audrec_u.cpp @@ -2,9 +2,6 @@ // 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/audrec_u.h" namespace Service::Audio { @@ -30,7 +27,6 @@ public: RegisterHandlers(functions); } - ~IFinalOutputRecorder() = default; }; AudRecU::AudRecU() : ServiceFramework("audrec:u") { diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index c9de10a24..1dde6edb7 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -10,6 +10,7 @@ #include "common/alignment.h" #include "common/common_funcs.h" #include "common/logging/log.h" +#include "common/string_util.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/hle_ipc.h" @@ -184,7 +185,6 @@ public: private: void ListAudioDeviceName(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_Audio, "(STUBBED) called"); - IPC::RequestParser rp{ctx}; constexpr std::array<char, 15> audio_interface{{"AudioInterface"}}; ctx.WriteBuffer(audio_interface); @@ -195,13 +195,13 @@ private: } void SetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); - IPC::RequestParser rp{ctx}; - f32 volume = static_cast<f32>(rp.Pop<u32>()); + const f32 volume = rp.Pop<f32>(); - auto file_buffer = ctx.ReadBuffer(); - auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0'); + const auto device_name_buffer = ctx.ReadBuffer(); + const std::string name = Common::StringFromBuffer(device_name_buffer); + + LOG_WARNING(Service_Audio, "(STUBBED) called. name={}, volume={}", name, volume); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -209,7 +209,6 @@ private: void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_Audio, "(STUBBED) called"); - IPC::RequestParser rp{ctx}; constexpr std::array<char, 12> audio_interface{{"AudioDevice"}}; ctx.WriteBuffer(audio_interface); diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp index 770590d0b..2c229bcad 100644 --- a/src/core/hle/service/fatal/fatal.cpp +++ b/src/core/hle/service/fatal/fatal.cpp @@ -25,21 +25,34 @@ Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) Module::Interface::~Interface() = default; struct FatalInfo { - std::array<u64_le, 31> registers{}; // TODO(ogniK): See if this actually is registers or - // not(find a game which has non zero valeus) - u64_le unk0{}; - u64_le unk1{}; - u64_le unk2{}; - u64_le unk3{}; - u64_le unk4{}; - u64_le unk5{}; - u64_le unk6{}; + enum class Architecture : s32 { + AArch64, + AArch32, + }; + + const char* ArchAsString() const { + return arch == Architecture::AArch64 ? "AArch64" : "AArch32"; + } + + std::array<u64_le, 31> registers{}; + u64_le sp{}; + u64_le pc{}; + u64_le pstate{}; + u64_le afsr0{}; + u64_le afsr1{}; + u64_le esr{}; + u64_le far{}; std::array<u64_le, 32> backtrace{}; - u64_le unk7{}; - u64_le unk8{}; + u64_le program_entry_point{}; + + // Bit flags that indicate which registers have been set with values + // for this context. The service itself uses these to determine which + // registers to specifically print out. + u64_le set_flags{}; + u32_le backtrace_size{}; - u32_le unk9{}; + Architecture arch{}; u32_le unk10{}; // TODO(ogniK): Is this even used or is it just padding? }; static_assert(sizeof(FatalInfo) == 0x250, "FatalInfo is an invalid size"); @@ -52,36 +65,36 @@ enum class FatalType : u32 { static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) { const auto title_id = Core::CurrentProcess()->GetTitleID(); - std::string crash_report = - fmt::format("Yuzu {}-{} crash report\n" - "Title ID: {:016x}\n" - "Result: 0x{:X} ({:04}-{:04d})\n" - "\n", - Common::g_scm_branch, Common::g_scm_desc, title_id, error_code.raw, - 2000 + static_cast<u32>(error_code.module.Value()), - static_cast<u32>(error_code.description.Value()), info.unk8, info.unk7); + std::string crash_report = fmt::format( + "Yuzu {}-{} crash report\n" + "Title ID: {:016x}\n" + "Result: 0x{:X} ({:04}-{:04d})\n" + "Set flags: 0x{:16X}\n" + "Program entry point: 0x{:16X}\n" + "\n", + Common::g_scm_branch, Common::g_scm_desc, title_id, error_code.raw, + 2000 + static_cast<u32>(error_code.module.Value()), + static_cast<u32>(error_code.description.Value()), info.set_flags, info.program_entry_point); if (info.backtrace_size != 0x0) { crash_report += "Registers:\n"; - // TODO(ogniK): This is just a guess, find a game which actually has non zero values for (size_t i = 0; i < info.registers.size(); i++) { crash_report += fmt::format(" X[{:02d}]: {:016x}\n", i, info.registers[i]); } - crash_report += fmt::format(" Unknown 0: {:016x}\n", info.unk0); - crash_report += fmt::format(" Unknown 1: {:016x}\n", info.unk1); - crash_report += fmt::format(" Unknown 2: {:016x}\n", info.unk2); - crash_report += fmt::format(" Unknown 3: {:016x}\n", info.unk3); - crash_report += fmt::format(" Unknown 4: {:016x}\n", info.unk4); - crash_report += fmt::format(" Unknown 5: {:016x}\n", info.unk5); - crash_report += fmt::format(" Unknown 6: {:016x}\n", info.unk6); + crash_report += fmt::format(" SP: {:016x}\n", info.sp); + crash_report += fmt::format(" PC: {:016x}\n", info.pc); + crash_report += fmt::format(" PSTATE: {:016x}\n", info.pstate); + crash_report += fmt::format(" AFSR0: {:016x}\n", info.afsr0); + crash_report += fmt::format(" AFSR1: {:016x}\n", info.afsr1); + crash_report += fmt::format(" ESR: {:016x}\n", info.esr); + crash_report += fmt::format(" FAR: {:016x}\n", info.far); crash_report += "\nBacktrace:\n"; for (size_t i = 0; i < info.backtrace_size; i++) { crash_report += fmt::format(" Backtrace[{:02d}]: {:016x}\n", i, info.backtrace[i]); } - crash_report += fmt::format("\nUnknown 7: 0x{:016x}\n", info.unk7); - crash_report += fmt::format("Unknown 8: 0x{:016x}\n", info.unk8); - crash_report += fmt::format("Unknown 9: 0x{:016x}\n", info.unk9); + + crash_report += fmt::format("Architecture: {}\n", info.ArchAsString()); crash_report += fmt::format("Unknown 10: 0x{:016x}\n", info.unk10); } @@ -125,13 +138,13 @@ static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const F case FatalType::ErrorReport: GenerateErrorReport(error_code, info); break; - }; + } } void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) { LOG_ERROR(Service_Fatal, "called"); IPC::RequestParser rp{ctx}; - auto error_code = rp.Pop<ResultCode>(); + const auto error_code = rp.Pop<ResultCode>(); ThrowFatalError(error_code, FatalType::ErrorScreen, {}); IPC::ResponseBuilder rb{ctx, 2}; @@ -141,8 +154,8 @@ void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) { void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) { LOG_ERROR(Service_Fatal, "called"); IPC::RequestParser rp(ctx); - auto error_code = rp.Pop<ResultCode>(); - auto fatal_type = rp.PopEnum<FatalType>(); + const auto error_code = rp.Pop<ResultCode>(); + const auto fatal_type = rp.PopEnum<FatalType>(); ThrowFatalError(error_code, fatal_type, {}); // No info is passed with ThrowFatalWithPolicy IPC::ResponseBuilder rb{ctx, 2}; @@ -152,9 +165,9 @@ void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) { void Module::Interface::ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx) { LOG_ERROR(Service_Fatal, "called"); IPC::RequestParser rp(ctx); - auto error_code = rp.Pop<ResultCode>(); - auto fatal_type = rp.PopEnum<FatalType>(); - auto fatal_info = ctx.ReadBuffer(); + const auto error_code = rp.Pop<ResultCode>(); + const auto fatal_type = rp.PopEnum<FatalType>(); + const auto fatal_info = ctx.ReadBuffer(); FatalInfo info{}; ASSERT_MSG(fatal_info.size() == sizeof(FatalInfo), "Invalid fatal info buffer size!"); diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index c6da2df43..4c2b371c3 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -197,13 +197,16 @@ ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_pa ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path_, FileSys::Mode mode) const { - std::string path(FileUtil::SanitizePath(path_)); - auto npath = path; - while (npath.size() > 0 && (npath[0] == '/' || npath[0] == '\\')) - npath = npath.substr(1); + const std::string path(FileUtil::SanitizePath(path_)); + std::string_view npath = path; + while (!npath.empty() && (npath[0] == '/' || npath[0] == '\\')) { + npath.remove_prefix(1); + } + auto file = backing->GetFileRelative(npath); - if (file == nullptr) + if (file == nullptr) { return FileSys::ERROR_PATH_NOT_FOUND; + } if (mode == FileSys::Mode::Append) { return MakeResult<FileSys::VirtualFile>( @@ -319,15 +322,15 @@ ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId stora } ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, - FileSys::SaveDataDescriptor save_struct) { + const FileSys::SaveDataDescriptor& descriptor) { LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}", - static_cast<u8>(space), save_struct.DebugInfo()); + static_cast<u8>(space), descriptor.DebugInfo()); if (save_data_factory == nullptr) { return FileSys::ERROR_ENTITY_NOT_FOUND; } - return save_data_factory->Open(space, save_struct); + return save_data_factory->Open(space, descriptor); } ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) { diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index 6fd5e7b23..7cfc0d902 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h @@ -46,7 +46,7 @@ ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess(); ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id, FileSys::ContentRecordType type); ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, - FileSys::SaveDataDescriptor save_struct); + const FileSys::SaveDataDescriptor& descriptor); ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space); ResultVal<FileSys::VirtualDir> OpenSDMC(); diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index f03fb629c..657baddb8 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -315,61 +315,53 @@ public: void CreateFile(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - auto file_buffer = ctx.ReadBuffer(); - std::string name = Common::StringFromBuffer(file_buffer); + const auto file_buffer = ctx.ReadBuffer(); + const std::string name = Common::StringFromBuffer(file_buffer); - u64 mode = rp.Pop<u64>(); - u32 size = rp.Pop<u32>(); + const u64 mode = rp.Pop<u64>(); + const u32 size = rp.Pop<u32>(); - LOG_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)); } void DeleteFile(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - auto file_buffer = ctx.ReadBuffer(); - std::string name = Common::StringFromBuffer(file_buffer); + const auto file_buffer = ctx.ReadBuffer(); + const std::string name = Common::StringFromBuffer(file_buffer); - LOG_DEBUG(Service_FS, "called file {}", name); + LOG_DEBUG(Service_FS, "called. file={}", name); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(backend.DeleteFile(name)); } void CreateDirectory(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - auto file_buffer = ctx.ReadBuffer(); - std::string name = Common::StringFromBuffer(file_buffer); + const auto file_buffer = ctx.ReadBuffer(); + const std::string name = Common::StringFromBuffer(file_buffer); - LOG_DEBUG(Service_FS, "called directory {}", name); + LOG_DEBUG(Service_FS, "called. directory={}", name); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(backend.CreateDirectory(name)); } void DeleteDirectory(Kernel::HLERequestContext& ctx) { - const IPC::RequestParser rp{ctx}; - const auto file_buffer = ctx.ReadBuffer(); - std::string name = Common::StringFromBuffer(file_buffer); + const std::string name = Common::StringFromBuffer(file_buffer); - LOG_DEBUG(Service_FS, "called directory {}", name); + LOG_DEBUG(Service_FS, "called. directory={}", name); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(backend.DeleteDirectory(name)); } void DeleteDirectoryRecursively(Kernel::HLERequestContext& ctx) { - const IPC::RequestParser rp{ctx}; - const auto file_buffer = ctx.ReadBuffer(); - std::string name = Common::StringFromBuffer(file_buffer); + const std::string name = Common::StringFromBuffer(file_buffer); - LOG_DEBUG(Service_FS, "called directory {}", name); + LOG_DEBUG(Service_FS, "called. directory={}", name); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(backend.DeleteDirectoryRecursively(name)); @@ -386,18 +378,16 @@ public: } void RenameFile(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - std::vector<u8> buffer; buffer.resize(ctx.BufferDescriptorX()[0].Size()); Memory::ReadBlock(ctx.BufferDescriptorX()[0].Address(), buffer.data(), buffer.size()); - std::string src_name = Common::StringFromBuffer(buffer); + const std::string src_name = Common::StringFromBuffer(buffer); buffer.resize(ctx.BufferDescriptorX()[1].Size()); Memory::ReadBlock(ctx.BufferDescriptorX()[1].Address(), buffer.data(), buffer.size()); - std::string dst_name = Common::StringFromBuffer(buffer); + const std::string dst_name = Common::StringFromBuffer(buffer); - LOG_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)); @@ -406,12 +396,12 @@ public: void OpenFile(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - auto file_buffer = ctx.ReadBuffer(); - std::string name = Common::StringFromBuffer(file_buffer); + const auto file_buffer = ctx.ReadBuffer(); + const std::string name = Common::StringFromBuffer(file_buffer); - auto mode = static_cast<FileSys::Mode>(rp.Pop<u32>()); + const auto mode = static_cast<FileSys::Mode>(rp.Pop<u32>()); - LOG_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()) { @@ -430,13 +420,13 @@ public: void OpenDirectory(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - auto file_buffer = ctx.ReadBuffer(); - std::string name = Common::StringFromBuffer(file_buffer); + const auto file_buffer = ctx.ReadBuffer(); + const std::string name = Common::StringFromBuffer(file_buffer); // TODO(Subv): Implement this filter. - u32 filter_flags = rp.Pop<u32>(); + const u32 filter_flags = rp.Pop<u32>(); - LOG_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()) { @@ -453,12 +443,10 @@ public: } void GetEntryType(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - auto file_buffer = ctx.ReadBuffer(); - std::string name = Common::StringFromBuffer(file_buffer); + const auto file_buffer = ctx.ReadBuffer(); + const std::string name = Common::StringFromBuffer(file_buffer); - LOG_DEBUG(Service_FS, "called file {}", name); + LOG_DEBUG(Service_FS, "called. file={}", name); auto result = backend.GetEntryType(name); if (result.Failed()) { @@ -616,7 +604,9 @@ private: u64_le save_id; u64_le title_id; u64_le save_image_size; - INSERT_PADDING_BYTES(0x28); + u16_le index; + FileSys::SaveDataRank rank; + INSERT_PADDING_BYTES(0x25); }; static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size."); @@ -779,16 +769,17 @@ void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) { } void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - auto space_id = rp.PopRaw<FileSys::SaveDataSpaceId>(); - auto unk = rp.Pop<u32>(); - LOG_INFO(Service_FS, "called with unknown={:08X}", unk); + LOG_INFO(Service_FS, "called."); - auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>(); + struct Parameters { + FileSys::SaveDataSpaceId save_data_space_id; + FileSys::SaveDataDescriptor descriptor; + }; - auto dir = OpenSaveData(space_id, save_struct); + IPC::RequestParser rp{ctx}; + const auto parameters = rp.PopRaw<Parameters>(); + auto dir = OpenSaveData(parameters.save_data_space_id, parameters.descriptor); if (dir.Failed()) { IPC::ResponseBuilder rb{ctx, 2, 0, 0}; rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp index 5c62d42ba..ca88bf97f 100644 --- a/src/core/hle/service/nfc/nfc.cpp +++ b/src/core/hle/service/nfc/nfc.cpp @@ -150,7 +150,7 @@ private: IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.PushRaw<u8>(Settings::values.enable_nfc); + rb.PushRaw<u8>(true); } void GetStateOld(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 1c4482e47..c6babdd4d 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp @@ -335,7 +335,7 @@ void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { } bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) { - std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); + std::lock_guard lock{HLE::g_hle_lock}; if (buffer.size() < sizeof(AmiiboFile)) { return false; } diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h index ace71169f..12f3ef825 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h @@ -18,7 +18,7 @@ class nvmap; class nvdisp_disp0 final : public nvdevice { public: explicit nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev); - ~nvdisp_disp0(); + ~nvdisp_disp0() override; u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h index fe311b069..5b4889910 100644 --- a/src/core/hle/service/nvdrv/interface.h +++ b/src/core/hle/service/nvdrv/interface.h @@ -17,7 +17,7 @@ namespace Service::Nvidia { class NVDRV final : public ServiceFramework<NVDRV> { public: NVDRV(std::shared_ptr<Module> nvdrv, const char* name); - ~NVDRV(); + ~NVDRV() override; private: void Open(Kernel::HLERequestContext& ctx); diff --git a/src/core/hle/service/nvdrv/nvmemp.h b/src/core/hle/service/nvdrv/nvmemp.h index 5a4dfc1f9..6eafb1346 100644 --- a/src/core/hle/service/nvdrv/nvmemp.h +++ b/src/core/hle/service/nvdrv/nvmemp.h @@ -11,7 +11,7 @@ namespace Service::Nvidia { class NVMEMP final : public ServiceFramework<NVMEMP> { public: NVMEMP(); - ~NVMEMP(); + ~NVMEMP() override; private: void Cmd0(Kernel::HLERequestContext& ctx); diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 830790269..abbfe5524 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -90,7 +90,7 @@ private: Kernel::HLERequestContext& ctx); ServiceFrameworkBase(const char* service_name, u32 max_sessions, InvokerFn* handler_invoker); - ~ServiceFrameworkBase(); + ~ServiceFrameworkBase() override; void RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n); void ReportUnimplementedFunction(Kernel::HLERequestContext& ctx, const FunctionInfoBase* info); diff --git a/src/core/hle/service/set/set_cal.h b/src/core/hle/service/set/set_cal.h index 583036eac..a0677e815 100644 --- a/src/core/hle/service/set/set_cal.h +++ b/src/core/hle/service/set/set_cal.h @@ -11,7 +11,7 @@ namespace Service::Set { class SET_CAL final : public ServiceFramework<SET_CAL> { public: explicit SET_CAL(); - ~SET_CAL(); + ~SET_CAL() override; }; } // namespace Service::Set diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp index 13ab1d31e..852e71e4b 100644 --- a/src/core/hle/service/sockets/sfdnsres.cpp +++ b/src/core/hle/service/sockets/sfdnsres.cpp @@ -8,12 +8,20 @@ namespace Service::Sockets { void SFDNSRES::GetAddrInfo(Kernel::HLERequestContext& ctx) { + struct Parameters { + u8 use_nsd_resolve; + u32 unknown; + u64 process_id; + }; + IPC::RequestParser rp{ctx}; + const auto parameters = rp.PopRaw<Parameters>(); - LOG_WARNING(Service, "(STUBBED) called"); + LOG_WARNING(Service, + "(STUBBED) called. use_nsd_resolve={}, unknown=0x{:08X}, process_id=0x{:016X}", + parameters.use_nsd_resolve, parameters.unknown, parameters.process_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); } diff --git a/src/core/hle/service/spl/module.cpp b/src/core/hle/service/spl/module.cpp index 8db0c2f13..e724d4ab8 100644 --- a/src/core/hle/service/spl/module.cpp +++ b/src/core/hle/service/spl/module.cpp @@ -26,9 +26,7 @@ Module::Interface::~Interface() = default; void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_SPL, "called"); - IPC::RequestParser rp{ctx}; - - std::size_t size = ctx.GetWriteBufferSize(); + const std::size_t size = ctx.GetWriteBufferSize(); std::uniform_int_distribution<u16> distribution(0, std::numeric_limits<u8>::max()); std::vector<u8> data(size); diff --git a/src/core/hle/service/ssl/ssl.cpp b/src/core/hle/service/ssl/ssl.cpp index af40a1815..f7f87a958 100644 --- a/src/core/hle/service/ssl/ssl.cpp +++ b/src/core/hle/service/ssl/ssl.cpp @@ -64,13 +64,19 @@ public: }; RegisterHandlers(functions); } - ~ISslContext() = default; private: void SetOption(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_SSL, "(STUBBED) called"); + struct Parameters { + u8 enable; + u32 option; + }; IPC::RequestParser rp{ctx}; + const auto parameters = rp.PopRaw<Parameters>(); + + LOG_WARNING(Service_SSL, "(STUBBED) called. enable={}, option={}", parameters.enable, + parameters.option); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 566cd6006..4e17249a9 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -498,7 +498,6 @@ public: }; RegisterHandlers(functions); } - ~IHOSBinderDriver() = default; private: enum class TransactionId { @@ -692,7 +691,6 @@ public: }; RegisterHandlers(functions); } - ~ISystemDisplayService() = default; private: void SetLayerZ(Kernel::HLERequestContext& ctx) { @@ -818,7 +816,6 @@ public: }; RegisterHandlers(functions); } - ~IManagerDisplayService() = default; private: void CloseDisplay(Kernel::HLERequestContext& ctx) { @@ -884,7 +881,6 @@ private: class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> { public: explicit IApplicationDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); - ~IApplicationDisplayService() = default; private: enum class ConvertedScaleMode : u64 { @@ -1037,7 +1033,6 @@ private: void ListDisplays(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_VI, "(STUBBED) called"); - IPC::RequestParser rp{ctx}; DisplayInfo display_info; display_info.width *= static_cast<u64>(Settings::values.resolution_factor); display_info.height *= static_cast<u64>(Settings::values.resolution_factor); diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index 8b1920f22..46ac372f6 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp @@ -341,7 +341,7 @@ Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) { } codeset.entrypoint = base_addr + header->e_entry; - codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image)); + codeset.memory = std::move(program_image); LOG_DEBUG(Loader, "Done loading."); diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 5de02a94b..31e4a0c84 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -187,7 +187,7 @@ static bool LoadNroImpl(Kernel::Process& process, const std::vector<u8>& data, program_image.resize(static_cast<u32>(program_image.size()) + bss_size); // Load codeset for current process - codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image)); + codeset.memory = std::move(program_image); process.LoadModule(std::move(codeset), load_base); // Register module with GDBStub diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 714d85a59..ffe2eea8a 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -4,11 +4,12 @@ #include <cinttypes> #include <vector> -#include <lz4.h> + #include "common/common_funcs.h" #include "common/file_util.h" #include "common/hex_util.h" #include "common/logging/log.h" +#include "common/lz4_compression.h" #include "common/swap.h" #include "core/core.h" #include "core/file_sys/patch_manager.h" @@ -35,15 +36,11 @@ static_assert(sizeof(MODHeader) == 0x1c, "MODHeader has incorrect size."); std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data, const NSOSegmentHeader& header) { - std::vector<u8> uncompressed_data(header.size); - const int bytes_uncompressed = - LZ4_decompress_safe(reinterpret_cast<const char*>(compressed_data.data()), - reinterpret_cast<char*>(uncompressed_data.data()), - static_cast<int>(compressed_data.size()), header.size); + const std::vector<u8> uncompressed_data = + Common::Compression::DecompressDataLZ4(compressed_data, header.size); - ASSERT_MSG(bytes_uncompressed == static_cast<int>(header.size) && - bytes_uncompressed == static_cast<int>(uncompressed_data.size()), - "{} != {} != {}", bytes_uncompressed, header.size, uncompressed_data.size()); + ASSERT_MSG(uncompressed_data.size() == static_cast<int>(header.size), "{} != {}", header.size, + uncompressed_data.size()); return uncompressed_data; } @@ -161,7 +158,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, } // Load codeset for current process - codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image)); + codeset.memory = std::move(program_image); process.LoadModule(std::move(codeset), load_base); // Register module with GDBStub diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h index d6995b61e..436f7387c 100644 --- a/src/core/loader/xci.h +++ b/src/core/loader/xci.h @@ -22,7 +22,7 @@ class AppLoader_NCA; class AppLoader_XCI final : public AppLoader { public: explicit AppLoader_XCI(FileSys::VirtualFile file); - ~AppLoader_XCI(); + ~AppLoader_XCI() override; /** * Returns the type of the file diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp index c716a462b..4afd6c8a3 100644 --- a/src/core/perf_stats.cpp +++ b/src/core/perf_stats.cpp @@ -18,13 +18,13 @@ using std::chrono::microseconds; namespace Core { void PerfStats::BeginSystemFrame() { - std::lock_guard<std::mutex> lock(object_mutex); + std::lock_guard lock{object_mutex}; frame_begin = Clock::now(); } void PerfStats::EndSystemFrame() { - std::lock_guard<std::mutex> lock(object_mutex); + std::lock_guard lock{object_mutex}; auto frame_end = Clock::now(); accumulated_frametime += frame_end - frame_begin; @@ -35,13 +35,13 @@ void PerfStats::EndSystemFrame() { } void PerfStats::EndGameFrame() { - std::lock_guard<std::mutex> lock(object_mutex); + std::lock_guard lock{object_mutex}; game_frames += 1; } PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) { - std::lock_guard<std::mutex> lock(object_mutex); + std::lock_guard lock{object_mutex}; const auto now = Clock::now(); // Walltime elapsed since stats were reset @@ -67,7 +67,7 @@ PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us } double PerfStats::GetLastFrameTimeScale() { - std::lock_guard<std::mutex> lock(object_mutex); + std::lock_guard lock{object_mutex}; constexpr double FRAME_LENGTH = 1.0 / 60; return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH; diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 6dd3139cc..6d32ebea3 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -82,7 +82,6 @@ void LogSetting(const std::string& name, const T& value) { void LogSettings() { LOG_INFO(Config, "yuzu Configuration:"); LogSetting("System_UseDockedMode", Settings::values.use_docked_mode); - LogSetting("System_EnableNfc", Settings::values.enable_nfc); LogSetting("System_RngSeed", Settings::values.rng_seed.value_or(0)); LogSetting("System_CurrentUser", Settings::values.current_user); LogSetting("System_LanguageIndex", Settings::values.language_index); diff --git a/src/core/settings.h b/src/core/settings.h index cdfb2f742..d543eb32f 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -349,7 +349,6 @@ struct TouchscreenInput { struct Values { // System bool use_docked_mode; - bool enable_nfc; std::optional<u32> rng_seed; // Measured in seconds since epoch std::optional<std::chrono::seconds> custom_rtc; diff --git a/src/input_common/keyboard.cpp b/src/input_common/keyboard.cpp index 525fe6abc..078374be5 100644 --- a/src/input_common/keyboard.cpp +++ b/src/input_common/keyboard.cpp @@ -36,18 +36,18 @@ struct KeyButtonPair { class KeyButtonList { public: void AddKeyButton(int key_code, KeyButton* key_button) { - std::lock_guard<std::mutex> guard(mutex); + std::lock_guard guard{mutex}; list.push_back(KeyButtonPair{key_code, key_button}); } void RemoveKeyButton(const KeyButton* key_button) { - std::lock_guard<std::mutex> guard(mutex); + std::lock_guard guard{mutex}; list.remove_if( [key_button](const KeyButtonPair& pair) { return pair.key_button == key_button; }); } void ChangeKeyStatus(int key_code, bool pressed) { - std::lock_guard<std::mutex> guard(mutex); + std::lock_guard guard{mutex}; for (const KeyButtonPair& pair : list) { if (pair.key_code == key_code) pair.key_button->status.store(pressed); @@ -55,7 +55,7 @@ public: } void ChangeAllKeyStatus(bool pressed) { - std::lock_guard<std::mutex> guard(mutex); + std::lock_guard guard{mutex}; for (const KeyButtonPair& pair : list) { pair.key_button->status.store(pressed); } diff --git a/src/input_common/motion_emu.cpp b/src/input_common/motion_emu.cpp index 6d96d4019..868251628 100644 --- a/src/input_common/motion_emu.cpp +++ b/src/input_common/motion_emu.cpp @@ -39,7 +39,7 @@ public: void Tilt(int x, int y) { auto mouse_move = Common::MakeVec(x, y) - mouse_origin; if (is_tilting) { - std::lock_guard<std::mutex> guard(tilt_mutex); + std::lock_guard guard{tilt_mutex}; if (mouse_move.x == 0 && mouse_move.y == 0) { tilt_angle = 0; } else { @@ -51,13 +51,13 @@ public: } void EndTilt() { - std::lock_guard<std::mutex> guard(tilt_mutex); + std::lock_guard guard{tilt_mutex}; tilt_angle = 0; is_tilting = false; } std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() { - std::lock_guard<std::mutex> guard(status_mutex); + std::lock_guard guard{status_mutex}; return status; } @@ -93,7 +93,7 @@ private: old_q = q; { - std::lock_guard<std::mutex> guard(tilt_mutex); + std::lock_guard guard{tilt_mutex}; // Find the quaternion describing current 3DS tilting q = Common::MakeQuaternion( @@ -115,7 +115,7 @@ private: // Update the sensor state { - std::lock_guard<std::mutex> guard(status_mutex); + std::lock_guard guard{status_mutex}; status = std::make_tuple(gravity, angular_rate); } } diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index b132d77f5..5949ecbae 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -55,22 +55,22 @@ public: : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, deleter} {} void SetButton(int button, bool value) { - std::lock_guard<std::mutex> lock(mutex); + std::lock_guard lock{mutex}; state.buttons[button] = value; } bool GetButton(int button) const { - std::lock_guard<std::mutex> lock(mutex); + std::lock_guard lock{mutex}; return state.buttons.at(button); } void SetAxis(int axis, Sint16 value) { - std::lock_guard<std::mutex> lock(mutex); + std::lock_guard lock{mutex}; state.axes[axis] = value; } float GetAxis(int axis) const { - std::lock_guard<std::mutex> lock(mutex); + std::lock_guard lock{mutex}; return state.axes.at(axis) / 32767.0f; } @@ -92,12 +92,12 @@ public: } void SetHat(int hat, Uint8 direction) { - std::lock_guard<std::mutex> lock(mutex); + std::lock_guard lock{mutex}; state.hats[hat] = direction; } bool GetHatDirection(int hat, Uint8 direction) const { - std::lock_guard<std::mutex> lock(mutex); + std::lock_guard lock{mutex}; return (state.hats.at(hat) & direction) != 0; } /** @@ -140,7 +140,7 @@ private: * Get the nth joystick with the corresponding GUID */ std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) { - std::lock_guard<std::mutex> lock(joystick_map_mutex); + std::lock_guard lock{joystick_map_mutex}; const auto it = joystick_map.find(guid); if (it != joystick_map.end()) { while (it->second.size() <= port) { @@ -161,7 +161,8 @@ std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& g std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) { auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id); const std::string guid = GetGUID(sdl_joystick); - std::lock_guard<std::mutex> lock(joystick_map_mutex); + + std::lock_guard lock{joystick_map_mutex}; auto map_it = joystick_map.find(guid); if (map_it != joystick_map.end()) { auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(), @@ -198,8 +199,9 @@ void SDLState::InitJoystick(int joystick_index) { LOG_ERROR(Input, "failed to open joystick {}", joystick_index); return; } - std::string guid = GetGUID(sdl_joystick); - std::lock_guard<std::mutex> lock(joystick_map_mutex); + const std::string guid = GetGUID(sdl_joystick); + + std::lock_guard lock{joystick_map_mutex}; if (joystick_map.find(guid) == joystick_map.end()) { auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick); joystick_map[guid].emplace_back(std::move(joystick)); @@ -221,7 +223,7 @@ void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) { std::string guid = GetGUID(sdl_joystick); std::shared_ptr<SDLJoystick> joystick; { - std::lock_guard<std::mutex> lock(joystick_map_mutex); + std::lock_guard lock{joystick_map_mutex}; // This call to guid is safe since the joystick is guaranteed to be in the map auto& joystick_guid_list = joystick_map[guid]; const auto joystick_it = @@ -274,7 +276,7 @@ void SDLState::HandleGameControllerEvent(const SDL_Event& event) { } void SDLState::CloseJoysticks() { - std::lock_guard<std::mutex> lock(joystick_map_mutex); + std::lock_guard lock{joystick_map_mutex}; joystick_map.clear(); } diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 14b76680f..242a0d1cd 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -128,7 +128,9 @@ if (ENABLE_VULKAN) renderer_vulkan/vk_scheduler.cpp renderer_vulkan/vk_scheduler.h renderer_vulkan/vk_stream_buffer.cpp - renderer_vulkan/vk_stream_buffer.h) + renderer_vulkan/vk_stream_buffer.h + renderer_vulkan/vk_swapchain.cpp + renderer_vulkan/vk_swapchain.h) target_include_directories(video_core PRIVATE ../../externals/Vulkan-Headers/include) target_compile_definitions(video_core PRIVATE HAS_VULKAN) @@ -137,4 +139,4 @@ endif() create_target_directory_groups(video_core) target_link_libraries(video_core PUBLIC common core) -target_link_libraries(video_core PRIVATE glad lz4_static) +target_link_libraries(video_core PRIVATE glad) diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp index 5ffb492ea..f0ef67535 100644 --- a/src/video_core/debug_utils/debug_utils.cpp +++ b/src/video_core/debug_utils/debug_utils.cpp @@ -10,7 +10,7 @@ namespace Tegra { void DebugContext::DoOnEvent(Event event, void* data) { { - std::unique_lock<std::mutex> lock(breakpoint_mutex); + std::unique_lock lock{breakpoint_mutex}; // TODO(Subv): Commit the rasterizer's caches so framebuffers, render targets, etc. will // show on debug widgets @@ -32,7 +32,7 @@ void DebugContext::DoOnEvent(Event event, void* data) { void DebugContext::Resume() { { - std::lock_guard<std::mutex> lock(breakpoint_mutex); + std::lock_guard lock{breakpoint_mutex}; // Tell all observers that we are about to resume for (auto& breakpoint_observer : breakpoint_observers) { diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h index c235faf46..ac3a2eb01 100644 --- a/src/video_core/debug_utils/debug_utils.h +++ b/src/video_core/debug_utils/debug_utils.h @@ -40,7 +40,7 @@ public: /// Constructs the object such that it observes events of the given DebugContext. explicit BreakPointObserver(std::shared_ptr<DebugContext> debug_context) : context_weak(debug_context) { - std::unique_lock<std::mutex> lock(debug_context->breakpoint_mutex); + std::unique_lock lock{debug_context->breakpoint_mutex}; debug_context->breakpoint_observers.push_back(this); } @@ -48,7 +48,7 @@ public: auto context = context_weak.lock(); if (context) { { - std::unique_lock<std::mutex> lock(context->breakpoint_mutex); + std::unique_lock lock{context->breakpoint_mutex}; context->breakpoint_observers.remove(this); } diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp index 03b7ee5d8..55966eef1 100644 --- a/src/video_core/engines/fermi_2d.cpp +++ b/src/video_core/engines/fermi_2d.cpp @@ -6,12 +6,13 @@ #include "common/logging/log.h" #include "common/math_util.h" #include "video_core/engines/fermi_2d.h" +#include "video_core/memory_manager.h" #include "video_core/rasterizer_interface.h" namespace Tegra::Engines { Fermi2D::Fermi2D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager) - : memory_manager(memory_manager), rasterizer{rasterizer} {} + : rasterizer{rasterizer}, memory_manager{memory_manager} {} void Fermi2D::CallMethod(const GPU::MethodCall& method_call) { ASSERT_MSG(method_call.method < Regs::NUM_REGS, diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h index 80523e320..2e51b7f13 100644 --- a/src/video_core/engines/fermi_2d.h +++ b/src/video_core/engines/fermi_2d.h @@ -10,7 +10,10 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "video_core/gpu.h" -#include "video_core/memory_manager.h" + +namespace Tegra { +class MemoryManager; +} namespace VideoCore { class RasterizerInterface; @@ -115,10 +118,9 @@ public: }; } regs{}; - MemoryManager& memory_manager; - private: VideoCore::RasterizerInterface& rasterizer; + MemoryManager& memory_manager; /// Performs the copy from the source surface to the destination surface as configured in the /// registers. diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h index 6575afd0f..fb6cdf432 100644 --- a/src/video_core/engines/kepler_compute.h +++ b/src/video_core/engines/kepler_compute.h @@ -9,7 +9,10 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "video_core/gpu.h" -#include "video_core/memory_manager.h" + +namespace Tegra { +class MemoryManager; +} namespace Tegra::Engines { @@ -40,10 +43,11 @@ public: static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), "KeplerCompute Regs has wrong size"); - MemoryManager& memory_manager; - /// Write the value to the register identified by method. void CallMethod(const GPU::MethodCall& method_call); + +private: + MemoryManager& memory_manager; }; #define ASSERT_REG_POSITION(field_name, position) \ diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp index e259bf46b..cd51a31d7 100644 --- a/src/video_core/engines/kepler_memory.cpp +++ b/src/video_core/engines/kepler_memory.cpp @@ -5,9 +5,9 @@ #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" -#include "core/memory.h" #include "video_core/engines/kepler_memory.h" #include "video_core/engines/maxwell_3d.h" +#include "video_core/memory_manager.h" #include "video_core/rasterizer_interface.h" #include "video_core/renderer_base.h" @@ -15,7 +15,7 @@ namespace Tegra::Engines { KeplerMemory::KeplerMemory(Core::System& system, VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager) - : system{system}, memory_manager(memory_manager), rasterizer{rasterizer} {} + : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager} {} KeplerMemory::~KeplerMemory() = default; diff --git a/src/video_core/engines/kepler_memory.h b/src/video_core/engines/kepler_memory.h index 9181e9d80..78b6c3e45 100644 --- a/src/video_core/engines/kepler_memory.h +++ b/src/video_core/engines/kepler_memory.h @@ -10,12 +10,15 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "video_core/gpu.h" -#include "video_core/memory_manager.h" namespace Core { class System; } +namespace Tegra { +class MemoryManager; +} + namespace VideoCore { class RasterizerInterface; } @@ -82,8 +85,8 @@ public: private: Core::System& system; - MemoryManager& memory_manager; VideoCore::RasterizerInterface& rasterizer; + MemoryManager& memory_manager; void ProcessData(u32 data); }; diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 7713e10e2..8194a4b4a 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -7,11 +7,10 @@ #include "common/assert.h" #include "core/core.h" #include "core/core_timing.h" -#include "core/memory.h" #include "video_core/debug_utils/debug_utils.h" #include "video_core/engines/maxwell_3d.h" +#include "video_core/memory_manager.h" #include "video_core/rasterizer_interface.h" -#include "video_core/renderer_base.h" #include "video_core/textures/texture.h" namespace Tegra::Engines { @@ -21,8 +20,8 @@ constexpr u32 MacroRegistersStart = 0xE00; Maxwell3D::Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager) - : memory_manager(memory_manager), system{system}, rasterizer{rasterizer}, - macro_interpreter(*this) { + : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager}, macro_interpreter{ + *this} { InitializeRegisterDefaults(); } diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index f7ecee254..321af3297 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -16,13 +16,16 @@ #include "common/math_util.h" #include "video_core/gpu.h" #include "video_core/macro_interpreter.h" -#include "video_core/memory_manager.h" #include "video_core/textures/texture.h" namespace Core { class System; } +namespace Tegra { +class MemoryManager; +} + namespace VideoCore { class RasterizerInterface; } @@ -1103,7 +1106,6 @@ public: }; State state{}; - MemoryManager& memory_manager; struct DirtyFlags { std::bitset<8> color_buffer{0xFF}; @@ -1151,6 +1153,8 @@ private: VideoCore::RasterizerInterface& rasterizer; + MemoryManager& memory_manager; + /// Start offsets of each macro in macro_memory std::unordered_map<u32, u32> macro_offsets; diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index 5cca5c29a..2426d0067 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp @@ -5,9 +5,9 @@ #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" -#include "core/memory.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/engines/maxwell_dma.h" +#include "video_core/memory_manager.h" #include "video_core/rasterizer_interface.h" #include "video_core/renderer_base.h" #include "video_core/textures/decoders.h" @@ -16,7 +16,7 @@ namespace Tegra::Engines { MaxwellDMA::MaxwellDMA(Core::System& system, VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager) - : memory_manager(memory_manager), system{system}, rasterizer{rasterizer} {} + : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager} {} void MaxwellDMA::CallMethod(const GPU::MethodCall& method_call) { ASSERT_MSG(method_call.method < Regs::NUM_REGS, diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h index 34c369320..c6b649842 100644 --- a/src/video_core/engines/maxwell_dma.h +++ b/src/video_core/engines/maxwell_dma.h @@ -10,12 +10,15 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "video_core/gpu.h" -#include "video_core/memory_manager.h" namespace Core { class System; } +namespace Tegra { +class MemoryManager; +} + namespace VideoCore { class RasterizerInterface; } @@ -139,13 +142,13 @@ public: }; } regs{}; - MemoryManager& memory_manager; - private: Core::System& system; VideoCore::RasterizerInterface& rasterizer; + MemoryManager& memory_manager; + /// Performs the copy from the source buffer to the destination buffer as configured in the /// registers. void HandleCopy(); diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp index 8b355cf7b..db507cf04 100644 --- a/src/video_core/gpu_asynch.cpp +++ b/src/video_core/gpu_asynch.cpp @@ -9,7 +9,7 @@ namespace VideoCommon { GPUAsynch::GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer) - : Tegra::GPU(system, renderer), gpu_thread{renderer, *dma_pusher} {} + : Tegra::GPU(system, renderer), gpu_thread{system, renderer, *dma_pusher} {} GPUAsynch::~GPUAsynch() = default; diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index 086b2f625..cc56cf467 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp @@ -4,6 +4,9 @@ #include "common/assert.h" #include "common/microprofile.h" +#include "core/core.h" +#include "core/core_timing.h" +#include "core/core_timing_util.h" #include "core/frontend/scope_acquire_window_context.h" #include "video_core/dma_pusher.h" #include "video_core/gpu.h" @@ -36,7 +39,6 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p dma_pusher.Push(std::move(submit_list->entries)); dma_pusher.DispatchCalls(); } else if (const auto data = std::get_if<SwapBuffersCommand>(&next.data)) { - state.DecrementFramesCounter(); renderer.SwapBuffers(std::move(data->framebuffer)); } else if (const auto data = std::get_if<FlushRegionCommand>(&next.data)) { renderer.Rasterizer().FlushRegion(data->addr, data->size); @@ -47,13 +49,18 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p } else { UNREACHABLE(); } + state.signaled_fence = next.fence; + state.TrySynchronize(); } } } -ThreadManager::ThreadManager(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher) - : renderer{renderer}, dma_pusher{dma_pusher}, thread{RunThread, std::ref(renderer), - std::ref(dma_pusher), std::ref(state)} {} +ThreadManager::ThreadManager(Core::System& system, VideoCore::RendererBase& renderer, + Tegra::DmaPusher& dma_pusher) + : system{system}, thread{RunThread, std::ref(renderer), std::ref(dma_pusher), std::ref(state)} { + synchronization_event = system.CoreTiming().RegisterEvent( + "GPUThreadSynch", [this](u64 fence, s64) { state.WaitForSynchronization(fence); }); +} ThreadManager::~ThreadManager() { // Notify GPU thread that a shutdown is pending @@ -62,14 +69,14 @@ ThreadManager::~ThreadManager() { } void ThreadManager::SubmitList(Tegra::CommandList&& entries) { - PushCommand(SubmitListCommand(std::move(entries))); + const u64 fence{PushCommand(SubmitListCommand(std::move(entries)))}; + const s64 synchronization_ticks{Core::Timing::usToCycles(9000)}; + system.CoreTiming().ScheduleEvent(synchronization_ticks, synchronization_event, fence); } void ThreadManager::SwapBuffers( std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) { - state.IncrementFramesCounter(); PushCommand(SwapBuffersCommand(std::move(framebuffer))); - state.WaitForFrames(); } void ThreadManager::FlushRegion(CacheAddr addr, u64 size) { @@ -79,7 +86,7 @@ void ThreadManager::FlushRegion(CacheAddr addr, u64 size) { void ThreadManager::InvalidateRegion(CacheAddr addr, u64 size) { if (state.queue.Empty()) { // It's quicker to invalidate a single region on the CPU if the queue is already empty - renderer.Rasterizer().InvalidateRegion(addr, size); + system.Renderer().Rasterizer().InvalidateRegion(addr, size); } else { PushCommand(InvalidateRegionCommand(addr, size)); } @@ -90,9 +97,25 @@ void ThreadManager::FlushAndInvalidateRegion(CacheAddr addr, u64 size) { InvalidateRegion(addr, size); } -void ThreadManager::PushCommand(CommandData&& command_data) { - state.queue.Push(CommandDataContainer(std::move(command_data))); +u64 ThreadManager::PushCommand(CommandData&& command_data) { + const u64 fence{++state.last_fence}; + state.queue.Push(CommandDataContainer(std::move(command_data), fence)); state.SignalCommands(); + return fence; +} + +MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192)); +void SynchState::WaitForSynchronization(u64 fence) { + if (signaled_fence >= fence) { + return; + } + + // Wait for the GPU to be idle (all commands to be executed) + { + MICROPROFILE_SCOPE(GPU_wait); + std::unique_lock<std::mutex> lock{synchronization_mutex}; + synchronization_condition.wait(lock, [this, fence] { return signaled_fence >= fence; }); + } } } // namespace VideoCommon::GPUThread diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h index 8cd7db1c6..62bcea5bb 100644 --- a/src/video_core/gpu_thread.h +++ b/src/video_core/gpu_thread.h @@ -4,10 +4,8 @@ #pragma once -#include <array> #include <atomic> #include <condition_variable> -#include <memory> #include <mutex> #include <optional> #include <thread> @@ -21,9 +19,12 @@ struct FramebufferConfig; class DmaPusher; } // namespace Tegra -namespace VideoCore { -class RendererBase; -} // namespace VideoCore +namespace Core { +class System; +namespace Timing { +struct EventType; +} // namespace Timing +} // namespace Core namespace VideoCommon::GPUThread { @@ -77,81 +78,68 @@ using CommandData = struct CommandDataContainer { CommandDataContainer() = default; - CommandDataContainer(CommandData&& data) : data{std::move(data)} {} + CommandDataContainer(CommandData&& data, u64 next_fence) + : data{std::move(data)}, fence{next_fence} {} CommandDataContainer& operator=(const CommandDataContainer& t) { data = std::move(t.data); + fence = t.fence; return *this; } CommandData data; + u64 fence{}; }; /// Struct used to synchronize the GPU thread struct SynchState final { std::atomic_bool is_running{true}; std::atomic_int queued_frame_count{}; - std::mutex frames_mutex; + std::mutex synchronization_mutex; std::mutex commands_mutex; std::condition_variable commands_condition; - std::condition_variable frames_condition; + std::condition_variable synchronization_condition; - void IncrementFramesCounter() { - std::lock_guard<std::mutex> lock{frames_mutex}; - ++queued_frame_count; + /// Returns true if the gap in GPU commands is small enough that we can consider the CPU and GPU + /// synchronized. This is entirely empirical. + bool IsSynchronized() const { + constexpr std::size_t max_queue_gap{5}; + return queue.Size() <= max_queue_gap; } - void DecrementFramesCounter() { - { - std::lock_guard<std::mutex> lock{frames_mutex}; - --queued_frame_count; - - if (queued_frame_count) { - return; - } + void TrySynchronize() { + if (IsSynchronized()) { + std::lock_guard<std::mutex> lock{synchronization_mutex}; + synchronization_condition.notify_one(); } - frames_condition.notify_one(); } - void WaitForFrames() { - { - std::lock_guard<std::mutex> lock{frames_mutex}; - if (!queued_frame_count) { - return; - } - } - - // Wait for the GPU to be idle (all commands to be executed) - { - std::unique_lock<std::mutex> lock{frames_mutex}; - frames_condition.wait(lock, [this] { return !queued_frame_count; }); - } - } + void WaitForSynchronization(u64 fence); void SignalCommands() { - { - std::unique_lock<std::mutex> lock{commands_mutex}; - if (queue.Empty()) { - return; - } + if (queue.Empty()) { + return; } commands_condition.notify_one(); } void WaitForCommands() { - std::unique_lock<std::mutex> lock{commands_mutex}; + std::unique_lock lock{commands_mutex}; commands_condition.wait(lock, [this] { return !queue.Empty(); }); } using CommandQueue = Common::SPSCQueue<CommandDataContainer>; CommandQueue queue; + u64 last_fence{}; + std::atomic<u64> signaled_fence{}; }; /// Class used to manage the GPU thread class ThreadManager final { public: - explicit ThreadManager(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher); + explicit ThreadManager(Core::System& system, VideoCore::RendererBase& renderer, + Tegra::DmaPusher& dma_pusher); ~ThreadManager(); /// Push GPU command entries to be processed @@ -172,12 +160,12 @@ public: private: /// Pushes a command to be executed by the GPU thread - void PushCommand(CommandData&& command_data); + u64 PushCommand(CommandData&& command_data); private: SynchState state; - VideoCore::RendererBase& renderer; - Tegra::DmaPusher& dma_pusher; + Core::System& system; + Core::Timing::EventType* synchronization_event{}; std::thread thread; std::thread::id thread_id; }; diff --git a/src/video_core/macro_interpreter.cpp b/src/video_core/macro_interpreter.cpp index 64f75db43..524d9ea5a 100644 --- a/src/video_core/macro_interpreter.cpp +++ b/src/video_core/macro_interpreter.cpp @@ -223,27 +223,21 @@ void MacroInterpreter::ProcessResult(ResultOperation operation, u32 reg, u32 res } u32 MacroInterpreter::FetchParameter() { - ASSERT(next_parameter_index < parameters.size()); - return parameters[next_parameter_index++]; + return parameters.at(next_parameter_index++); } u32 MacroInterpreter::GetRegister(u32 register_id) const { - // Register 0 is supposed to always return 0. - if (register_id == 0) - return 0; - - ASSERT(register_id < registers.size()); - return registers[register_id]; + return registers.at(register_id); } void MacroInterpreter::SetRegister(u32 register_id, u32 value) { - // Register 0 is supposed to always return 0. NOP is implemented as a store to the zero - // register. - if (register_id == 0) + // Register 0 is hardwired as the zero register. + // Ensure no writes to it actually occur. + if (register_id == 0) { return; + } - ASSERT(register_id < registers.size()); - registers[register_id] = value; + registers.at(register_id) = value; } void MacroInterpreter::SetMethodAddress(u32 address) { diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index e76b59842..8417324ff 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -77,16 +77,17 @@ GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) { return gpu_addr; } -GPUVAddr MemoryManager::FindFreeRegion(GPUVAddr region_start, u64 size) { +GPUVAddr MemoryManager::FindFreeRegion(GPUVAddr region_start, u64 size) const { // Find the first Free VMA. - const VMAHandle vma_handle{std::find_if(vma_map.begin(), vma_map.end(), [&](const auto& vma) { - if (vma.second.type != VirtualMemoryArea::Type::Unmapped) { - return false; - } + const VMAHandle vma_handle{ + std::find_if(vma_map.begin(), vma_map.end(), [region_start, size](const auto& vma) { + if (vma.second.type != VirtualMemoryArea::Type::Unmapped) { + return false; + } - const VAddr vma_end{vma.second.base + vma.second.size}; - return vma_end > region_start && vma_end >= region_start + size; - })}; + const VAddr vma_end{vma.second.base + vma.second.size}; + return vma_end > region_start && vma_end >= region_start + size; + })}; if (vma_handle == vma_map.end()) { return {}; @@ -99,12 +100,12 @@ bool MemoryManager::IsAddressValid(GPUVAddr addr) const { return (addr >> page_bits) < page_table.pointers.size(); } -std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr addr) { +std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr addr) const { if (!IsAddressValid(addr)) { return {}; } - VAddr cpu_addr{page_table.backing_addr[addr >> page_bits]}; + const VAddr cpu_addr{page_table.backing_addr[addr >> page_bits]}; if (cpu_addr) { return cpu_addr + (addr & page_mask); } @@ -113,7 +114,7 @@ std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr addr) { } template <typename T> -T MemoryManager::Read(GPUVAddr addr) { +T MemoryManager::Read(GPUVAddr addr) const { if (!IsAddressValid(addr)) { return {}; } @@ -165,10 +166,10 @@ void MemoryManager::Write(GPUVAddr addr, T data) { } } -template u8 MemoryManager::Read<u8>(GPUVAddr addr); -template u16 MemoryManager::Read<u16>(GPUVAddr addr); -template u32 MemoryManager::Read<u32>(GPUVAddr addr); -template u64 MemoryManager::Read<u64>(GPUVAddr addr); +template u8 MemoryManager::Read<u8>(GPUVAddr addr) const; +template u16 MemoryManager::Read<u16>(GPUVAddr addr) const; +template u32 MemoryManager::Read<u32>(GPUVAddr addr) const; +template u64 MemoryManager::Read<u64>(GPUVAddr addr) const; template void MemoryManager::Write<u8>(GPUVAddr addr, u8 data); template void MemoryManager::Write<u16>(GPUVAddr addr, u16 data); template void MemoryManager::Write<u32>(GPUVAddr addr, u32 data); @@ -179,8 +180,22 @@ u8* MemoryManager::GetPointer(GPUVAddr addr) { return {}; } - u8* page_pointer{page_table.pointers[addr >> page_bits]}; - if (page_pointer) { + u8* const page_pointer{page_table.pointers[addr >> page_bits]}; + if (page_pointer != nullptr) { + return page_pointer + (addr & page_mask); + } + + LOG_ERROR(HW_GPU, "Unknown GetPointer @ 0x{:016X}", addr); + return {}; +} + +const u8* MemoryManager::GetPointer(GPUVAddr addr) const { + if (!IsAddressValid(addr)) { + return {}; + } + + const u8* const page_pointer{page_table.pointers[addr >> page_bits]}; + if (page_pointer != nullptr) { return page_pointer + (addr & page_mask); } @@ -188,7 +203,7 @@ u8* MemoryManager::GetPointer(GPUVAddr addr) { return {}; } -void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) { +void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) const { std::memcpy(dest_buffer, GetPointer(src_addr), size); } void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size) { diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index 34744bb27..178e2f655 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -50,17 +50,18 @@ public: GPUVAddr MapBufferEx(VAddr cpu_addr, u64 size); GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr addr, u64 size); GPUVAddr UnmapBuffer(GPUVAddr addr, u64 size); - std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr); + std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr) const; template <typename T> - T Read(GPUVAddr addr); + T Read(GPUVAddr addr) const; template <typename T> void Write(GPUVAddr addr, T data); u8* GetPointer(GPUVAddr addr); + const u8* GetPointer(GPUVAddr addr) const; - void ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size); + void ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) const; void WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size); void CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size); @@ -127,7 +128,7 @@ private: void UpdatePageTableForVMA(const VirtualMemoryArea& vma); /// Finds a free (unmapped region) of the specified size starting at the specified address. - GPUVAddr FindFreeRegion(GPUVAddr region_start, u64 size); + GPUVAddr FindFreeRegion(GPUVAddr region_start, u64 size) const; private: static constexpr u64 page_bits{16}; diff --git a/src/video_core/rasterizer_cache.h b/src/video_core/rasterizer_cache.h index 9fc9f3056..291772186 100644 --- a/src/video_core/rasterizer_cache.h +++ b/src/video_core/rasterizer_cache.h @@ -71,8 +71,8 @@ private: bool is_registered{}; ///< Whether the object is currently registered with the cache bool is_dirty{}; ///< Whether the object is dirty (out of sync with guest memory) u64 last_modified_ticks{}; ///< When the object was last modified, used for in-order flushing - CacheAddr cache_addr{}; ///< Cache address memory, unique from emulated virtual address space const u8* host_ptr{}; ///< Pointer to the memory backing this cached region + CacheAddr cache_addr{}; ///< Cache address memory, unique from emulated virtual address space }; template <class T> @@ -84,7 +84,7 @@ public: /// Write any cached resources overlapping the specified region back to memory void FlushRegion(CacheAddr addr, std::size_t size) { - std::lock_guard<std::recursive_mutex> lock{mutex}; + std::lock_guard lock{mutex}; const auto& objects{GetSortedObjectsFromRegion(addr, size)}; for (auto& object : objects) { @@ -94,7 +94,7 @@ public: /// Mark the specified region as being invalidated void InvalidateRegion(CacheAddr addr, u64 size) { - std::lock_guard<std::recursive_mutex> lock{mutex}; + std::lock_guard lock{mutex}; const auto& objects{GetSortedObjectsFromRegion(addr, size)}; for (auto& object : objects) { @@ -108,7 +108,7 @@ public: /// Invalidates everything in the cache void InvalidateAll() { - std::lock_guard<std::recursive_mutex> lock{mutex}; + std::lock_guard lock{mutex}; while (interval_cache.begin() != interval_cache.end()) { Unregister(*interval_cache.begin()->second.begin()); @@ -133,7 +133,7 @@ protected: /// Register an object into the cache virtual void Register(const T& object) { - std::lock_guard<std::recursive_mutex> lock{mutex}; + std::lock_guard lock{mutex}; object->SetIsRegistered(true); interval_cache.add({GetInterval(object), ObjectSet{object}}); @@ -143,7 +143,7 @@ protected: /// Unregisters an object from the cache virtual void Unregister(const T& object) { - std::lock_guard<std::recursive_mutex> lock{mutex}; + std::lock_guard lock{mutex}; object->SetIsRegistered(false); rasterizer.UpdatePagesCachedCount(object->GetCpuAddr(), object->GetSizeInBytes(), -1); @@ -153,14 +153,14 @@ protected: /// Returns a ticks counter used for tracking when cached objects were last modified u64 GetModifiedTicks() { - std::lock_guard<std::recursive_mutex> lock{mutex}; + std::lock_guard lock{mutex}; return ++modified_ticks; } /// Flushes the specified object, updating appropriate cache state as needed void FlushObject(const T& object) { - std::lock_guard<std::recursive_mutex> lock{mutex}; + std::lock_guard lock{mutex}; if (!object->IsDirty()) { return; diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index f75c65825..7989ec11b 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp @@ -7,7 +7,6 @@ #include "common/alignment.h" #include "core/core.h" -#include "core/memory.h" #include "video_core/renderer_opengl/gl_buffer_cache.h" #include "video_core/renderer_opengl/gl_rasterizer.h" @@ -15,8 +14,8 @@ namespace OpenGL { CachedBufferEntry::CachedBufferEntry(VAddr cpu_addr, std::size_t size, GLintptr offset, std::size_t alignment, u8* host_ptr) - : cpu_addr{cpu_addr}, size{size}, offset{offset}, alignment{alignment}, RasterizerCacheObject{ - host_ptr} {} + : RasterizerCacheObject{host_ptr}, cpu_addr{cpu_addr}, size{size}, offset{offset}, + alignment{alignment} {} OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, std::size_t size) : RasterizerCache{rasterizer}, stream_buffer(size, true) {} diff --git a/src/video_core/renderer_opengl/gl_global_cache.cpp b/src/video_core/renderer_opengl/gl_global_cache.cpp index 0fbfbad55..5842d6213 100644 --- a/src/video_core/renderer_opengl/gl_global_cache.cpp +++ b/src/video_core/renderer_opengl/gl_global_cache.cpp @@ -4,7 +4,6 @@ #include <glad/glad.h> -#include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" #include "video_core/renderer_opengl/gl_global_cache.h" @@ -15,7 +14,7 @@ namespace OpenGL { CachedGlobalRegion::CachedGlobalRegion(VAddr cpu_addr, u32 size, u8* host_ptr) - : cpu_addr{cpu_addr}, size{size}, RasterizerCacheObject{host_ptr} { + : RasterizerCacheObject{host_ptr}, cpu_addr{cpu_addr}, size{size} { buffer.Create(); // Bind and unbind the buffer so it gets allocated by the driver glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer.handle); diff --git a/src/video_core/renderer_opengl/gl_primitive_assembler.cpp b/src/video_core/renderer_opengl/gl_primitive_assembler.cpp index 2bcbd3da2..c3e94d917 100644 --- a/src/video_core/renderer_opengl/gl_primitive_assembler.cpp +++ b/src/video_core/renderer_opengl/gl_primitive_assembler.cpp @@ -7,7 +7,7 @@ #include "common/assert.h" #include "common/common_types.h" #include "core/core.h" -#include "core/memory.h" +#include "video_core/memory_manager.h" #include "video_core/renderer_opengl/gl_buffer_cache.h" #include "video_core/renderer_opengl/gl_primitive_assembler.h" diff --git a/src/video_core/renderer_opengl/gl_primitive_assembler.h b/src/video_core/renderer_opengl/gl_primitive_assembler.h index 0e2e7dc36..4e87ce4d6 100644 --- a/src/video_core/renderer_opengl/gl_primitive_assembler.h +++ b/src/video_core/renderer_opengl/gl_primitive_assembler.h @@ -4,11 +4,9 @@ #pragma once -#include <vector> #include <glad/glad.h> #include "common/common_types.h" -#include "video_core/memory_manager.h" namespace OpenGL { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index e06dfe43f..7ff1e6737 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -17,7 +17,6 @@ #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/settings.h" #include "video_core/engines/maxwell_3d.h" @@ -26,7 +25,6 @@ #include "video_core/renderer_opengl/gl_shader_gen.h" #include "video_core/renderer_opengl/maxwell_to_gl.h" #include "video_core/renderer_opengl/renderer_opengl.h" -#include "video_core/video_core.h" namespace OpenGL { @@ -100,11 +98,9 @@ struct FramebufferCacheKey { } }; -RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, Core::System& system, - ScreenInfo& info) - : res_cache{*this}, shader_cache{*this, system}, global_cache{*this}, - emu_window{window}, system{system}, screen_info{info}, - buffer_cache(*this, STREAM_BUFFER_SIZE) { +RasterizerOpenGL::RasterizerOpenGL(Core::System& system, ScreenInfo& info) + : res_cache{*this}, shader_cache{*this, system}, global_cache{*this}, system{system}, + screen_info{info}, buffer_cache(*this, STREAM_BUFFER_SIZE) { // Create sampler objects for (std::size_t i = 0; i < texture_samplers.size(); ++i) { texture_samplers[i].Create(); @@ -320,7 +316,7 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { const std::size_t stage{index == 0 ? 0 : index - 1}; // Stage indices are 0 - 5 GLShader::MaxwellUniformData ubo{}; - ubo.SetFromRegs(gpu.state.shader_stages[stage]); + ubo.SetFromRegs(gpu, stage); const GLintptr offset = buffer_cache.UploadHostMemory( &ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment)); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 30f3e8acb..54fbf48aa 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -12,15 +12,12 @@ #include <optional> #include <tuple> #include <utility> -#include <vector> #include <boost/icl/interval_map.hpp> -#include <boost/range/iterator_range.hpp> #include <glad/glad.h> #include "common/common_types.h" #include "video_core/engines/maxwell_3d.h" -#include "video_core/memory_manager.h" #include "video_core/rasterizer_cache.h" #include "video_core/rasterizer_interface.h" #include "video_core/renderer_opengl/gl_buffer_cache.h" @@ -29,10 +26,8 @@ #include "video_core/renderer_opengl/gl_rasterizer_cache.h" #include "video_core/renderer_opengl/gl_resource_manager.h" #include "video_core/renderer_opengl/gl_shader_cache.h" -#include "video_core/renderer_opengl/gl_shader_gen.h" #include "video_core/renderer_opengl/gl_shader_manager.h" #include "video_core/renderer_opengl/gl_state.h" -#include "video_core/renderer_opengl/gl_stream_buffer.h" namespace Core { class System; @@ -50,8 +45,7 @@ struct FramebufferCacheKey; class RasterizerOpenGL : public VideoCore::RasterizerInterface { public: - explicit RasterizerOpenGL(Core::Frontend::EmuWindow& window, Core::System& system, - ScreenInfo& info); + explicit RasterizerOpenGL(Core::System& system, ScreenInfo& info); ~RasterizerOpenGL() override; void DrawArrays() override; @@ -214,7 +208,6 @@ private: ShaderCacheOpenGL shader_cache; GlobalRegionCacheOpenGL global_cache; - Core::Frontend::EmuWindow& emu_window; Core::System& system; ScreenInfo& screen_info; diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 0235317c0..7a3280620 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -13,7 +13,6 @@ #include "common/scope_exit.h" #include "core/core.h" #include "core/hle/kernel/process.h" -#include "core/memory.h" #include "core/settings.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/morton.h" @@ -562,8 +561,8 @@ void RasterizerCacheOpenGL::CopySurface(const Surface& src_surface, const Surfac } CachedSurface::CachedSurface(const SurfaceParams& params) - : params{params}, gl_target{SurfaceTargetToGL(params.target)}, - cached_size_in_bytes{params.size_in_bytes}, RasterizerCacheObject{params.host_ptr} { + : RasterizerCacheObject{params.host_ptr}, params{params}, + gl_target{SurfaceTargetToGL(params.target)}, cached_size_in_bytes{params.size_in_bytes} { const auto optional_cpu_addr{ Core::System::GetInstance().GPU().MemoryManager().GpuToCpuAddress(params.gpu_addr)}; diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index e8073579f..ad4fd3ad2 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -5,10 +5,9 @@ #pragma once #include <array> -#include <map> #include <memory> #include <string> -#include <unordered_set> +#include <tuple> #include <vector> #include "common/alignment.h" diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 1f8eca6f0..7030db365 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -6,13 +6,11 @@ #include "common/assert.h" #include "common/hash.h" #include "core/core.h" -#include "core/memory.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/renderer_opengl/gl_rasterizer.h" #include "video_core/renderer_opengl/gl_shader_cache.h" #include "video_core/renderer_opengl/gl_shader_decompiler.h" #include "video_core/renderer_opengl/gl_shader_disk_cache.h" -#include "video_core/renderer_opengl/gl_shader_manager.h" #include "video_core/renderer_opengl/utils.h" #include "video_core/shader/shader_ir.h" @@ -215,9 +213,9 @@ CachedShader::CachedShader(VAddr cpu_addr, u64 unique_identifier, Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache, const PrecompiledPrograms& precompiled_programs, ProgramCode&& program_code, ProgramCode&& program_code_b, u8* host_ptr) - : host_ptr{host_ptr}, cpu_addr{cpu_addr}, unique_identifier{unique_identifier}, - program_type{program_type}, disk_cache{disk_cache}, - precompiled_programs{precompiled_programs}, RasterizerCacheObject{host_ptr} { + : RasterizerCacheObject{host_ptr}, host_ptr{host_ptr}, cpu_addr{cpu_addr}, + unique_identifier{unique_identifier}, program_type{program_type}, disk_cache{disk_cache}, + precompiled_programs{precompiled_programs} { const std::size_t code_size = CalculateProgramSize(program_code); const std::size_t code_size_b = @@ -245,9 +243,9 @@ CachedShader::CachedShader(VAddr cpu_addr, u64 unique_identifier, Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache, const PrecompiledPrograms& precompiled_programs, GLShader::ProgramResult result, u8* host_ptr) - : cpu_addr{cpu_addr}, unique_identifier{unique_identifier}, program_type{program_type}, - disk_cache{disk_cache}, precompiled_programs{precompiled_programs}, RasterizerCacheObject{ - host_ptr} { + : RasterizerCacheObject{host_ptr}, cpu_addr{cpu_addr}, unique_identifier{unique_identifier}, + program_type{program_type}, disk_cache{disk_cache}, precompiled_programs{ + precompiled_programs} { code = std::move(result.first); entries = result.second; diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h index fd1c85115..0cf8e0b3d 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_cache.h @@ -5,21 +5,20 @@ #pragma once #include <array> +#include <atomic> #include <memory> #include <set> #include <tuple> #include <unordered_map> +#include <vector> #include <glad/glad.h> -#include "common/assert.h" #include "common/common_types.h" #include "video_core/rasterizer_cache.h" -#include "video_core/renderer_base.h" #include "video_core/renderer_opengl/gl_resource_manager.h" #include "video_core/renderer_opengl/gl_shader_decompiler.h" #include "video_core/renderer_opengl/gl_shader_disk_cache.h" -#include "video_core/renderer_opengl/gl_shader_gen.h" namespace Core { class System; diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 11d1169f0..a1a51f226 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -69,10 +69,10 @@ public: shader_source += '\n'; } - std::string GenerateTemporal() { - std::string temporal = "tmp"; - temporal += std::to_string(temporal_index++); - return temporal; + std::string GenerateTemporary() { + std::string temporary = "tmp"; + temporary += std::to_string(temporary_index++); + return temporary; } std::string GetResult() { @@ -87,7 +87,7 @@ private: } std::string shader_source; - u32 temporal_index = 1; + u32 temporary_index = 1; }; /// Generates code to use for a swizzle operation. @@ -426,9 +426,14 @@ private: std::string Visit(Node node) { if (const auto operation = std::get_if<OperationNode>(node)) { const auto operation_index = static_cast<std::size_t>(operation->GetCode()); + if (operation_index >= operation_decompilers.size()) { + UNREACHABLE_MSG("Out of bounds operation: {}", operation_index); + return {}; + } const auto decompiler = operation_decompilers[operation_index]; if (decompiler == nullptr) { - UNREACHABLE_MSG("Operation decompiler {} not defined", operation_index); + UNREACHABLE_MSG("Undefined operation: {}", operation_index); + return {}; } return (this->*decompiler)(*operation); @@ -540,7 +545,7 @@ private: } else if (std::holds_alternative<OperationNode>(*offset)) { // Indirect access - const std::string final_offset = code.GenerateTemporal(); + const std::string final_offset = code.GenerateTemporary(); code.AddLine("uint " + final_offset + " = (ftou(" + Visit(offset) + ") / 4) & " + std::to_string(MAX_CONSTBUFFER_ELEMENTS - 1) + ';'); return fmt::format("{}[{} / 4][{} % 4]", GetConstBuffer(cbuf->GetIndex()), @@ -587,9 +592,9 @@ private: // There's a bug in NVidia's proprietary drivers that makes precise fail on fragment shaders const std::string precise = stage != ShaderStage::Fragment ? "precise " : ""; - const std::string temporal = code.GenerateTemporal(); - code.AddLine(precise + "float " + temporal + " = " + value + ';'); - return temporal; + const std::string temporary = code.GenerateTemporary(); + code.AddLine(precise + "float " + temporary + " = " + value + ';'); + return temporary; } std::string VisitOperand(Operation operation, std::size_t operand_index) { @@ -601,9 +606,9 @@ private: return Visit(operand); } - const std::string temporal = code.GenerateTemporal(); - code.AddLine("float " + temporal + " = " + Visit(operand) + ';'); - return temporal; + const std::string temporary = code.GenerateTemporary(); + code.AddLine("float " + temporary + " = " + Visit(operand) + ';'); + return temporary; } std::string VisitOperand(Operation operation, std::size_t operand_index, Type type) { @@ -1196,11 +1201,12 @@ private: switch (meta->element) { case 0: case 1: - return "textureSize(" + sampler + ", " + lod + ')' + GetSwizzle(meta->element); + return "itof(int(textureSize(" + sampler + ", " + lod + ')' + + GetSwizzle(meta->element) + "))"; case 2: return "0"; case 3: - return "textureQueryLevels(" + sampler + ')'; + return "itof(textureQueryLevels(" + sampler + "))"; } UNREACHABLE(); return "0"; diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.h b/src/video_core/renderer_opengl/gl_shader_decompiler.h index 72aca4938..4e04ab2f8 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.h +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.h @@ -5,7 +5,6 @@ #pragma once #include <array> -#include <set> #include <string> #include <utility> #include <vector> diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp index 82fc4d44b..d2d979997 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp @@ -4,13 +4,13 @@ #include <cstring> #include <fmt/format.h> -#include <lz4.h> #include "common/assert.h" #include "common/common_paths.h" #include "common/common_types.h" #include "common/file_util.h" #include "common/logging/log.h" +#include "common/lz4_compression.h" #include "common/scm_rev.h" #include "core/core.h" @@ -49,39 +49,6 @@ ShaderCacheVersionHash GetShaderCacheVersionHash() { return hash; } -template <typename T> -std::vector<u8> CompressData(const T* source, std::size_t source_size) { - if (source_size > LZ4_MAX_INPUT_SIZE) { - // Source size exceeds LZ4 maximum input size - return {}; - } - const auto source_size_int = static_cast<int>(source_size); - const int max_compressed_size = LZ4_compressBound(source_size_int); - std::vector<u8> compressed(max_compressed_size); - const int compressed_size = LZ4_compress_default(reinterpret_cast<const char*>(source), - reinterpret_cast<char*>(compressed.data()), - source_size_int, max_compressed_size); - if (compressed_size <= 0) { - // Compression failed - return {}; - } - compressed.resize(compressed_size); - return compressed; -} - -std::vector<u8> DecompressData(const std::vector<u8>& compressed, std::size_t uncompressed_size) { - std::vector<u8> uncompressed(uncompressed_size); - const int size_check = LZ4_decompress_safe(reinterpret_cast<const char*>(compressed.data()), - reinterpret_cast<char*>(uncompressed.data()), - static_cast<int>(compressed.size()), - static_cast<int>(uncompressed.size())); - if (static_cast<int>(uncompressed_size) != size_check) { - // Decompression failed - return {}; - } - return uncompressed; -} - } // namespace ShaderDiskCacheRaw::ShaderDiskCacheRaw(u64 unique_identifier, Maxwell::ShaderProgram program_type, @@ -292,7 +259,7 @@ ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) { return {}; } - dump.binary = DecompressData(compressed_binary, binary_length); + dump.binary = Common::Compression::DecompressDataLZ4(compressed_binary, binary_length); if (dump.binary.empty()) { return {}; } @@ -321,7 +288,7 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn return {}; } - const std::vector<u8> code = DecompressData(compressed_code, code_size); + const std::vector<u8> code = Common::Compression::DecompressDataLZ4(compressed_code, code_size); if (code.empty()) { return {}; } @@ -507,7 +474,8 @@ void ShaderDiskCacheOpenGL::SaveDecompiled(u64 unique_identifier, const std::str if (!IsUsable()) return; - const std::vector<u8> compressed_code{CompressData(code.data(), code.size())}; + const std::vector<u8> compressed_code{Common::Compression::CompressDataLZ4HC( + reinterpret_cast<const u8*>(code.data()), code.size(), 9)}; if (compressed_code.empty()) { LOG_ERROR(Render_OpenGL, "Failed to compress GLSL code - skipping shader {:016x}", unique_identifier); @@ -537,7 +505,9 @@ void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint p std::vector<u8> binary(binary_length); glGetProgramBinary(program, binary_length, nullptr, &binary_format, binary.data()); - const std::vector<u8> compressed_binary = CompressData(binary.data(), binary.size()); + const std::vector<u8> compressed_binary = + Common::Compression::CompressDataLZ4HC(binary.data(), binary.size(), 9); + if (compressed_binary.empty()) { LOG_ERROR(Render_OpenGL, "Failed to compress binary program in shader={:016x}", usage.unique_identifier); diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 7d96649af..8763d9c71 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -3,7 +3,6 @@ // Refer to the license.txt file included. #include <fmt/format.h> -#include "common/assert.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/renderer_opengl/gl_shader_decompiler.h" #include "video_core/renderer_opengl/gl_shader_gen.h" diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h index fba8e681b..fad346b48 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.h +++ b/src/video_core/renderer_opengl/gl_shader_gen.h @@ -4,12 +4,9 @@ #pragma once -#include <array> -#include <string> #include <vector> #include "common/common_types.h" -#include "video_core/engines/shader_bytecode.h" #include "video_core/renderer_opengl/gl_shader_decompiler.h" #include "video_core/shader/shader_ir.h" diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp index 6a30c28d2..eaf3e03a0 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.cpp +++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp @@ -2,15 +2,15 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/core.h" #include "video_core/renderer_opengl/gl_shader_manager.h" namespace OpenGL::GLShader { -void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage) { - const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); - const auto& regs = gpu.regs; - const auto& state = gpu.state; +using Tegra::Engines::Maxwell3D; + +void MaxwellUniformData::SetFromRegs(const Maxwell3D& maxwell, std::size_t shader_stage) { + const auto& regs = maxwell.regs; + const auto& state = maxwell.state; // TODO(bunnei): Support more than one viewport viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f; @@ -18,7 +18,7 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& sh u32 func = static_cast<u32>(regs.alpha_test_func); // Normalize the gl variants of opCompare to be the same as the normal variants - u32 op_gl_variant_base = static_cast<u32>(Tegra::Engines::Maxwell3D::Regs::ComparisonOp::Never); + const u32 op_gl_variant_base = static_cast<u32>(Maxwell3D::Regs::ComparisonOp::Never); if (func >= op_gl_variant_base) { func = func - op_gl_variant_base + 1U; } @@ -31,8 +31,9 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& sh // Assign in which stage the position has to be flipped // (the last stage before the fragment shader). - if (gpu.regs.shader_config[static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry)].enable) { - flip_stage = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry); + constexpr u32 geometry_index = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry); + if (maxwell.regs.shader_config[geometry_index].enable) { + flip_stage = geometry_index; } else { flip_stage = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::VertexB); } diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h index 4970aafed..8eef2a920 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.h +++ b/src/video_core/renderer_opengl/gl_shader_manager.h @@ -12,14 +12,13 @@ namespace OpenGL::GLShader { -using Tegra::Engines::Maxwell3D; - /// Uniform structure for the Uniform Buffer Object, all vectors must be 16-byte aligned -// NOTE: Always keep a vec4 at the end. The GL spec is not clear whether the alignment at -// the end of a uniform block is included in UNIFORM_BLOCK_DATA_SIZE or not. -// Not following that rule will cause problems on some AMD drivers. +/// @note Always keep a vec4 at the end. The GL spec is not clear whether the alignment at +/// the end of a uniform block is included in UNIFORM_BLOCK_DATA_SIZE or not. +/// Not following that rule will cause problems on some AMD drivers. struct MaxwellUniformData { - void SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage); + void SetFromRegs(const Tegra::Engines::Maxwell3D& maxwell, std::size_t shader_stage); + alignas(16) GLvec4 viewport_flip; struct alignas(16) { GLuint instance_id; diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 5e3d862c6..d69cba9c3 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -5,7 +5,6 @@ #include <algorithm> #include <cstddef> #include <cstdlib> -#include <cstring> #include <memory> #include <glad/glad.h> #include "common/assert.h" @@ -266,7 +265,7 @@ void RendererOpenGL::CreateRasterizer() { } // Initialize sRGB Usage OpenGLState::ClearsRGBUsed(); - rasterizer = std::make_unique<RasterizerOpenGL>(render_window, system, screen_info); + rasterizer = std::make_unique<RasterizerOpenGL>(system, screen_info); } void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index eac51ecb3..02a9f5ecb 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -10,6 +10,7 @@ #include "common/alignment.h" #include "common/assert.h" #include "core/memory.h" +#include "video_core/memory_manager.h" #include "video_core/renderer_vulkan/declarations.h" #include "video_core/renderer_vulkan/vk_buffer_cache.h" #include "video_core/renderer_vulkan/vk_scheduler.h" @@ -19,8 +20,8 @@ namespace Vulkan { CachedBufferEntry::CachedBufferEntry(VAddr cpu_addr, std::size_t size, u64 offset, std::size_t alignment, u8* host_ptr) - : cpu_addr{cpu_addr}, size{size}, offset{offset}, alignment{alignment}, RasterizerCacheObject{ - host_ptr} {} + : RasterizerCacheObject{host_ptr}, cpu_addr{cpu_addr}, size{size}, offset{offset}, + alignment{alignment} {} VKBufferCache::VKBufferCache(Tegra::MemoryManager& tegra_memory_manager, VideoCore::RasterizerInterface& rasterizer, const VKDevice& device, diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp new file mode 100644 index 000000000..08279e562 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -0,0 +1,210 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <array> +#include <limits> +#include <vector> + +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/core.h" +#include "core/frontend/framebuffer_layout.h" +#include "video_core/renderer_vulkan/declarations.h" +#include "video_core/renderer_vulkan/vk_device.h" +#include "video_core/renderer_vulkan/vk_resource_manager.h" +#include "video_core/renderer_vulkan/vk_swapchain.h" + +namespace Vulkan { + +namespace { +vk::SurfaceFormatKHR ChooseSwapSurfaceFormat(const std::vector<vk::SurfaceFormatKHR>& formats) { + if (formats.size() == 1 && formats[0].format == vk::Format::eUndefined) { + return {vk::Format::eB8G8R8A8Unorm, vk::ColorSpaceKHR::eSrgbNonlinear}; + } + const auto& found = std::find_if(formats.begin(), formats.end(), [](const auto& format) { + return format.format == vk::Format::eB8G8R8A8Unorm && + format.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear; + }); + return found != formats.end() ? *found : formats[0]; +} + +vk::PresentModeKHR ChooseSwapPresentMode(const std::vector<vk::PresentModeKHR>& modes) { + // Mailbox doesn't lock the application like fifo (vsync), prefer it + const auto& found = std::find_if(modes.begin(), modes.end(), [](const auto& mode) { + return mode == vk::PresentModeKHR::eMailbox; + }); + return found != modes.end() ? *found : vk::PresentModeKHR::eFifo; +} + +vk::Extent2D ChooseSwapExtent(const vk::SurfaceCapabilitiesKHR& capabilities, u32 width, + u32 height) { + constexpr auto undefined_size{std::numeric_limits<u32>::max()}; + if (capabilities.currentExtent.width != undefined_size) { + return capabilities.currentExtent; + } + vk::Extent2D extent = {width, height}; + extent.width = std::max(capabilities.minImageExtent.width, + std::min(capabilities.maxImageExtent.width, extent.width)); + extent.height = std::max(capabilities.minImageExtent.height, + std::min(capabilities.maxImageExtent.height, extent.height)); + return extent; +} +} // namespace + +VKSwapchain::VKSwapchain(vk::SurfaceKHR surface, const VKDevice& device) + : surface{surface}, device{device} {} + +VKSwapchain::~VKSwapchain() = default; + +void VKSwapchain::Create(u32 width, u32 height) { + const auto dev = device.GetLogical(); + const auto& dld = device.GetDispatchLoader(); + const auto physical_device = device.GetPhysical(); + + const vk::SurfaceCapabilitiesKHR capabilities{ + physical_device.getSurfaceCapabilitiesKHR(surface, dld)}; + if (capabilities.maxImageExtent.width == 0 || capabilities.maxImageExtent.height == 0) { + return; + } + + dev.waitIdle(dld); + Destroy(); + + CreateSwapchain(capabilities, width, height); + CreateSemaphores(); + CreateImageViews(); + + fences.resize(image_count, nullptr); +} + +void VKSwapchain::AcquireNextImage() { + const auto dev{device.GetLogical()}; + const auto& dld{device.GetDispatchLoader()}; + dev.acquireNextImageKHR(*swapchain, std::numeric_limits<u64>::max(), + *present_semaphores[frame_index], {}, &image_index, dld); + + if (auto& fence = fences[image_index]; fence) { + fence->Wait(); + fence->Release(); + fence = nullptr; + } +} + +bool VKSwapchain::Present(vk::Semaphore render_semaphore, VKFence& fence) { + const vk::Semaphore present_semaphore{*present_semaphores[frame_index]}; + const std::array<vk::Semaphore, 2> semaphores{present_semaphore, render_semaphore}; + const u32 wait_semaphore_count{render_semaphore ? 2U : 1U}; + const auto& dld{device.GetDispatchLoader()}; + const auto present_queue{device.GetPresentQueue()}; + bool recreated = false; + + const vk::PresentInfoKHR present_info(wait_semaphore_count, semaphores.data(), 1, + &swapchain.get(), &image_index, {}); + switch (const auto result = present_queue.presentKHR(&present_info, dld); result) { + case vk::Result::eSuccess: + break; + case vk::Result::eErrorOutOfDateKHR: + if (current_width > 0 && current_height > 0) { + Create(current_width, current_height); + recreated = true; + } + break; + default: + LOG_CRITICAL(Render_Vulkan, "Vulkan failed to present swapchain due to {}!", + vk::to_string(result)); + UNREACHABLE(); + } + + ASSERT(fences[image_index] == nullptr); + fences[image_index] = &fence; + frame_index = (frame_index + 1) % image_count; + return recreated; +} + +bool VKSwapchain::HasFramebufferChanged(const Layout::FramebufferLayout& framebuffer) const { + // TODO(Rodrigo): Handle framebuffer pixel format changes + return framebuffer.width != current_width || framebuffer.height != current_height; +} + +void VKSwapchain::CreateSwapchain(const vk::SurfaceCapabilitiesKHR& capabilities, u32 width, + u32 height) { + const auto dev{device.GetLogical()}; + const auto& dld{device.GetDispatchLoader()}; + const auto physical_device{device.GetPhysical()}; + + const std::vector<vk::SurfaceFormatKHR> formats{ + physical_device.getSurfaceFormatsKHR(surface, dld)}; + + const std::vector<vk::PresentModeKHR> present_modes{ + physical_device.getSurfacePresentModesKHR(surface, dld)}; + + const vk::SurfaceFormatKHR surface_format{ChooseSwapSurfaceFormat(formats)}; + const vk::PresentModeKHR present_mode{ChooseSwapPresentMode(present_modes)}; + extent = ChooseSwapExtent(capabilities, width, height); + + current_width = extent.width; + current_height = extent.height; + + u32 requested_image_count{capabilities.minImageCount + 1}; + if (capabilities.maxImageCount > 0 && requested_image_count > capabilities.maxImageCount) { + requested_image_count = capabilities.maxImageCount; + } + + vk::SwapchainCreateInfoKHR swapchain_ci( + {}, surface, requested_image_count, surface_format.format, surface_format.colorSpace, + extent, 1, vk::ImageUsageFlagBits::eColorAttachment, {}, {}, {}, + capabilities.currentTransform, vk::CompositeAlphaFlagBitsKHR::eOpaque, present_mode, false, + {}); + + const u32 graphics_family{device.GetGraphicsFamily()}; + const u32 present_family{device.GetPresentFamily()}; + const std::array<u32, 2> queue_indices{graphics_family, present_family}; + if (graphics_family != present_family) { + swapchain_ci.imageSharingMode = vk::SharingMode::eConcurrent; + swapchain_ci.queueFamilyIndexCount = static_cast<u32>(queue_indices.size()); + swapchain_ci.pQueueFamilyIndices = queue_indices.data(); + } else { + swapchain_ci.imageSharingMode = vk::SharingMode::eExclusive; + } + + swapchain = dev.createSwapchainKHRUnique(swapchain_ci, nullptr, dld); + + images = dev.getSwapchainImagesKHR(*swapchain, dld); + image_count = static_cast<u32>(images.size()); + image_format = surface_format.format; +} + +void VKSwapchain::CreateSemaphores() { + const auto dev{device.GetLogical()}; + const auto& dld{device.GetDispatchLoader()}; + + present_semaphores.resize(image_count); + for (std::size_t i = 0; i < image_count; i++) { + present_semaphores[i] = dev.createSemaphoreUnique({}, nullptr, dld); + } +} + +void VKSwapchain::CreateImageViews() { + const auto dev{device.GetLogical()}; + const auto& dld{device.GetDispatchLoader()}; + + image_views.resize(image_count); + for (std::size_t i = 0; i < image_count; i++) { + const vk::ImageViewCreateInfo image_view_ci({}, images[i], vk::ImageViewType::e2D, + image_format, {}, + {vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}); + image_views[i] = dev.createImageViewUnique(image_view_ci, nullptr, dld); + } +} + +void VKSwapchain::Destroy() { + frame_index = 0; + present_semaphores.clear(); + framebuffers.clear(); + image_views.clear(); + swapchain.reset(); +} + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h new file mode 100644 index 000000000..2ad84f185 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_swapchain.h @@ -0,0 +1,92 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <vector> + +#include "common/common_types.h" +#include "video_core/renderer_vulkan/declarations.h" + +namespace Layout { +struct FramebufferLayout; +} + +namespace Vulkan { + +class VKDevice; +class VKFence; + +class VKSwapchain { +public: + explicit VKSwapchain(vk::SurfaceKHR surface, const VKDevice& device); + ~VKSwapchain(); + + /// Creates (or recreates) the swapchain with a given size. + void Create(u32 width, u32 height); + + /// Acquires the next image in the swapchain, waits as needed. + void AcquireNextImage(); + + /// Presents the rendered image to the swapchain. Returns true when the swapchains had to be + /// recreated. Takes responsability for the ownership of fence. + bool Present(vk::Semaphore render_semaphore, VKFence& fence); + + /// Returns true when the framebuffer layout has changed. + bool HasFramebufferChanged(const Layout::FramebufferLayout& framebuffer) const; + + const vk::Extent2D& GetSize() const { + return extent; + } + + u32 GetImageCount() const { + return image_count; + } + + u32 GetImageIndex() const { + return image_index; + } + + vk::Image GetImageIndex(u32 index) const { + return images[index]; + } + + vk::ImageView GetImageViewIndex(u32 index) const { + return *image_views[index]; + } + + vk::Format GetImageFormat() const { + return image_format; + } + +private: + void CreateSwapchain(const vk::SurfaceCapabilitiesKHR& capabilities, u32 width, u32 height); + void CreateSemaphores(); + void CreateImageViews(); + + void Destroy(); + + const vk::SurfaceKHR surface; + const VKDevice& device; + + UniqueSwapchainKHR swapchain; + + u32 image_count{}; + std::vector<vk::Image> images; + std::vector<UniqueImageView> image_views; + std::vector<UniqueFramebuffer> framebuffers; + std::vector<VKFence*> fences; + std::vector<UniqueSemaphore> present_semaphores; + + u32 image_index{}; + u32 frame_index{}; + + vk::Format image_format{}; + vk::Extent2D extent{}; + + u32 current_width{}; + u32 current_height{}; +}; + +} // namespace Vulkan diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp index 40da1a4e2..dc149d2ed 100644 --- a/src/web_service/web_backend.cpp +++ b/src/web_service/web_backend.cpp @@ -24,7 +24,7 @@ constexpr u32 TIMEOUT_SECONDS = 30; struct Client::Impl { Impl(std::string host, std::string username, std::string token) : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} { - std::lock_guard<std::mutex> lock(jwt_cache.mutex); + std::lock_guard lock{jwt_cache.mutex}; if (this->username == jwt_cache.username && this->token == jwt_cache.token) { jwt = jwt_cache.jwt; } @@ -151,7 +151,7 @@ struct Client::Impl { if (result.result_code != Common::WebResult::Code::Success) { LOG_ERROR(WebService, "UpdateJWT failed"); } else { - std::lock_guard<std::mutex> lock(jwt_cache.mutex); + std::lock_guard lock{jwt_cache.mutex}; jwt_cache.username = username; jwt_cache.token = token; jwt_cache.jwt = jwt = result.returned_data; diff --git a/src/yuzu/applets/profile_select.cpp b/src/yuzu/applets/profile_select.cpp index 5c1b65a2c..f95f7fe3c 100644 --- a/src/yuzu/applets/profile_select.cpp +++ b/src/yuzu/applets/profile_select.cpp @@ -58,10 +58,7 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent) scroll_area = new QScrollArea; - buttons = new QDialogButtonBox; - buttons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole); - buttons->addButton(tr("OK"), QDialogButtonBox::AcceptRole); - + buttons = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); connect(buttons, &QDialogButtonBox::accepted, this, &QtProfileSelectionDialog::accept); connect(buttons, &QDialogButtonBox::rejected, this, &QtProfileSelectionDialog::reject); @@ -163,6 +160,6 @@ void QtProfileSelector::SelectProfile( void QtProfileSelector::MainWindowFinishedSelection(std::optional<Service::Account::UUID> uuid) { // Acquire the HLE mutex - std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); + std::lock_guard lock{HLE::g_hle_lock}; callback(uuid); } diff --git a/src/yuzu/applets/software_keyboard.cpp b/src/yuzu/applets/software_keyboard.cpp index 8a26fdff1..f3eb29b25 100644 --- a/src/yuzu/applets/software_keyboard.cpp +++ b/src/yuzu/applets/software_keyboard.cpp @@ -75,13 +75,13 @@ QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog( length_label->setText(QStringLiteral("%1/%2").arg(text.size()).arg(parameters.max_length)); }); - buttons = new QDialogButtonBox; - buttons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole); - buttons->addButton(parameters.submit_text.empty() - ? tr("OK") - : QString::fromStdU16String(parameters.submit_text), - QDialogButtonBox::AcceptRole); - + buttons = new QDialogButtonBox(QDialogButtonBox::Cancel); + if (parameters.submit_text.empty()) { + buttons->addButton(QDialogButtonBox::Ok); + } else { + buttons->addButton(QString::fromStdU16String(parameters.submit_text), + QDialogButtonBox::AcceptRole); + } connect(buttons, &QDialogButtonBox::accepted, this, &QtSoftwareKeyboardDialog::accept); connect(buttons, &QDialogButtonBox::rejected, this, &QtSoftwareKeyboardDialog::reject); layout->addWidget(header_label); @@ -141,12 +141,12 @@ void QtSoftwareKeyboard::SendTextCheckDialog(std::u16string error_message, void QtSoftwareKeyboard::MainWindowFinishedText(std::optional<std::u16string> text) { // Acquire the HLE mutex - std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); + std::lock_guard lock{HLE::g_hle_lock}; text_output(text); } void QtSoftwareKeyboard::MainWindowFinishedCheckDialog() { // Acquire the HLE mutex - std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); + std::lock_guard lock{HLE::g_hle_lock}; finished_check(); } diff --git a/src/yuzu/applets/web_browser.cpp b/src/yuzu/applets/web_browser.cpp index 979b9ec14..ac80b2fa2 100644 --- a/src/yuzu/applets/web_browser.cpp +++ b/src/yuzu/applets/web_browser.cpp @@ -104,12 +104,12 @@ void QtWebBrowser::OpenPage(std::string_view url, std::function<void()> unpack_r void QtWebBrowser::MainWindowUnpackRomFS() { // Acquire the HLE mutex - std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); + std::lock_guard lock{HLE::g_hle_lock}; unpack_romfs_callback(); } void QtWebBrowser::MainWindowFinishedBrowsing() { // Acquire the HLE mutex - std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); + std::lock_guard lock{HLE::g_hle_lock}; finished_callback(); } diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 05ad19e1d..7438fbc0a 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -67,7 +67,7 @@ void EmuThread::run() { was_active = false; } else { - std::unique_lock<std::mutex> lock(running_mutex); + std::unique_lock lock{running_mutex}; running_cv.wait(lock, [this] { return IsRunning() || exec_step || stop_run; }); } } diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 7226e690e..3183621bc 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -53,7 +53,7 @@ public: * @note This function is thread-safe */ void SetRunning(bool running) { - std::unique_lock<std::mutex> lock(running_mutex); + std::unique_lock lock{running_mutex}; this->running = running; lock.unlock(); running_cv.notify_all(); diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 4650f96a3..dead9f807 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -413,7 +413,6 @@ void Config::ReadValues() { qt_config->beginGroup("System"); Settings::values.use_docked_mode = ReadSetting("use_docked_mode", false).toBool(); - Settings::values.enable_nfc = ReadSetting("enable_nfc", true).toBool(); Settings::values.current_user = std::clamp<int>(ReadSetting("current_user", 0).toInt(), 0, Service::Account::MAX_USERS - 1); @@ -675,7 +674,6 @@ void Config::SaveValues() { qt_config->beginGroup("System"); WriteSetting("use_docked_mode", Settings::values.use_docked_mode, false); - WriteSetting("enable_nfc", Settings::values.enable_nfc, true); WriteSetting("current_user", Settings::values.current_user, 0); WriteSetting("language_index", Settings::values.language_index, 1); diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index 4116b6cd7..389fcf667 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp @@ -33,7 +33,6 @@ void ConfigureGeneral::setConfiguration() { ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot); ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit); - ui->enable_nfc->setChecked(Settings::values.enable_nfc); } void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) { @@ -48,5 +47,4 @@ void ConfigureGeneral::applyConfiguration() { ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked(); - Settings::values.enable_nfc = ui->enable_nfc->isChecked(); } diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui index dff0ad5d0..01d1c0b8e 100644 --- a/src/yuzu/configuration/configure_general.ui +++ b/src/yuzu/configuration/configure_general.ui @@ -71,26 +71,6 @@ </widget> </item> <item> - <widget class="QGroupBox" name="EmulationGroupBox"> - <property name="title"> - <string>Emulation</string> - </property> - <layout class="QHBoxLayout" name="EmulationHorizontalLayout"> - <item> - <layout class="QVBoxLayout" name="EmulationVerticalLayout"> - <item> - <widget class="QCheckBox" name="enable_nfc"> - <property name="text"> - <string>Enable NFC</string> - </property> - </widget> - </item> - </layout> - </item> - </layout> - </widget> - </item> - <item> <widget class="QGroupBox" name="theme_group_box"> <property name="title"> <string>Theme</string> diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp index 11023ed63..f2d14becf 100644 --- a/src/yuzu/debugger/graphics/graphics_surface.cpp +++ b/src/yuzu/debugger/graphics/graphics_surface.cpp @@ -7,6 +7,7 @@ #include <QDebug> #include <QFileDialog> #include <QLabel> +#include <QMessageBox> #include <QMouseEvent> #include <QPushButton> #include <QScrollArea> @@ -95,50 +96,91 @@ GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Tegra::DebugContext surface_picker_y_control = new QSpinBox; surface_picker_y_control->setRange(0, max_dimension - 1); - surface_format_control = new QComboBox; - + // clang-format off // Color formats sorted by Maxwell texture format index - surface_format_control->addItem(tr("None")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("A8R8G8B8")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("DXT1")); - surface_format_control->addItem(tr("DXT23")); - surface_format_control->addItem(tr("DXT45")); - surface_format_control->addItem(tr("DXN1")); - surface_format_control->addItem(tr("DXN2")); + const QStringList surface_formats{ + tr("None"), + QStringLiteral("R32_G32_B32_A32"), + QStringLiteral("R32_G32_B32"), + QStringLiteral("R16_G16_B16_A16"), + QStringLiteral("R32_G32"), + QStringLiteral("R32_B24G8"), + QStringLiteral("ETC2_RGB"), + QStringLiteral("X8B8G8R8"), + QStringLiteral("A8R8G8B8"), + QStringLiteral("A2B10G10R10"), + QStringLiteral("ETC2_RGB_PTA"), + QStringLiteral("ETC2_RGBA"), + QStringLiteral("R16_G16"), + QStringLiteral("G8R24"), + QStringLiteral("G24R8"), + QStringLiteral("R32"), + QStringLiteral("BC6H_SF16"), + QStringLiteral("BC6H_UF16"), + QStringLiteral("A4B4G4R4"), + QStringLiteral("A5B5G5R1"), + QStringLiteral("A1B5G5R5"), + QStringLiteral("B5G6R5"), + QStringLiteral("B6G5R5"), + QStringLiteral("BC7U"), + QStringLiteral("G8R8"), + QStringLiteral("EAC"), + QStringLiteral("EACX2"), + QStringLiteral("R16"), + QStringLiteral("Y8_VIDEO"), + QStringLiteral("R8"), + QStringLiteral("G4R4"), + QStringLiteral("R1"), + QStringLiteral("E5B9G9R9_SHAREDEXP"), + QStringLiteral("BF10GF11RF11"), + QStringLiteral("G8B8G8R8"), + QStringLiteral("B8G8R8G8"), + QStringLiteral("DXT1"), + QStringLiteral("DXT23"), + QStringLiteral("DXT45"), + QStringLiteral("DXN1"), + QStringLiteral("DXN2"), + QStringLiteral("Z24S8"), + QStringLiteral("X8Z24"), + QStringLiteral("S8Z24"), + QStringLiteral("X4V4Z24__COV4R4V"), + QStringLiteral("X4V4Z24__COV8R8V"), + QStringLiteral("V8Z24__COV4R12V"), + QStringLiteral("ZF32"), + QStringLiteral("ZF32_X24S8"), + QStringLiteral("X8Z24_X20V4S8__COV4R4V"), + QStringLiteral("X8Z24_X20V4S8__COV8R8V"), + QStringLiteral("ZF32_X20V4X8__COV4R4V"), + QStringLiteral("ZF32_X20V4X8__COV8R8V"), + QStringLiteral("ZF32_X20V4S8__COV4R4V"), + QStringLiteral("ZF32_X20V4S8__COV8R8V"), + QStringLiteral("X8Z24_X16V8S8__COV4R12V"), + QStringLiteral("ZF32_X16V8X8__COV4R12V"), + QStringLiteral("ZF32_X16V8S8__COV4R12V"), + QStringLiteral("Z16"), + QStringLiteral("V8Z24__COV8R24V"), + QStringLiteral("X8Z24_X16V8S8__COV8R24V"), + QStringLiteral("ZF32_X16V8X8__COV8R24V"), + QStringLiteral("ZF32_X16V8S8__COV8R24V"), + QStringLiteral("ASTC_2D_4X4"), + QStringLiteral("ASTC_2D_5X5"), + QStringLiteral("ASTC_2D_6X6"), + QStringLiteral("ASTC_2D_8X8"), + QStringLiteral("ASTC_2D_10X10"), + QStringLiteral("ASTC_2D_12X12"), + QStringLiteral("ASTC_2D_5X4"), + QStringLiteral("ASTC_2D_6X5"), + QStringLiteral("ASTC_2D_8X6"), + QStringLiteral("ASTC_2D_10X8"), + QStringLiteral("ASTC_2D_12X10"), + QStringLiteral("ASTC_2D_8X5"), + QStringLiteral("ASTC_2D_10X5"), + QStringLiteral("ASTC_2D_10X6"), + }; + // clang-format on + + surface_format_control = new QComboBox; + surface_format_control->addItems(surface_formats); surface_info_label = new QLabel(); surface_info_label->setWordWrap(true); @@ -157,22 +199,20 @@ GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Tegra::DebugContext // Connections connect(this, &GraphicsSurfaceWidget::Update, this, &GraphicsSurfaceWidget::OnUpdate); - connect(surface_source_list, - static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, + connect(surface_source_list, qOverload<int>(&QComboBox::currentIndexChanged), this, &GraphicsSurfaceWidget::OnSurfaceSourceChanged); connect(surface_address_control, &CSpinBox::ValueChanged, this, &GraphicsSurfaceWidget::OnSurfaceAddressChanged); - connect(surface_width_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), - this, &GraphicsSurfaceWidget::OnSurfaceWidthChanged); - connect(surface_height_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), - this, &GraphicsSurfaceWidget::OnSurfaceHeightChanged); - connect(surface_format_control, - static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, + connect(surface_width_control, qOverload<int>(&QSpinBox::valueChanged), this, + &GraphicsSurfaceWidget::OnSurfaceWidthChanged); + connect(surface_height_control, qOverload<int>(&QSpinBox::valueChanged), this, + &GraphicsSurfaceWidget::OnSurfaceHeightChanged); + connect(surface_format_control, qOverload<int>(&QComboBox::currentIndexChanged), this, &GraphicsSurfaceWidget::OnSurfaceFormatChanged); - connect(surface_picker_x_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), - this, &GraphicsSurfaceWidget::OnSurfacePickerXChanged); - connect(surface_picker_y_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), - this, &GraphicsSurfaceWidget::OnSurfacePickerYChanged); + connect(surface_picker_x_control, qOverload<int>(&QSpinBox::valueChanged), this, + &GraphicsSurfaceWidget::OnSurfacePickerXChanged); + connect(surface_picker_y_control, qOverload<int>(&QSpinBox::valueChanged), this, + &GraphicsSurfaceWidget::OnSurfacePickerYChanged); connect(save_surface, &QPushButton::clicked, this, &GraphicsSurfaceWidget::SaveSurface); auto main_widget = new QWidget; @@ -420,40 +460,56 @@ void GraphicsSurfaceWidget::OnUpdate() { } void GraphicsSurfaceWidget::SaveSurface() { - QString png_filter = tr("Portable Network Graphic (*.png)"); - QString bin_filter = tr("Binary data (*.bin)"); + const QString png_filter = tr("Portable Network Graphic (*.png)"); + const QString bin_filter = tr("Binary data (*.bin)"); - QString selectedFilter; - QString filename = QFileDialog::getSaveFileName( + QString selected_filter; + const QString filename = QFileDialog::getSaveFileName( this, tr("Save Surface"), - QString("texture-0x%1.png").arg(QString::number(surface_address, 16)), - QString("%1;;%2").arg(png_filter, bin_filter), &selectedFilter); + QStringLiteral("texture-0x%1.png").arg(QString::number(surface_address, 16)), + QStringLiteral("%1;;%2").arg(png_filter, bin_filter), &selected_filter); if (filename.isEmpty()) { // If the user canceled the dialog, don't save anything. return; } - if (selectedFilter == png_filter) { - const QPixmap* pixmap = surface_picture_label->pixmap(); + if (selected_filter == png_filter) { + const QPixmap* const pixmap = surface_picture_label->pixmap(); ASSERT_MSG(pixmap != nullptr, "No pixmap set"); - QFile file(filename); - file.open(QIODevice::WriteOnly); - if (pixmap) - pixmap->save(&file, "PNG"); - } else if (selectedFilter == bin_filter) { + QFile file{filename}; + if (!file.open(QIODevice::WriteOnly)) { + QMessageBox::warning(this, tr("Error"), tr("Failed to open file '%1'").arg(filename)); + return; + } + + if (!pixmap->save(&file, "PNG")) { + QMessageBox::warning(this, tr("Error"), + tr("Failed to save surface data to file '%1'").arg(filename)); + } + } else if (selected_filter == bin_filter) { auto& gpu = Core::System::GetInstance().GPU(); - std::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address); + const std::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address); - const u8* buffer = Memory::GetPointer(*address); + const u8* const buffer = Memory::GetPointer(*address); ASSERT_MSG(buffer != nullptr, "Memory not accessible"); - QFile file(filename); - file.open(QIODevice::WriteOnly); - int size = surface_width * surface_height * Tegra::Texture::BytesPerPixel(surface_format); - QByteArray data(reinterpret_cast<const char*>(buffer), size); - file.write(data); + QFile file{filename}; + if (!file.open(QIODevice::WriteOnly)) { + QMessageBox::warning(this, tr("Error"), tr("Failed to open file '%1'").arg(filename)); + return; + } + + const int size = + surface_width * surface_height * Tegra::Texture::BytesPerPixel(surface_format); + const QByteArray data(reinterpret_cast<const char*>(buffer), size); + if (file.write(data) != data.size()) { + QMessageBox::warning( + this, tr("Error"), + tr("Failed to completely write surface data to file. The saved data will " + "likely be corrupt.")); + } } else { UNREACHABLE_MSG("Unhandled filter selected"); } diff --git a/src/yuzu/debugger/profiler.cpp b/src/yuzu/debugger/profiler.cpp index 8b30e0a85..86e03e46d 100644 --- a/src/yuzu/debugger/profiler.cpp +++ b/src/yuzu/debugger/profiler.cpp @@ -7,6 +7,7 @@ #include <QMouseEvent> #include <QPainter> #include <QString> +#include <QTimer> #include "common/common_types.h" #include "common/microprofile.h" #include "yuzu/debugger/profiler.h" diff --git a/src/yuzu/debugger/profiler.h b/src/yuzu/debugger/profiler.h index eae1e9e3c..8e69fdb06 100644 --- a/src/yuzu/debugger/profiler.h +++ b/src/yuzu/debugger/profiler.h @@ -4,10 +4,11 @@ #pragma once -#include <QAbstractItemModel> -#include <QDockWidget> -#include <QTimer> -#include "common/microprofile.h" +#include <QWidget> + +class QAction; +class QHideEvent; +class QShowEvent; class MicroProfileDialog : public QWidget { Q_OBJECT diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index c0e3c5fa9..4422a572b 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -329,6 +329,8 @@ void GameList::PopupContextMenu(const QPoint& menu_location) { QMenu context_menu; QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location")); QAction* open_lfs_location = context_menu.addAction(tr("Open Mod Data Location")); + QAction* open_transferable_shader_cache = + context_menu.addAction(tr("Open Transferable Shader Cache")); context_menu.addSeparator(); QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS")); QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); @@ -344,6 +346,8 @@ void GameList::PopupContextMenu(const QPoint& menu_location) { [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData); }); connect(open_lfs_location, &QAction::triggered, [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::ModData); }); + connect(open_transferable_shader_cache, &QAction::triggered, + [&]() { emit OpenTransferableShaderCacheRequested(program_id); }); connect(dump_romfs, &QAction::triggered, [&]() { emit DumpRomFSRequested(program_id, path); }); connect(copy_tid, &QAction::triggered, [&]() { emit CopyTIDRequested(program_id); }); connect(navigate_to_gamedb_entry, &QAction::triggered, diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index b317eb2fc..8ea5cbaaa 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -66,6 +66,7 @@ signals: void GameChosen(QString game_path); void ShouldCancelWorker(); void OpenFolderRequested(u64 program_id, GameListOpenTarget target); + void OpenTransferableShaderCacheRequested(u64 program_id); void DumpRomFSRequested(u64 program_id, const std::string& game_path); void CopyTIDRequested(u64 program_id); void NavigateToGamedbEntryRequested(u64 program_id, diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 41ba3c4c6..2b9db69a3 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -37,14 +37,20 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include <glad/glad.h> #define QT_NO_OPENGL +#include <QClipboard> +#include <QDesktopServices> #include <QDesktopWidget> #include <QDialogButtonBox> #include <QFile> #include <QFileDialog> +#include <QInputDialog> #include <QMessageBox> +#include <QProgressBar> +#include <QProgressDialog> +#include <QShortcut> +#include <QStatusBar> #include <QtConcurrent/QtConcurrent> -#include <QtGui> -#include <QtWidgets> + #include <fmt/format.h> #include "common/common_paths.h" #include "common/detached_tasks.h" @@ -55,11 +61,9 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "common/microprofile.h" #include "common/scm_rev.h" #include "common/scope_exit.h" -#include "common/string_util.h" #include "common/telemetry.h" #include "core/core.h" #include "core/crypto/key_manager.h" -#include "core/file_sys/bis_factory.h" #include "core/file_sys/card_image.h" #include "core/file_sys/content_archive.h" #include "core/file_sys/control_metadata.h" @@ -71,7 +75,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "core/frontend/applets/software_keyboard.h" #include "core/hle/kernel/process.h" #include "core/hle/service/filesystem/filesystem.h" -#include "core/hle/service/filesystem/fsp_ldr.h" #include "core/hle/service/nfp/nfp.h" #include "core/hle/service/sm/sm.h" #include "core/loader/loader.h" @@ -648,6 +651,8 @@ void GMainWindow::RestoreUIState() { void GMainWindow::ConnectWidgetEvents() { connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile); connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder); + connect(game_list, &GameList::OpenTransferableShaderCacheRequested, this, + &GMainWindow::OnTransferableShaderCacheOpenFile); connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS); connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, @@ -1082,6 +1087,39 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target QDesktopServices::openUrl(QUrl::fromLocalFile(qpath)); } +void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) { + ASSERT(program_id != 0); + + const QString tranferable_shader_cache_folder_path = + QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)) + "opengl" + + DIR_SEP + "transferable"; + + const QString transferable_shader_cache_file_path = + tranferable_shader_cache_folder_path + DIR_SEP + + QString::fromStdString(fmt::format("{:016X}.bin", program_id)); + + if (!QFile::exists(transferable_shader_cache_file_path)) { + QMessageBox::warning(this, tr("Error Opening Transferable Shader Cache"), + tr("A shader cache for this title does not exist.")); + return; + } + + // Windows supports opening a folder with selecting a specified file in explorer. On every other + // OS we just open the transferable shader cache folder without preselecting the transferable + // shader cache file for the selected game. +#if defined(Q_OS_WIN) + const QString explorer = QStringLiteral("explorer"); + QStringList param; + if (!QFileInfo(transferable_shader_cache_file_path).isDir()) { + param << QStringLiteral("/select,"); + } + param << QDir::toNativeSeparators(transferable_shader_cache_file_path); + QProcess::startDetached(explorer, param); +#else + QDesktopServices::openUrl(QUrl::fromLocalFile(tranferable_shader_cache_folder_path)); +#endif +} + static std::size_t CalculateRomFSEntrySize(const FileSys::VirtualDir& dir, bool full) { std::size_t out = 0; diff --git a/src/yuzu/main.h b/src/yuzu/main.h index e07c892cf..7f3aa998e 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -176,6 +176,7 @@ private slots: /// Called whenever a user selects a game in the game list widget. void OnGameListLoadFile(QString game_path); void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target); + void OnTransferableShaderCacheOpenFile(u64 program_id); void OnGameListDumpRomFS(u64 program_id, const std::string& game_path); void OnGameListCopyTID(u64 program_id); void OnGameListNavigateToGamedbEntry(u64 program_id, diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 32e78049c..f24cc77fe 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -319,7 +319,6 @@ void Config::ReadValues() { // System Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false); - Settings::values.enable_nfc = sdl2_config->GetBoolean("System", "enable_nfc", true); const auto size = sdl2_config->GetInteger("System", "users_size", 0); Settings::values.current_user = std::clamp<int>( |