diff options
Diffstat (limited to '')
167 files changed, 2197 insertions, 2553 deletions
diff --git a/src/audio_core/command_generator.cpp b/src/audio_core/command_generator.cpp index 45b2eef52..830af46ad 100644 --- a/src/audio_core/command_generator.cpp +++ b/src/audio_core/command_generator.cpp @@ -2,13 +2,16 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> #include <cmath> #include <numbers> + #include "audio_core/algorithm/interpolate.h" #include "audio_core/command_generator.h" #include "audio_core/effect_context.h" #include "audio_core/mix_context.h" #include "audio_core/voice_context.h" +#include "common/common_types.h" #include "core/memory.h" namespace AudioCore { diff --git a/src/audio_core/mix_context.cpp b/src/audio_core/mix_context.cpp index 4bca72eb0..057aab5ad 100644 --- a/src/audio_core/mix_context.cpp +++ b/src/audio_core/mix_context.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> + #include "audio_core/behavior_info.h" #include "audio_core/common.h" #include "audio_core/effect_context.h" diff --git a/src/audio_core/voice_context.cpp b/src/audio_core/voice_context.cpp index d8c954b60..75012a887 100644 --- a/src/audio_core/voice_context.cpp +++ b/src/audio_core/voice_context.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> + #include "audio_core/behavior_info.h" #include "audio_core/voice_context.h" #include "core/memory.h" diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index b18a2a2f5..cb5c0f326 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -79,6 +79,7 @@ add_library(common STATIC logging/filter.cpp logging/filter.h logging/log.h + logging/log_entry.h logging/text_formatter.cpp logging/text_formatter.h logging/types.h diff --git a/src/common/alignment.h b/src/common/alignment.h index 32d796ffa..1b56569d1 100644 --- a/src/common/alignment.h +++ b/src/common/alignment.h @@ -9,41 +9,48 @@ namespace Common { template <typename T> -requires std::is_unsigned_v<T>[[nodiscard]] constexpr T AlignUp(T value, size_t size) { +requires std::is_unsigned_v<T> +[[nodiscard]] constexpr T AlignUp(T value, size_t size) { auto mod{static_cast<T>(value % size)}; value -= mod; return static_cast<T>(mod == T{0} ? value : value + size); } template <typename T> -requires std::is_unsigned_v<T>[[nodiscard]] constexpr T AlignUpLog2(T value, size_t align_log2) { +requires std::is_unsigned_v<T> +[[nodiscard]] constexpr T AlignUpLog2(T value, size_t align_log2) { return static_cast<T>((value + ((1ULL << align_log2) - 1)) >> align_log2 << align_log2); } template <typename T> -requires std::is_unsigned_v<T>[[nodiscard]] constexpr T AlignDown(T value, size_t size) { +requires std::is_unsigned_v<T> +[[nodiscard]] constexpr T AlignDown(T value, size_t size) { return static_cast<T>(value - value % size); } template <typename T> -requires std::is_unsigned_v<T>[[nodiscard]] constexpr bool Is4KBAligned(T value) { +requires std::is_unsigned_v<T> +[[nodiscard]] constexpr bool Is4KBAligned(T value) { return (value & 0xFFF) == 0; } template <typename T> -requires std::is_unsigned_v<T>[[nodiscard]] constexpr bool IsWordAligned(T value) { +requires std::is_unsigned_v<T> +[[nodiscard]] constexpr bool IsWordAligned(T value) { return (value & 0b11) == 0; } template <typename T> -requires std::is_integral_v<T>[[nodiscard]] constexpr bool IsAligned(T value, size_t alignment) { +requires std::is_integral_v<T> +[[nodiscard]] constexpr bool IsAligned(T value, size_t alignment) { using U = typename std::make_unsigned_t<T>; const U mask = static_cast<U>(alignment - 1); return (value & mask) == 0; } template <typename T, typename U> -requires std::is_integral_v<T>[[nodiscard]] constexpr T DivideUp(T x, U y) { +requires std::is_integral_v<T> +[[nodiscard]] constexpr T DivideUp(T x, U y) { return (x + (y - 1)) / y; } diff --git a/src/common/div_ceil.h b/src/common/div_ceil.h index 95e1489a9..e1db35464 100644 --- a/src/common/div_ceil.h +++ b/src/common/div_ceil.h @@ -11,15 +11,15 @@ namespace Common { /// Ceiled integer division. template <typename N, typename D> -requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr N DivCeil(N number, - D divisor) { +requires std::is_integral_v<N> && std::is_unsigned_v<D> +[[nodiscard]] constexpr N DivCeil(N number, D divisor) { return static_cast<N>((static_cast<D>(number) + divisor - 1) / divisor); } /// Ceiled integer division with logarithmic divisor in base 2 template <typename N, typename D> -requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr N DivCeilLog2( - N value, D alignment_log2) { +requires std::is_integral_v<N> && std::is_unsigned_v<D> +[[nodiscard]] constexpr N DivCeilLog2(N value, D alignment_log2) { return static_cast<N>((static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2); } diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp index 6661244cf..b44a44949 100644 --- a/src/common/host_memory.cpp +++ b/src/common/host_memory.cpp @@ -314,8 +314,8 @@ private: } void UntrackPlaceholder(boost::icl::separate_interval_set<size_t>::iterator it) { - placeholders.erase(it); placeholder_host_pointers.erase(it->lower()); + placeholders.erase(it); } /// Return true when a given memory region is a "nieche" and the placeholders don't have to be diff --git a/src/common/intrusive_red_black_tree.h b/src/common/intrusive_red_black_tree.h index 1f696fe80..3173cc449 100644 --- a/src/common/intrusive_red_black_tree.h +++ b/src/common/intrusive_red_black_tree.h @@ -235,20 +235,19 @@ public: template <typename T> concept HasLightCompareType = requires { - { std::is_same<typename T::LightCompareType, void>::value } - ->std::convertible_to<bool>; + { std::is_same<typename T::LightCompareType, void>::value } -> std::convertible_to<bool>; }; namespace impl { -template <typename T, typename Default> -consteval auto* GetLightCompareType() { - if constexpr (HasLightCompareType<T>) { - return static_cast<typename T::LightCompareType*>(nullptr); - } else { - return static_cast<Default*>(nullptr); + template <typename T, typename Default> + consteval auto* GetLightCompareType() { + if constexpr (HasLightCompareType<T>) { + return static_cast<typename T::LightCompareType*>(nullptr); + } else { + return static_cast<Default*>(nullptr); + } } -} } // namespace impl diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index e40d117d6..0e85a9c1d 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -9,6 +9,8 @@ #include <thread> #include <vector> +#include <fmt/format.h> + #ifdef _WIN32 #include <windows.h> // For OutputDebugStringW #endif @@ -22,6 +24,7 @@ #include "common/logging/backend.h" #include "common/logging/log.h" +#include "common/logging/log_entry.h" #include "common/logging/text_formatter.h" #include "common/settings.h" #ifdef _WIN32 diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 8d43eddc7..c186d55ef 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h @@ -4,7 +4,11 @@ #pragma once -#include <fmt/format.h> +#include <algorithm> +#include <string_view> + +#include <fmt/core.h> + #include "common/logging/types.h" namespace Common::Log { diff --git a/src/common/logging/log_entry.h b/src/common/logging/log_entry.h new file mode 100644 index 000000000..dd6f44841 --- /dev/null +++ b/src/common/logging/log_entry.h @@ -0,0 +1,28 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <chrono> + +#include "common/logging/types.h" + +namespace Common::Log { + +/** + * A log entry. Log entries are store in a structured format to permit more varied output + * formatting on different frontends, as well as facilitating filtering and aggregation. + */ +struct Entry { + std::chrono::microseconds timestamp; + Class log_class{}; + Level log_level{}; + const char* filename = nullptr; + unsigned int line_num = 0; + std::string function; + std::string message; + bool final_entry = false; +}; + +} // namespace Common::Log diff --git a/src/common/logging/text_formatter.cpp b/src/common/logging/text_formatter.cpp index cfc0d5846..10b2281db 100644 --- a/src/common/logging/text_formatter.cpp +++ b/src/common/logging/text_formatter.cpp @@ -13,6 +13,7 @@ #include "common/common_funcs.h" #include "common/logging/filter.h" #include "common/logging/log.h" +#include "common/logging/log_entry.h" #include "common/logging/text_formatter.h" #include "common/string_util.h" diff --git a/src/common/logging/types.h b/src/common/logging/types.h index ddf9d27ca..2d21fc483 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -4,8 +4,6 @@ #pragma once -#include <chrono> - #include "common/common_types.h" namespace Common::Log { @@ -131,19 +129,4 @@ enum class Class : u8 { Count ///< Total number of logging classes }; -/** - * A log entry. Log entries are store in a structured format to permit more varied output - * formatting on different frontends, as well as facilitating filtering and aggregation. - */ -struct Entry { - std::chrono::microseconds timestamp; - Class log_class{}; - Level log_level{}; - const char* filename = nullptr; - unsigned int line_num = 0; - std::string function; - std::string message; - bool final_entry = false; -}; - } // namespace Common::Log diff --git a/src/common/param_package.cpp b/src/common/param_package.cpp index b916b4866..bbf20f5eb 100644 --- a/src/common/param_package.cpp +++ b/src/common/param_package.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include <array> +#include <stdexcept> #include <utility> #include <vector> diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 69f0bd8c0..9dd5e3efb 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -69,8 +69,6 @@ void LogSettings() { log_path("DataStorage_NANDDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir)); log_path("DataStorage_SDMCDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir)); log_setting("Debugging_ProgramArgs", values.program_args.GetValue()); - log_setting("Services_BCATBackend", values.bcat_backend.GetValue()); - log_setting("Services_BCATBoxcatLocal", values.bcat_boxcat_local.GetValue()); log_setting("Input_EnableMotion", values.motion_enabled.GetValue()); log_setting("Input_EnableVibration", values.vibration_enabled.GetValue()); log_setting("Input_EnableRawInput", values.enable_raw_input.GetValue()); diff --git a/src/common/settings.h b/src/common/settings.h index c53d5acc3..402339443 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -568,8 +568,6 @@ struct Values { BasicSetting<bool> use_dev_keys{false, "use_dev_keys"}; // Network - BasicSetting<std::string> bcat_backend{"none", "bcat_backend"}; - BasicSetting<bool> bcat_boxcat_local{false, "bcat_boxcat_local"}; BasicSetting<std::string> network_interface{std::string(), "network_interface"}; // WebService diff --git a/src/common/uuid.h b/src/common/uuid.h index 2353179d8..8ea01f8da 100644 --- a/src/common/uuid.h +++ b/src/common/uuid.h @@ -58,6 +58,13 @@ struct UUID { uuid = INVALID_UUID; } + [[nodiscard]] constexpr bool IsInvalid() const { + return uuid == INVALID_UUID; + } + [[nodiscard]] constexpr bool IsValid() const { + return !IsInvalid(); + } + // TODO(ogniK): Properly generate a Nintendo ID [[nodiscard]] constexpr u64 GetNintendoID() const { return uuid[0]; diff --git a/src/common/vector_math.h b/src/common/vector_math.h index 22dba3c2d..ba7c363c1 100644 --- a/src/common/vector_math.h +++ b/src/common/vector_math.h @@ -667,8 +667,8 @@ template <typename T> // linear interpolation via float: 0.0=begin, 1.0=end template <typename X> -[[nodiscard]] constexpr decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end, - const float t) { +[[nodiscard]] constexpr decltype(X{} * float{} + X{} * float{}) + Lerp(const X& begin, const X& end, const float t) { return begin * (1.f - t) + end * t; } diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 7140d0db8..9f0fbba2d 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -106,8 +106,6 @@ add_library(core STATIC file_sys/vfs_concat.h file_sys/vfs_layered.cpp file_sys/vfs_layered.h - file_sys/vfs_libzip.cpp - file_sys/vfs_libzip.h file_sys/vfs_offset.cpp file_sys/vfs_offset.h file_sys/vfs_real.cpp @@ -218,6 +216,7 @@ add_library(core STATIC hle/kernel/k_session.h hle/kernel/k_shared_memory.cpp hle/kernel/k_shared_memory.h + hle/kernel/k_shared_memory_info.h hle/kernel/k_slab_heap.h hle/kernel/k_spin_lock.cpp hle/kernel/k_spin_lock.h @@ -653,13 +652,6 @@ add_library(core STATIC tools/freezer.h ) -if (YUZU_ENABLE_BOXCAT) - target_sources(core PRIVATE - hle/service/bcat/backend/boxcat.cpp - hle/service/bcat/backend/boxcat.h - ) -endif() - if (MSVC) target_compile_options(core PRIVATE /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data @@ -690,12 +682,7 @@ endif() 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::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus zip) - -if (YUZU_ENABLE_BOXCAT) - target_compile_definitions(core PRIVATE -DYUZU_ENABLE_BOXCAT) - target_link_libraries(core PRIVATE httplib nlohmann_json::nlohmann_json) -endif() +target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus) if (ENABLE_WEB_SERVICE) target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE) diff --git a/src/core/core.cpp b/src/core/core.cpp index 54ebed2c1..bb268a319 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -305,7 +305,6 @@ struct System::Impl { is_powered_on = false; exit_lock = false; - gpu_core.reset(); services.reset(); service_manager.reset(); cheat_engine.reset(); @@ -315,6 +314,7 @@ struct System::Impl { core_timing.Shutdown(); app_loader.reset(); perf_stats.reset(); + gpu_core.reset(); kernel.Shutdown(); memory.Reset(); applet_manager.ClearAll(); @@ -421,6 +421,7 @@ struct System::Impl { bool is_async_gpu{}; ExecuteProgramCallback execute_program_callback; + ExitCallback exit_callback; std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{}; std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_dynarmic{}; @@ -798,6 +799,18 @@ void System::ExecuteProgram(std::size_t program_index) { } } +void System::RegisterExitCallback(ExitCallback&& callback) { + impl->exit_callback = std::move(callback); +} + +void System::Exit() { + if (impl->exit_callback) { + impl->exit_callback(); + } else { + LOG_CRITICAL(Core, "exit_callback must be initialized by the frontend"); + } +} + void System::ApplySettings() { if (IsPoweredOn()) { Renderer().RefreshBaseSettings(); diff --git a/src/core/core.h b/src/core/core.h index 715ab88e7..a796472b2 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -387,6 +387,18 @@ public: */ void ExecuteProgram(std::size_t program_index); + /// Type used for the frontend to designate a callback for System to exit the application. + using ExitCallback = std::function<void()>; + + /** + * Registers a callback from the frontend for System to exit the application. + * @param callback Callback from the frontend to exit the application. + */ + void RegisterExitCallback(ExitCallback&& callback); + + /// Instructs the frontend to exit the application. + void Exit(); + /// Applies any changes to settings to this core instance. void ApplySettings(); diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index 01ae1a567..35a53d36c 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp @@ -77,7 +77,7 @@ void ProgramMetadata::LoadManual(bool is_64_bit, ProgramAddressSpaceType address aci_header.title_id = title_id; aci_file_access.permissions = filesystem_permissions; npdm_header.system_resource_size = system_resource_size; - aci_kernel_capabilities = std ::move(capabilities); + aci_kernel_capabilities = std::move(capabilities); } bool ProgramMetadata::Is64BitProgram() const { diff --git a/src/core/file_sys/vfs_libzip.cpp b/src/core/file_sys/vfs_libzip.cpp deleted file mode 100644 index 00e256779..000000000 --- a/src/core/file_sys/vfs_libzip.cpp +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <string> - -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wshadow" -#endif -#include <zip.h> -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif - -#include "common/fs/path_util.h" -#include "core/file_sys/vfs.h" -#include "core/file_sys/vfs_libzip.h" -#include "core/file_sys/vfs_vector.h" - -namespace FileSys { - -VirtualDir ExtractZIP(VirtualFile file) { - zip_error_t error{}; - - const auto data = file->ReadAllBytes(); - std::unique_ptr<zip_source_t, decltype(&zip_source_close)> src{ - zip_source_buffer_create(data.data(), data.size(), 0, &error), zip_source_close}; - if (src == nullptr) - return nullptr; - - std::unique_ptr<zip_t, decltype(&zip_close)> zip{zip_open_from_source(src.get(), 0, &error), - zip_close}; - if (zip == nullptr) - return nullptr; - - std::shared_ptr<VectorVfsDirectory> out = std::make_shared<VectorVfsDirectory>(); - - const auto num_entries = static_cast<std::size_t>(zip_get_num_entries(zip.get(), 0)); - - zip_stat_t stat{}; - zip_stat_init(&stat); - - for (std::size_t i = 0; i < num_entries; ++i) { - const auto stat_res = zip_stat_index(zip.get(), i, 0, &stat); - if (stat_res == -1) - return nullptr; - - const std::string name(stat.name); - if (name.empty()) - continue; - - if (name.back() != '/') { - std::unique_ptr<zip_file_t, decltype(&zip_fclose)> file2{ - zip_fopen_index(zip.get(), i, 0), zip_fclose}; - - std::vector<u8> buf(stat.size); - if (zip_fread(file2.get(), buf.data(), buf.size()) != s64(buf.size())) - return nullptr; - - const auto parts = Common::FS::SplitPathComponents(stat.name); - const auto new_file = std::make_shared<VectorVfsFile>(buf, parts.back()); - - std::shared_ptr<VectorVfsDirectory> dtrv = out; - for (std::size_t j = 0; j < parts.size() - 1; ++j) { - if (dtrv == nullptr) - return nullptr; - const auto subdir = dtrv->GetSubdirectory(parts[j]); - if (subdir == nullptr) { - const auto temp = std::make_shared<VectorVfsDirectory>( - std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, parts[j]); - dtrv->AddDirectory(temp); - dtrv = temp; - } else { - dtrv = std::dynamic_pointer_cast<VectorVfsDirectory>(subdir); - } - } - - if (dtrv == nullptr) - return nullptr; - dtrv->AddFile(new_file); - } - } - - return out; -} - -} // namespace FileSys diff --git a/src/core/file_sys/vfs_libzip.h b/src/core/file_sys/vfs_libzip.h deleted file mode 100644 index f68af576a..000000000 --- a/src/core/file_sys/vfs_libzip.h +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "core/file_sys/vfs_types.h" - -namespace FileSys { - -VirtualDir ExtractZIP(VirtualFile zip); - -} // namespace FileSys diff --git a/src/core/frontend/applets/profile_select.cpp b/src/core/frontend/applets/profile_select.cpp index 4c58c310f..3e4f90be2 100644 --- a/src/core/frontend/applets/profile_select.cpp +++ b/src/core/frontend/applets/profile_select.cpp @@ -13,7 +13,8 @@ ProfileSelectApplet::~ProfileSelectApplet() = default; void DefaultProfileSelectApplet::SelectProfile( std::function<void(std::optional<Common::UUID>)> callback) const { Service::Account::ProfileManager manager; - callback(manager.GetUser(Settings::values.current_user.GetValue()).value_or(Common::UUID{})); + callback(manager.GetUser(Settings::values.current_user.GetValue()) + .value_or(Common::UUID{Common::INVALID_UUID})); LOG_INFO(Service_ACC, "called, selecting current user instead of prompting..."); } diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index ceff2532d..cf204f570 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h @@ -4,17 +4,14 @@ #pragma once -#include <array> #include <cstring> #include <memory> -#include <tuple> #include <type_traits> #include <utility> #include "common/assert.h" #include "common/common_types.h" #include "core/hle/ipc.h" #include "core/hle/kernel/hle_ipc.h" -#include "core/hle/kernel/k_client_port.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_resource_limit.h" #include "core/hle/kernel/k_session.h" diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index ca68fc325..cee96dd9b 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -15,6 +15,7 @@ #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/k_auto_object.h" #include "core/hle/kernel/k_handle_table.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_readable_event.h" diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index a61870f8b..55e6fb9f7 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -17,7 +17,6 @@ #include "common/concepts.h" #include "common/swap.h" #include "core/hle/ipc.h" -#include "core/hle/kernel/k_auto_object.h" #include "core/hle/kernel/svc_common.h" union ResultCode; @@ -38,6 +37,7 @@ namespace Kernel { class Domain; class HLERequestContext; +class KAutoObject; class KernelCore; class KHandleTable; class KProcess; diff --git a/src/core/hle/kernel/k_auto_object_container.cpp b/src/core/hle/kernel/k_auto_object_container.cpp index 010006bb7..d5f80d5b2 100644 --- a/src/core/hle/kernel/k_auto_object_container.cpp +++ b/src/core/hle/kernel/k_auto_object_container.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> + #include "core/hle/kernel/k_auto_object_container.h" namespace Kernel { diff --git a/src/core/hle/kernel/k_handle_table.cpp b/src/core/hle/kernel/k_handle_table.cpp index 6a420d5b0..44d13169f 100644 --- a/src/core/hle/kernel/k_handle_table.cpp +++ b/src/core/hle/kernel/k_handle_table.cpp @@ -7,7 +7,7 @@ namespace Kernel { KHandleTable::KHandleTable(KernelCore& kernel_) : kernel{kernel_} {} -KHandleTable ::~KHandleTable() = default; +KHandleTable::~KHandleTable() = default; ResultCode KHandleTable::Finalize() { // Get the table and clear our record of it. diff --git a/src/core/hle/kernel/k_priority_queue.h b/src/core/hle/kernel/k_priority_queue.h index 4aa669d95..f4d71ad7e 100644 --- a/src/core/hle/kernel/k_priority_queue.h +++ b/src/core/hle/kernel/k_priority_queue.h @@ -22,12 +22,10 @@ class KThread; template <typename T> concept KPriorityQueueAffinityMask = !std::is_reference_v<T> && requires(T & t) { - { t.GetAffinityMask() } - ->Common::ConvertibleTo<u64>; + { t.GetAffinityMask() } -> Common::ConvertibleTo<u64>; {t.SetAffinityMask(0)}; - { t.GetAffinity(0) } - ->std::same_as<bool>; + { t.GetAffinity(0) } -> std::same_as<bool>; {t.SetAffinity(0, false)}; {t.SetAll()}; }; @@ -38,25 +36,20 @@ concept KPriorityQueueMember = !std::is_reference_v<T> && requires(T & t) { {(typename T::QueueEntry()).Initialize()}; {(typename T::QueueEntry()).SetPrev(std::addressof(t))}; {(typename T::QueueEntry()).SetNext(std::addressof(t))}; - { (typename T::QueueEntry()).GetNext() } - ->std::same_as<T*>; - { (typename T::QueueEntry()).GetPrev() } - ->std::same_as<T*>; - { t.GetPriorityQueueEntry(0) } - ->std::same_as<typename T::QueueEntry&>; + { (typename T::QueueEntry()).GetNext() } -> std::same_as<T*>; + { (typename T::QueueEntry()).GetPrev() } -> std::same_as<T*>; + { t.GetPriorityQueueEntry(0) } -> std::same_as<typename T::QueueEntry&>; {t.GetAffinityMask()}; - { std::remove_cvref_t<decltype(t.GetAffinityMask())>() } - ->KPriorityQueueAffinityMask; + { std::remove_cvref_t<decltype(t.GetAffinityMask())>() } -> KPriorityQueueAffinityMask; - { t.GetActiveCore() } - ->Common::ConvertibleTo<s32>; - { t.GetPriority() } - ->Common::ConvertibleTo<s32>; + { t.GetActiveCore() } -> Common::ConvertibleTo<s32>; + { t.GetPriority() } -> Common::ConvertibleTo<s32>; }; template <typename Member, size_t NumCores_, int LowestPriority, int HighestPriority> -requires KPriorityQueueMember<Member> class KPriorityQueue { +requires KPriorityQueueMember<Member> +class KPriorityQueue { public: using AffinityMaskType = std::remove_cv_t< std::remove_reference_t<decltype(std::declval<Member>().GetAffinityMask())>>; diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 8ead1a769..211157ccc 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -23,6 +23,7 @@ #include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/k_shared_memory.h" +#include "core/hle/kernel/k_shared_memory_info.h" #include "core/hle/kernel/k_slab_heap.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" @@ -254,10 +255,26 @@ ResultCode KProcess::AddSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAdd // Lock ourselves, to prevent concurrent access. KScopedLightLock lk(state_lock); - // TODO(bunnei): Manage KSharedMemoryInfo list here. + // Try to find an existing info for the memory. + KSharedMemoryInfo* shemen_info = nullptr; + const auto iter = std::find_if( + shared_memory_list.begin(), shared_memory_list.end(), + [shmem](const KSharedMemoryInfo* info) { return info->GetSharedMemory() == shmem; }); + if (iter != shared_memory_list.end()) { + shemen_info = *iter; + } + + if (shemen_info == nullptr) { + shemen_info = KSharedMemoryInfo::Allocate(kernel); + R_UNLESS(shemen_info != nullptr, ResultOutOfMemory); + + shemen_info->Initialize(shmem); + shared_memory_list.push_back(shemen_info); + } - // Open a reference to the shared memory. + // Open a reference to the shared memory and its info. shmem->Open(); + shemen_info->Open(); return ResultSuccess; } @@ -267,7 +284,20 @@ void KProcess::RemoveSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr a // Lock ourselves, to prevent concurrent access. KScopedLightLock lk(state_lock); - // TODO(bunnei): Manage KSharedMemoryInfo list here. + KSharedMemoryInfo* shemen_info = nullptr; + const auto iter = std::find_if( + shared_memory_list.begin(), shared_memory_list.end(), + [shmem](const KSharedMemoryInfo* info) { return info->GetSharedMemory() == shmem; }); + if (iter != shared_memory_list.end()) { + shemen_info = *iter; + } + + ASSERT(shemen_info != nullptr); + + if (shemen_info->Close()) { + shared_memory_list.erase(iter); + KSharedMemoryInfo::Free(kernel, shemen_info); + } // Close a reference to the shared memory. shmem->Close(); @@ -412,6 +442,24 @@ void KProcess::Finalize() { // Finalize the handle table and close any open handles. handle_table.Finalize(); + // Free all shared memory infos. + { + auto it = shared_memory_list.begin(); + while (it != shared_memory_list.end()) { + KSharedMemoryInfo* info = *it; + KSharedMemory* shmem = info->GetSharedMemory(); + + while (!info->Close()) { + shmem->Close(); + } + + shmem->Close(); + + it = shared_memory_list.erase(it); + KSharedMemoryInfo::Free(kernel, info); + } + } + // Perform inherited finalization. KAutoObjectWithSlabHeapAndContainer<KProcess, KSynchronizationObject>::Finalize(); } diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index a03c074fb..1a53e2be7 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h @@ -34,6 +34,7 @@ class KernelCore; class KPageTable; class KResourceLimit; class KThread; +class KSharedMemoryInfo; class TLSPage; struct CodeSet; @@ -448,6 +449,9 @@ private: /// List of threads that are running with this process as their owner. std::list<const KThread*> thread_list; + /// List of shared memory that are running with this process as their owner. + std::list<KSharedMemoryInfo*> shared_memory_list; + /// Address of the top of the main thread's stack VAddr main_thread_stack_top{}; diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h index 12cfae919..c8ccc1ae4 100644 --- a/src/core/hle/kernel/k_scheduler.h +++ b/src/core/hle/kernel/k_scheduler.h @@ -197,7 +197,7 @@ private: class [[nodiscard]] KScopedSchedulerLock : KScopedLock<GlobalSchedulerContext::LockType> { public: - explicit KScopedSchedulerLock(KernelCore & kernel); + explicit KScopedSchedulerLock(KernelCore& kernel); ~KScopedSchedulerLock(); }; diff --git a/src/core/hle/kernel/k_scoped_lock.h b/src/core/hle/kernel/k_scoped_lock.h index 72c3b0252..4fb180fc6 100644 --- a/src/core/hle/kernel/k_scoped_lock.h +++ b/src/core/hle/kernel/k_scoped_lock.h @@ -13,19 +13,18 @@ namespace Kernel { template <typename T> concept KLockable = !std::is_reference_v<T> && requires(T & t) { - { t.Lock() } - ->std::same_as<void>; - { t.Unlock() } - ->std::same_as<void>; + { t.Lock() } -> std::same_as<void>; + { t.Unlock() } -> std::same_as<void>; }; template <typename T> -requires KLockable<T> class [[nodiscard]] KScopedLock { +requires KLockable<T> +class [[nodiscard]] KScopedLock { public: - explicit KScopedLock(T * l) : lock_ptr(l) { + explicit KScopedLock(T* l) : lock_ptr(l) { this->lock_ptr->Lock(); } - explicit KScopedLock(T & l) : KScopedLock(std::addressof(l)) {} + explicit KScopedLock(T& l) : KScopedLock(std::addressof(l)) {} ~KScopedLock() { this->lock_ptr->Unlock(); @@ -34,7 +33,7 @@ public: KScopedLock(const KScopedLock&) = delete; KScopedLock& operator=(const KScopedLock&) = delete; - KScopedLock(KScopedLock &&) = delete; + KScopedLock(KScopedLock&&) = delete; KScopedLock& operator=(KScopedLock&&) = delete; private: diff --git a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h index a86af56dd..f6c75f2d9 100644 --- a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h +++ b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h @@ -17,7 +17,7 @@ namespace Kernel { class [[nodiscard]] KScopedSchedulerLockAndSleep { public: - explicit KScopedSchedulerLockAndSleep(KernelCore & kernel_, KThread * t, s64 timeout) + explicit KScopedSchedulerLockAndSleep(KernelCore& kernel_, KThread* t, s64 timeout) : kernel(kernel_), thread(t), timeout_tick(timeout) { // Lock the scheduler. kernel.GlobalSchedulerContext().scheduler_lock.Lock(); diff --git a/src/core/hle/kernel/k_shared_memory_info.h b/src/core/hle/kernel/k_shared_memory_info.h new file mode 100644 index 000000000..bf97a0184 --- /dev/null +++ b/src/core/hle/kernel/k_shared_memory_info.h @@ -0,0 +1,46 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <string> + +#include <boost/intrusive/list.hpp> + +#include "common/assert.h" +#include "core/hle/kernel/slab_helpers.h" + +namespace Kernel { + +class KSharedMemory; + +class KSharedMemoryInfo final : public KSlabAllocated<KSharedMemoryInfo>, + public boost::intrusive::list_base_hook<> { + +public: + explicit KSharedMemoryInfo() = default; + + constexpr void Initialize(KSharedMemory* shmem) { + shared_memory = shmem; + } + + constexpr KSharedMemory* GetSharedMemory() const { + return shared_memory; + } + + constexpr void Open() { + ++reference_count; + } + + constexpr bool Close() { + return (--reference_count) == 0; + } + +private: + KSharedMemory* shared_memory{}; + size_t reference_count{}; +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 901d43da9..b6658b437 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -49,6 +49,7 @@ class KScheduler; class KServerSession; class KSession; class KSharedMemory; +class KSharedMemoryInfo; class KThread; class KTransferMemory; class KWritableEvent; @@ -309,6 +310,8 @@ public: return slab_heap_container->session; } else if constexpr (std::is_same_v<T, KSharedMemory>) { return slab_heap_container->shared_memory; + } else if constexpr (std::is_same_v<T, KSharedMemoryInfo>) { + return slab_heap_container->shared_memory_info; } else if constexpr (std::is_same_v<T, KThread>) { return slab_heap_container->thread; } else if constexpr (std::is_same_v<T, KTransferMemory>) { @@ -362,6 +365,7 @@ private: KSlabHeap<KResourceLimit> resource_limit; KSlabHeap<KSession> session; KSlabHeap<KSharedMemory> shared_memory; + KSlabHeap<KSharedMemoryInfo> shared_memory_info; KSlabHeap<KThread> thread; KSlabHeap<KTransferMemory> transfer_memory; KSlabHeap<KWritableEvent> writeable_event; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 62fb06c45..f98f24a60 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -320,17 +320,19 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) { auto& kernel = system.Kernel(); - KScopedAutoObject session = - kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle); - R_UNLESS(session.IsNotNull(), ResultInvalidHandle); - LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); - auto thread = kernel.CurrentScheduler()->GetCurrentThread(); { KScopedSchedulerLock lock(kernel); thread->SetState(ThreadState::Waiting); thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC); - session->SendSyncRequest(thread, system.Memory(), system.CoreTiming()); + + { + KScopedAutoObject session = + kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle); + R_UNLESS(session.IsNotNull(), ResultInvalidHandle); + LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); + session->SendSyncRequest(thread, system.Memory(), system.CoreTiming()); + } } KSynchronizationObject* dummy{}; diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 6d9ec0a8a..689b36056 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -929,8 +929,7 @@ void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContex } const auto user_list = profile_manager->GetAllUsers(); - if (std::all_of(user_list.begin(), user_list.end(), - [](const auto& user) { return user.uuid == Common::INVALID_UUID; })) { + if (std::ranges::all_of(user_list, [](const auto& user) { return user.IsInvalid(); })) { rb.Push(ResultUnknown); // TODO(ogniK): Find the correct error code rb.PushRaw<u128>(Common::INVALID_UUID); return; diff --git a/src/core/hle/service/acc/async_context.cpp b/src/core/hle/service/acc/async_context.cpp index 459323132..a49dfdec7 100644 --- a/src/core/hle/service/acc/async_context.cpp +++ b/src/core/hle/service/acc/async_context.cpp @@ -4,15 +4,12 @@ #include "core/core.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_event.h" #include "core/hle/service/acc/async_context.h" namespace Service::Account { IAsyncContext::IAsyncContext(Core::System& system_) - : ServiceFramework{system_, "IAsyncContext"}, compeletion_event{system_.Kernel()} { - - Kernel::KAutoObject::Create(std::addressof(compeletion_event)); - compeletion_event.Initialize("IAsyncContext:CompletionEvent"); - + : ServiceFramework{system_, "IAsyncContext"}, service_context{system_, "IAsyncContext"} { // clang-format off static const FunctionInfo functions[] = { {0, &IAsyncContext::GetSystemEvent, "GetSystemEvent"}, @@ -23,6 +20,12 @@ IAsyncContext::IAsyncContext(Core::System& system_) // clang-format on RegisterHandlers(functions); + + completion_event = service_context.CreateEvent("IAsyncContext:CompletionEvent"); +} + +IAsyncContext::~IAsyncContext() { + service_context.CloseEvent(completion_event); } void IAsyncContext::GetSystemEvent(Kernel::HLERequestContext& ctx) { @@ -30,7 +33,7 @@ void IAsyncContext::GetSystemEvent(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(compeletion_event.GetReadableEvent()); + rb.PushCopyObjects(completion_event->GetReadableEvent()); } void IAsyncContext::Cancel(Kernel::HLERequestContext& ctx) { @@ -62,7 +65,7 @@ void IAsyncContext::GetResult(Kernel::HLERequestContext& ctx) { void IAsyncContext::MarkComplete() { is_complete.store(true); - compeletion_event.GetWritableEvent().Signal(); + completion_event->GetWritableEvent().Signal(); } } // namespace Service::Account diff --git a/src/core/hle/service/acc/async_context.h b/src/core/hle/service/acc/async_context.h index c694b4946..cc3a0a9fe 100644 --- a/src/core/hle/service/acc/async_context.h +++ b/src/core/hle/service/acc/async_context.h @@ -5,7 +5,7 @@ #pragma once #include <atomic> -#include "core/hle/kernel/k_event.h" +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" namespace Core { @@ -17,6 +17,7 @@ namespace Service::Account { class IAsyncContext : public ServiceFramework<IAsyncContext> { public: explicit IAsyncContext(Core::System& system_); + ~IAsyncContext() override; void GetSystemEvent(Kernel::HLERequestContext& ctx); void Cancel(Kernel::HLERequestContext& ctx); @@ -30,8 +31,10 @@ protected: void MarkComplete(); + KernelHelpers::ServiceContext service_context; + std::atomic<bool> is_complete{false}; - Kernel::KEvent compeletion_event; + Kernel::KEvent* completion_event; }; } // namespace Service::Account diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index 24a1c9157..568303ced 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp @@ -208,9 +208,10 @@ bool ProfileManager::UserExists(UUID uuid) const { } bool ProfileManager::UserExistsIndex(std::size_t index) const { - if (index >= MAX_USERS) + if (index >= MAX_USERS) { return false; - return profiles[index].user_uuid.uuid != Common::INVALID_UUID; + } + return profiles[index].user_uuid.IsValid(); } /// Opens a specific user @@ -304,7 +305,7 @@ bool ProfileManager::RemoveUser(UUID uuid) { bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) { const auto index = GetUserIndex(uuid); - if (!index || profile_new.user_uuid == UUID(Common::INVALID_UUID)) { + if (!index || profile_new.user_uuid.IsInvalid()) { return false; } @@ -346,7 +347,7 @@ void ProfileManager::ParseUserSaveFile() { } for (const auto& user : data.users) { - if (user.uuid == UUID(Common::INVALID_UUID)) { + if (user.uuid.IsInvalid()) { continue; } diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index c3ac73131..eccdcc20d 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -16,9 +16,7 @@ #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_process.h" -#include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_transfer_memory.h" -#include "core/hle/kernel/k_writable_event.h" #include "core/hle/kernel/kernel.h" #include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/am/am.h" @@ -254,8 +252,9 @@ IDebugFunctions::IDebugFunctions(Core::System& system_) IDebugFunctions::~IDebugFunctions() = default; ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nvflinger_) - : ServiceFramework{system_, "ISelfController"}, nvflinger{nvflinger_}, - launchable_event{system.Kernel()}, accumulated_suspended_tick_changed_event{system.Kernel()} { + : ServiceFramework{system_, "ISelfController"}, nvflinger{nvflinger_}, service_context{ + system, + "ISelfController"} { // clang-format off static const FunctionInfo functions[] = { {0, &ISelfController::Exit, "Exit"}, @@ -275,12 +274,14 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv {18, nullptr, "SetRequiresCaptureButtonShortPressedMessage"}, {19, &ISelfController::SetAlbumImageOrientation, "SetAlbumImageOrientation"}, {20, nullptr, "SetDesirableKeyboardLayout"}, + {21, nullptr, "GetScreenShotProgramId"}, {40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"}, {41, nullptr, "IsSystemBufferSharingEnabled"}, {42, nullptr, "GetSystemSharedLayerHandle"}, {43, nullptr, "GetSystemSharedBufferHandle"}, {44, &ISelfController::CreateManagedDisplaySeparableLayer, "CreateManagedDisplaySeparableLayer"}, {45, nullptr, "SetManagedDisplayLayerSeparationMode"}, + {46, nullptr, "SetRecordingLayerCompositionEnabled"}, {50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"}, {51, nullptr, "ApproveToDisplay"}, {60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"}, @@ -302,15 +303,14 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv {100, &ISelfController::SetAlbumImageTakenNotificationEnabled, "SetAlbumImageTakenNotificationEnabled"}, {110, nullptr, "SetApplicationAlbumUserData"}, {120, nullptr, "SaveCurrentScreenshot"}, + {130, nullptr, "SetRecordVolumeMuted"}, {1000, nullptr, "GetDebugStorageChannel"}, }; // clang-format on RegisterHandlers(functions); - Kernel::KAutoObject::Create(std::addressof(launchable_event)); - - launchable_event.Initialize("ISelfController:LaunchableEvent"); + launchable_event = service_context.CreateEvent("ISelfController:LaunchableEvent"); // This event is created by AM on the first time GetAccumulatedSuspendedTickChangedEvent() is // called. Yuzu can just create it unconditionally, since it doesn't need to support multiple @@ -318,21 +318,23 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv // suspended if the event has previously been created by a call to // GetAccumulatedSuspendedTickChangedEvent. - Kernel::KAutoObject::Create(std::addressof(accumulated_suspended_tick_changed_event)); - accumulated_suspended_tick_changed_event.Initialize( - "ISelfController:AccumulatedSuspendedTickChangedEvent"); - accumulated_suspended_tick_changed_event.GetWritableEvent().Signal(); + accumulated_suspended_tick_changed_event = + service_context.CreateEvent("ISelfController:AccumulatedSuspendedTickChangedEvent"); + accumulated_suspended_tick_changed_event->GetWritableEvent().Signal(); } -ISelfController::~ISelfController() = default; +ISelfController::~ISelfController() { + service_context.CloseEvent(launchable_event); + service_context.CloseEvent(accumulated_suspended_tick_changed_event); +} void ISelfController::Exit(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "called"); - system.Shutdown(); - IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); + + system.Exit(); } void ISelfController::LockExit(Kernel::HLERequestContext& ctx) { @@ -380,11 +382,11 @@ void ISelfController::LeaveFatalSection(Kernel::HLERequestContext& ctx) { void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_AM, "(STUBBED) called"); - launchable_event.GetWritableEvent().Signal(); + launchable_event->GetWritableEvent().Signal(); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(launchable_event.GetReadableEvent()); + rb.PushCopyObjects(launchable_event->GetReadableEvent()); } void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) { @@ -563,7 +565,7 @@ void ISelfController::GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequest IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(accumulated_suspended_tick_changed_event.GetReadableEvent()); + rb.PushCopyObjects(accumulated_suspended_tick_changed_event->GetReadableEvent()); } void ISelfController::SetAlbumImageTakenNotificationEnabled(Kernel::HLERequestContext& ctx) { @@ -581,40 +583,39 @@ void ISelfController::SetAlbumImageTakenNotificationEnabled(Kernel::HLERequestCo rb.Push(ResultSuccess); } -AppletMessageQueue::AppletMessageQueue(Kernel::KernelCore& kernel) - : on_new_message{kernel}, on_operation_mode_changed{kernel} { - - Kernel::KAutoObject::Create(std::addressof(on_new_message)); - Kernel::KAutoObject::Create(std::addressof(on_operation_mode_changed)); - - on_new_message.Initialize("AMMessageQueue:OnMessageReceived"); - on_operation_mode_changed.Initialize("AMMessageQueue:OperationModeChanged"); +AppletMessageQueue::AppletMessageQueue(Core::System& system) + : service_context{system, "AppletMessageQueue"} { + on_new_message = service_context.CreateEvent("AMMessageQueue:OnMessageReceived"); + on_operation_mode_changed = service_context.CreateEvent("AMMessageQueue:OperationModeChanged"); } -AppletMessageQueue::~AppletMessageQueue() = default; +AppletMessageQueue::~AppletMessageQueue() { + service_context.CloseEvent(on_new_message); + service_context.CloseEvent(on_operation_mode_changed); +} Kernel::KReadableEvent& AppletMessageQueue::GetMessageReceiveEvent() { - return on_new_message.GetReadableEvent(); + return on_new_message->GetReadableEvent(); } Kernel::KReadableEvent& AppletMessageQueue::GetOperationModeChangedEvent() { - return on_operation_mode_changed.GetReadableEvent(); + return on_operation_mode_changed->GetReadableEvent(); } void AppletMessageQueue::PushMessage(AppletMessage msg) { messages.push(msg); - on_new_message.GetWritableEvent().Signal(); + on_new_message->GetWritableEvent().Signal(); } AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() { if (messages.empty()) { - on_new_message.GetWritableEvent().Clear(); + on_new_message->GetWritableEvent().Clear(); return AppletMessage::NoMessage; } auto msg = messages.front(); messages.pop(); if (messages.empty()) { - on_new_message.GetWritableEvent().Clear(); + on_new_message->GetWritableEvent().Clear(); } return msg; } @@ -634,7 +635,7 @@ void AppletMessageQueue::FocusStateChanged() { void AppletMessageQueue::OperationModeChanged() { PushMessage(AppletMessage::OperationModeChanged); PushMessage(AppletMessage::PerformanceModeChanged); - on_operation_mode_changed.GetWritableEvent().Signal(); + on_operation_mode_changed->GetWritableEvent().Signal(); } ICommonStateGetter::ICommonStateGetter(Core::System& system_, @@ -683,6 +684,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, {91, nullptr, "GetCurrentPerformanceConfiguration"}, {100, nullptr, "SetHandlingHomeButtonShortPressedEnabled"}, {110, nullptr, "OpenMyGpuErrorHandler"}, + {120, nullptr, "GetAppletLaunchedHistory"}, {200, nullptr, "GetOperationModeSystemInfo"}, {300, nullptr, "GetSettingsPlatformRegion"}, {400, nullptr, "ActivateMigrationService"}, @@ -1268,10 +1270,8 @@ void ILibraryAppletCreator::CreateHandleStorage(Kernel::HLERequestContext& ctx) } IApplicationFunctions::IApplicationFunctions(Core::System& system_) - : ServiceFramework{system_, "IApplicationFunctions"}, gpu_error_detected_event{system.Kernel()}, - friend_invitation_storage_channel_event{system.Kernel()}, - notification_storage_channel_event{system.Kernel()}, health_warning_disappeared_system_event{ - system.Kernel()} { + : ServiceFramework{system_, "IApplicationFunctions"}, service_context{system, + "IApplicationFunctions"} { // clang-format off static const FunctionInfo functions[] = { {1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"}, @@ -1339,21 +1339,22 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_) RegisterHandlers(functions); - Kernel::KAutoObject::Create(std::addressof(gpu_error_detected_event)); - Kernel::KAutoObject::Create(std::addressof(friend_invitation_storage_channel_event)); - Kernel::KAutoObject::Create(std::addressof(notification_storage_channel_event)); - Kernel::KAutoObject::Create(std::addressof(health_warning_disappeared_system_event)); - - gpu_error_detected_event.Initialize("IApplicationFunctions:GpuErrorDetectedSystemEvent"); - friend_invitation_storage_channel_event.Initialize( - "IApplicationFunctions:FriendInvitationStorageChannelEvent"); - notification_storage_channel_event.Initialize( - "IApplicationFunctions:NotificationStorageChannelEvent"); - health_warning_disappeared_system_event.Initialize( - "IApplicationFunctions:HealthWarningDisappearedSystemEvent"); + gpu_error_detected_event = + service_context.CreateEvent("IApplicationFunctions:GpuErrorDetectedSystemEvent"); + friend_invitation_storage_channel_event = + service_context.CreateEvent("IApplicationFunctions:FriendInvitationStorageChannelEvent"); + notification_storage_channel_event = + service_context.CreateEvent("IApplicationFunctions:NotificationStorageChannelEvent"); + health_warning_disappeared_system_event = + service_context.CreateEvent("IApplicationFunctions:HealthWarningDisappearedSystemEvent"); } -IApplicationFunctions::~IApplicationFunctions() = default; +IApplicationFunctions::~IApplicationFunctions() { + service_context.CloseEvent(gpu_error_detected_event); + service_context.CloseEvent(friend_invitation_storage_channel_event); + service_context.CloseEvent(notification_storage_channel_event); + service_context.CloseEvent(health_warning_disappeared_system_event); +} void IApplicationFunctions::EnableApplicationCrashReport(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_AM, "(STUBBED) called"); @@ -1747,7 +1748,7 @@ void IApplicationFunctions::GetGpuErrorDetectedSystemEvent(Kernel::HLERequestCon IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(gpu_error_detected_event.GetReadableEvent()); + rb.PushCopyObjects(gpu_error_detected_event->GetReadableEvent()); } void IApplicationFunctions::GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx) { @@ -1755,7 +1756,7 @@ void IApplicationFunctions::GetFriendInvitationStorageChannelEvent(Kernel::HLERe IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(friend_invitation_storage_channel_event.GetReadableEvent()); + rb.PushCopyObjects(friend_invitation_storage_channel_event->GetReadableEvent()); } void IApplicationFunctions::TryPopFromFriendInvitationStorageChannel( @@ -1771,7 +1772,7 @@ void IApplicationFunctions::GetNotificationStorageChannelEvent(Kernel::HLEReques IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(notification_storage_channel_event.GetReadableEvent()); + rb.PushCopyObjects(notification_storage_channel_event->GetReadableEvent()); } void IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(Kernel::HLERequestContext& ctx) { @@ -1779,12 +1780,12 @@ void IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(Kernel::HLERe IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(health_warning_disappeared_system_event.GetReadableEvent()); + rb.PushCopyObjects(health_warning_disappeared_system_event->GetReadableEvent()); } void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger, Core::System& system) { - auto message_queue = std::make_shared<AppletMessageQueue>(system.Kernel()); + auto message_queue = std::make_shared<AppletMessageQueue>(system); // Needed on game boot message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); @@ -1797,8 +1798,8 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger } IHomeMenuFunctions::IHomeMenuFunctions(Core::System& system_) - : ServiceFramework{system_, "IHomeMenuFunctions"}, pop_from_general_channel_event{ - system.Kernel()} { + : ServiceFramework{system_, "IHomeMenuFunctions"}, service_context{system, + "IHomeMenuFunctions"} { // clang-format off static const FunctionInfo functions[] = { {10, &IHomeMenuFunctions::RequestToGetForeground, "RequestToGetForeground"}, @@ -1819,11 +1820,13 @@ IHomeMenuFunctions::IHomeMenuFunctions(Core::System& system_) RegisterHandlers(functions); - Kernel::KAutoObject::Create(std::addressof(pop_from_general_channel_event)); - pop_from_general_channel_event.Initialize("IHomeMenuFunctions:PopFromGeneralChannelEvent"); + pop_from_general_channel_event = + service_context.CreateEvent("IHomeMenuFunctions:PopFromGeneralChannelEvent"); } -IHomeMenuFunctions::~IHomeMenuFunctions() = default; +IHomeMenuFunctions::~IHomeMenuFunctions() { + service_context.CloseEvent(pop_from_general_channel_event); +} void IHomeMenuFunctions::RequestToGetForeground(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_AM, "(STUBBED) called"); @@ -1837,7 +1840,7 @@ void IHomeMenuFunctions::GetPopFromGeneralChannelEvent(Kernel::HLERequestContext IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(pop_from_general_channel_event.GetReadableEvent()); + rb.PushCopyObjects(pop_from_general_channel_event->GetReadableEvent()); } IGlobalStateController::IGlobalStateController(Core::System& system_) diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index c13aa5787..202d20757 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -8,7 +8,7 @@ #include <memory> #include <queue> -#include "core/hle/kernel/k_event.h" +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" namespace Kernel { @@ -53,7 +53,7 @@ public: PerformanceModeChanged = 31, }; - explicit AppletMessageQueue(Kernel::KernelCore& kernel); + explicit AppletMessageQueue(Core::System& system); ~AppletMessageQueue(); Kernel::KReadableEvent& GetMessageReceiveEvent(); @@ -66,9 +66,12 @@ public: void OperationModeChanged(); private: + KernelHelpers::ServiceContext service_context; + + Kernel::KEvent* on_new_message; + Kernel::KEvent* on_operation_mode_changed; + std::queue<AppletMessage> messages; - Kernel::KEvent on_new_message; - Kernel::KEvent on_operation_mode_changed; }; class IWindowController final : public ServiceFramework<IWindowController> { @@ -156,8 +159,11 @@ private: }; NVFlinger::NVFlinger& nvflinger; - Kernel::KEvent launchable_event; - Kernel::KEvent accumulated_suspended_tick_changed_event; + + KernelHelpers::ServiceContext service_context; + + Kernel::KEvent* launchable_event; + Kernel::KEvent* accumulated_suspended_tick_changed_event; u32 idle_time_detection_extension = 0; u64 num_fatal_sections_entered = 0; @@ -298,13 +304,15 @@ private: void GetNotificationStorageChannelEvent(Kernel::HLERequestContext& ctx); void GetHealthWarningDisappearedSystemEvent(Kernel::HLERequestContext& ctx); + KernelHelpers::ServiceContext service_context; + bool launch_popped_application_specific = false; bool launch_popped_account_preselect = false; s32 previous_program_index{-1}; - Kernel::KEvent gpu_error_detected_event; - Kernel::KEvent friend_invitation_storage_channel_event; - Kernel::KEvent notification_storage_channel_event; - Kernel::KEvent health_warning_disappeared_system_event; + Kernel::KEvent* gpu_error_detected_event; + Kernel::KEvent* friend_invitation_storage_channel_event; + Kernel::KEvent* notification_storage_channel_event; + Kernel::KEvent* health_warning_disappeared_system_event; }; class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> { @@ -316,7 +324,9 @@ private: void RequestToGetForeground(Kernel::HLERequestContext& ctx); void GetPopFromGeneralChannelEvent(Kernel::HLERequestContext& ctx); - Kernel::KEvent pop_from_general_channel_event; + KernelHelpers::ServiceContext service_context; + + Kernel::KEvent* pop_from_general_channel_event; }; class IGlobalStateController final : public ServiceFramework<IGlobalStateController> { diff --git a/src/core/hle/service/am/applet_ae.h b/src/core/hle/service/am/applet_ae.h index adb207349..f89f65649 100644 --- a/src/core/hle/service/am/applet_ae.h +++ b/src/core/hle/service/am/applet_ae.h @@ -5,7 +5,7 @@ #pragma once #include <memory> -#include "core/hle/kernel/hle_ipc.h" + #include "core/hle/service/service.h" namespace Service { diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h index 6c1aa255a..64b874ead 100644 --- a/src/core/hle/service/am/applet_oe.h +++ b/src/core/hle/service/am/applet_oe.h @@ -5,7 +5,7 @@ #pragma once #include <memory> -#include "core/hle/kernel/hle_ipc.h" + #include "core/hle/service/service.h" namespace Service { diff --git a/src/core/hle/service/am/applets/applet_profile_select.cpp b/src/core/hle/service/am/applets/applet_profile_select.cpp index bdc21778e..a6e891944 100644 --- a/src/core/hle/service/am/applets/applet_profile_select.cpp +++ b/src/core/hle/service/am/applets/applet_profile_select.cpp @@ -60,7 +60,7 @@ void ProfileSelect::Execute() { void ProfileSelect::SelectionComplete(std::optional<Common::UUID> uuid) { UserSelectionOutput output{}; - if (uuid.has_value() && uuid->uuid != Common::INVALID_UUID) { + if (uuid.has_value() && uuid->IsValid()) { output.result = 0; output.uuid_selected = uuid->uuid; } else { diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index 2b7685d42..7320b1c0f 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp @@ -12,8 +12,7 @@ #include "core/frontend/applets/profile_select.h" #include "core/frontend/applets/software_keyboard.h" #include "core/frontend/applets/web_browser.h" -#include "core/hle/kernel/k_readable_event.h" -#include "core/hle/kernel/k_writable_event.h" +#include "core/hle/kernel/k_event.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/applet_ae.h" #include "core/hle/service/am/applet_oe.h" @@ -29,19 +28,19 @@ namespace Service::AM::Applets { AppletDataBroker::AppletDataBroker(Core::System& system_, LibraryAppletMode applet_mode_) - : system{system_}, applet_mode{applet_mode_}, state_changed_event{system.Kernel()}, - pop_out_data_event{system.Kernel()}, pop_interactive_out_data_event{system.Kernel()} { - - Kernel::KAutoObject::Create(std::addressof(state_changed_event)); - Kernel::KAutoObject::Create(std::addressof(pop_out_data_event)); - Kernel::KAutoObject::Create(std::addressof(pop_interactive_out_data_event)); - - state_changed_event.Initialize("ILibraryAppletAccessor:StateChangedEvent"); - pop_out_data_event.Initialize("ILibraryAppletAccessor:PopDataOutEvent"); - pop_interactive_out_data_event.Initialize("ILibraryAppletAccessor:PopInteractiveDataOutEvent"); + : system{system_}, applet_mode{applet_mode_}, service_context{system, + "ILibraryAppletAccessor"} { + state_changed_event = service_context.CreateEvent("ILibraryAppletAccessor:StateChangedEvent"); + pop_out_data_event = service_context.CreateEvent("ILibraryAppletAccessor:PopDataOutEvent"); + pop_interactive_out_data_event = + service_context.CreateEvent("ILibraryAppletAccessor:PopInteractiveDataOutEvent"); } -AppletDataBroker::~AppletDataBroker() = default; +AppletDataBroker::~AppletDataBroker() { + service_context.CloseEvent(state_changed_event); + service_context.CloseEvent(pop_out_data_event); + service_context.CloseEvent(pop_interactive_out_data_event); +} AppletDataBroker::RawChannelData AppletDataBroker::PeekDataToAppletForDebug() const { std::vector<std::vector<u8>> out_normal; @@ -65,7 +64,7 @@ std::shared_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() { auto out = std::move(out_channel.front()); out_channel.pop_front(); - pop_out_data_event.GetWritableEvent().Clear(); + pop_out_data_event->GetWritableEvent().Clear(); return out; } @@ -84,7 +83,7 @@ std::shared_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() { auto out = std::move(out_interactive_channel.front()); out_interactive_channel.pop_front(); - pop_interactive_out_data_event.GetWritableEvent().Clear(); + pop_interactive_out_data_event->GetWritableEvent().Clear(); return out; } @@ -103,7 +102,7 @@ void AppletDataBroker::PushNormalDataFromGame(std::shared_ptr<IStorage>&& storag void AppletDataBroker::PushNormalDataFromApplet(std::shared_ptr<IStorage>&& storage) { out_channel.emplace_back(std::move(storage)); - pop_out_data_event.GetWritableEvent().Signal(); + pop_out_data_event->GetWritableEvent().Signal(); } void AppletDataBroker::PushInteractiveDataFromGame(std::shared_ptr<IStorage>&& storage) { @@ -112,11 +111,11 @@ void AppletDataBroker::PushInteractiveDataFromGame(std::shared_ptr<IStorage>&& s void AppletDataBroker::PushInteractiveDataFromApplet(std::shared_ptr<IStorage>&& storage) { out_interactive_channel.emplace_back(std::move(storage)); - pop_interactive_out_data_event.GetWritableEvent().Signal(); + pop_interactive_out_data_event->GetWritableEvent().Signal(); } void AppletDataBroker::SignalStateChanged() { - state_changed_event.GetWritableEvent().Signal(); + state_changed_event->GetWritableEvent().Signal(); switch (applet_mode) { case LibraryAppletMode::AllForeground: @@ -141,15 +140,15 @@ void AppletDataBroker::SignalStateChanged() { } Kernel::KReadableEvent& AppletDataBroker::GetNormalDataEvent() { - return pop_out_data_event.GetReadableEvent(); + return pop_out_data_event->GetReadableEvent(); } Kernel::KReadableEvent& AppletDataBroker::GetInteractiveDataEvent() { - return pop_interactive_out_data_event.GetReadableEvent(); + return pop_interactive_out_data_event->GetReadableEvent(); } Kernel::KReadableEvent& AppletDataBroker::GetStateChangedEvent() { - return state_changed_event.GetReadableEvent(); + return state_changed_event->GetReadableEvent(); } Applet::Applet(Core::System& system_, LibraryAppletMode applet_mode_) diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index 5c0b4b459..15eeb4ee1 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h @@ -8,7 +8,7 @@ #include <queue> #include "common/swap.h" -#include "core/hle/kernel/k_event.h" +#include "core/hle/service/kernel_helpers.h" union ResultCode; @@ -105,6 +105,8 @@ private: Core::System& system; LibraryAppletMode applet_mode; + KernelHelpers::ServiceContext service_context; + // Queues are named from applet's perspective // PopNormalDataToApplet and PushNormalDataFromGame @@ -119,13 +121,13 @@ private: // PopInteractiveDataToGame and PushInteractiveDataFromApplet std::deque<std::shared_ptr<IStorage>> out_interactive_channel; - Kernel::KEvent state_changed_event; + Kernel::KEvent* state_changed_event; // Signaled on PushNormalDataFromApplet - Kernel::KEvent pop_out_data_event; + Kernel::KEvent* pop_out_data_event; // Signaled on PushInteractiveDataFromApplet - Kernel::KEvent pop_interactive_out_data_event; + Kernel::KEvent* pop_interactive_out_data_event; }; class Applet { diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index dd945e058..4c54066c6 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp @@ -16,8 +16,8 @@ #include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_process.h" -#include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/kernel.h" #include "core/hle/service/aoc/aoc_u.h" #include "core/loader/loader.h" @@ -49,7 +49,8 @@ static std::vector<u64> AccumulateAOCTitleIDs(Core::System& system) { class IPurchaseEventManager final : public ServiceFramework<IPurchaseEventManager> { public: explicit IPurchaseEventManager(Core::System& system_) - : ServiceFramework{system_, "IPurchaseEventManager"}, purchased_event{system.Kernel()} { + : ServiceFramework{system_, "IPurchaseEventManager"}, service_context{ + system, "IPurchaseEventManager"} { // clang-format off static const FunctionInfo functions[] = { {0, &IPurchaseEventManager::SetDefaultDeliveryTarget, "SetDefaultDeliveryTarget"}, @@ -62,8 +63,11 @@ public: RegisterHandlers(functions); - Kernel::KAutoObject::Create(std::addressof(purchased_event)); - purchased_event.Initialize("IPurchaseEventManager:PurchasedEvent"); + purchased_event = service_context.CreateEvent("IPurchaseEventManager:PurchasedEvent"); + } + + ~IPurchaseEventManager() override { + service_context.CloseEvent(purchased_event); } private: @@ -96,15 +100,17 @@ private: IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(purchased_event.GetReadableEvent()); + rb.PushCopyObjects(purchased_event->GetReadableEvent()); } - Kernel::KEvent purchased_event; + KernelHelpers::ServiceContext service_context; + + Kernel::KEvent* purchased_event; }; AOC_U::AOC_U(Core::System& system_) : ServiceFramework{system_, "aoc:u"}, add_on_content{AccumulateAOCTitleIDs(system)}, - aoc_change_event{system.Kernel()} { + service_context{system_, "aoc:u"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "CountAddOnContentByApplicationId"}, @@ -126,11 +132,12 @@ AOC_U::AOC_U(Core::System& system_) RegisterHandlers(functions); - Kernel::KAutoObject::Create(std::addressof(aoc_change_event)); - aoc_change_event.Initialize("GetAddOnContentListChanged:Event"); + aoc_change_event = service_context.CreateEvent("GetAddOnContentListChanged:Event"); } -AOC_U::~AOC_U() = default; +AOC_U::~AOC_U() { + service_context.CloseEvent(aoc_change_event); +} void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) { struct Parameters { @@ -254,7 +261,7 @@ void AOC_U::GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(aoc_change_event.GetReadableEvent()); + rb.PushCopyObjects(aoc_change_event->GetReadableEvent()); } void AOC_U::GetAddOnContentListChangedEventWithProcessId(Kernel::HLERequestContext& ctx) { @@ -262,7 +269,7 @@ void AOC_U::GetAddOnContentListChangedEventWithProcessId(Kernel::HLERequestConte IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(aoc_change_event.GetReadableEvent()); + rb.PushCopyObjects(aoc_change_event->GetReadableEvent()); } void AOC_U::CreateEcPurchasedEventManager(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/aoc/aoc_u.h b/src/core/hle/service/aoc/aoc_u.h index bb6ffb8eb..31d645be8 100644 --- a/src/core/hle/service/aoc/aoc_u.h +++ b/src/core/hle/service/aoc/aoc_u.h @@ -4,7 +4,7 @@ #pragma once -#include "core/hle/kernel/k_event.h" +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" namespace Core { @@ -33,7 +33,9 @@ private: void CreatePermanentEcPurchasedEventManager(Kernel::HLERequestContext& ctx); std::vector<u64> add_on_content; - Kernel::KEvent aoc_change_event; + KernelHelpers::ServiceContext service_context; + + Kernel::KEvent* aoc_change_event; }; /// Registers all AOC services with the specified service manager. diff --git a/src/core/hle/service/audio/audctl.cpp b/src/core/hle/service/audio/audctl.cpp index 8c4c49b85..2e46e7161 100644 --- a/src/core/hle/service/audio/audctl.cpp +++ b/src/core/hle/service/audio/audctl.cpp @@ -41,6 +41,14 @@ AudCtl::AudCtl(Core::System& system_) : ServiceFramework{system_, "audctl"} { {27, nullptr, "SetVolumeMappingTableForDev"}, {28, nullptr, "GetAudioOutputChannelCountForPlayReport"}, {29, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"}, + {30, nullptr, "Unknown30"}, + {31, nullptr, "Unknown31"}, + {32, nullptr, "Unknown32"}, + {33, nullptr, "Unknown33"}, + {34, nullptr, "Unknown34"}, + {10000, nullptr, "Unknown10000"}, + {10001, nullptr, "Unknown10001"}, + {10002, nullptr, "Unknown10002"}, }; // clang-format on diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp index 3e7fd6024..34cc659ed 100644 --- a/src/core/hle/service/audio/audin_u.cpp +++ b/src/core/hle/service/audio/audin_u.cpp @@ -3,38 +3,65 @@ // Refer to the license.txt file included. #include "common/logging/log.h" +#include "core/core.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/k_event.h" #include "core/hle/service/audio/audin_u.h" namespace Service::Audio { -class IAudioIn final : public ServiceFramework<IAudioIn> { -public: - explicit IAudioIn(Core::System& system_) : ServiceFramework{system_, "IAudioIn"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "GetAudioInState"}, - {1, nullptr, "Start"}, - {2, nullptr, "Stop"}, - {3, nullptr, "AppendAudioInBuffer"}, - {4, nullptr, "RegisterBufferEvent"}, - {5, nullptr, "GetReleasedAudioInBuffer"}, - {6, nullptr, "ContainsAudioInBuffer"}, - {7, nullptr, "AppendUacInBuffer"}, - {8, nullptr, "AppendAudioInBufferAuto"}, - {9, nullptr, "GetReleasedAudioInBuffersAuto"}, - {10, nullptr, "AppendUacInBufferAuto"}, - {11, nullptr, "GetAudioInBufferCount"}, - {12, nullptr, "SetDeviceGain"}, - {13, nullptr, "GetDeviceGain"}, - {14, nullptr, "FlushAudioInBuffers"}, - }; - // clang-format on - - RegisterHandlers(functions); - } -}; +IAudioIn::IAudioIn(Core::System& system_) + : ServiceFramework{system_, "IAudioIn"}, service_context{system_, "IAudioIn"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetAudioInState"}, + {1, &IAudioIn::Start, "Start"}, + {2, nullptr, "Stop"}, + {3, nullptr, "AppendAudioInBuffer"}, + {4, &IAudioIn::RegisterBufferEvent, "RegisterBufferEvent"}, + {5, nullptr, "GetReleasedAudioInBuffer"}, + {6, nullptr, "ContainsAudioInBuffer"}, + {7, nullptr, "AppendUacInBuffer"}, + {8, &IAudioIn::AppendAudioInBufferAuto, "AppendAudioInBufferAuto"}, + {9, nullptr, "GetReleasedAudioInBuffersAuto"}, + {10, nullptr, "AppendUacInBufferAuto"}, + {11, nullptr, "GetAudioInBufferCount"}, + {12, nullptr, "SetDeviceGain"}, + {13, nullptr, "GetDeviceGain"}, + {14, nullptr, "FlushAudioInBuffers"}, + }; + // clang-format on + + RegisterHandlers(functions); + + buffer_event = service_context.CreateEvent("IAudioIn:BufferEvent"); +} + +IAudioIn::~IAudioIn() { + service_context.CloseEvent(buffer_event); +} + +void IAudioIn::Start(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Audio, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IAudioIn::RegisterBufferEvent(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Audio, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(buffer_event->GetReadableEvent()); +} + +void IAudioIn::AppendAudioInBufferAuto(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Audio, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} AudInU::AudInU(Core::System& system_) : ServiceFramework{system_, "audin:u"} { // clang-format off diff --git a/src/core/hle/service/audio/audin_u.h b/src/core/hle/service/audio/audin_u.h index 0d75ae5ac..bf3418613 100644 --- a/src/core/hle/service/audio/audin_u.h +++ b/src/core/hle/service/audio/audin_u.h @@ -4,6 +4,7 @@ #pragma once +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" namespace Core { @@ -16,6 +17,21 @@ class HLERequestContext; namespace Service::Audio { +class IAudioIn final : public ServiceFramework<IAudioIn> { +public: + explicit IAudioIn(Core::System& system_); + ~IAudioIn() override; + +private: + void Start(Kernel::HLERequestContext& ctx); + void RegisterBufferEvent(Kernel::HLERequestContext& ctx); + void AppendAudioInBufferAuto(Kernel::HLERequestContext& ctx); + + KernelHelpers::ServiceContext service_context; + + Kernel::KEvent* buffer_event; +}; + class AudInU final : public ServiceFramework<AudInU> { public: explicit AudInU(Core::System& system_); diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 92d4510b1..81adbfe09 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -13,13 +13,11 @@ #include "common/swap.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/k_event.h" -#include "core/hle/kernel/k_readable_event.h" -#include "core/hle/kernel/k_writable_event.h" #include "core/hle/kernel/kernel.h" #include "core/hle/service/audio/audout_u.h" #include "core/hle/service/audio/errors.h" +#include "core/hle/service/kernel_helpers.h" #include "core/memory.h" namespace Service::Audio { @@ -41,11 +39,12 @@ enum class AudioState : u32 { class IAudioOut final : public ServiceFramework<IAudioOut> { public: - IAudioOut(Core::System& system_, AudoutParams audio_params_, AudioCore::AudioOut& audio_core_, - std::string&& device_name_, std::string&& unique_name) - : ServiceFramework{system_, "IAudioOut"}, audio_core{audio_core_}, device_name{std::move( - device_name_)}, - audio_params{audio_params_}, buffer_event{system.Kernel()}, main_memory{system.Memory()} { + explicit IAudioOut(Core::System& system_, AudoutParams audio_params_, + AudioCore::AudioOut& audio_core_, std::string&& device_name_, + std::string&& unique_name) + : ServiceFramework{system_, "IAudioOut"}, audio_core{audio_core_}, + device_name{std::move(device_name_)}, audio_params{audio_params_}, + main_memory{system.Memory()}, service_context{system_, "IAudioOut"} { // clang-format off static const FunctionInfo functions[] = { {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, @@ -67,16 +66,19 @@ public: RegisterHandlers(functions); // This is the event handle used to check if the audio buffer was released - Kernel::KAutoObject::Create(std::addressof(buffer_event)); - buffer_event.Initialize("IAudioOutBufferReleased"); + buffer_event = service_context.CreateEvent("IAudioOutBufferReleased"); stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate, audio_params.channel_count, std::move(unique_name), [this] { const auto guard = LockService(); - buffer_event.GetWritableEvent().Signal(); + buffer_event->GetWritableEvent().Signal(); }); } + ~IAudioOut() override { + service_context.CloseEvent(buffer_event); + } + private: struct AudioBuffer { u64_le next; @@ -126,7 +128,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(buffer_event.GetReadableEvent()); + rb.PushCopyObjects(buffer_event->GetReadableEvent()); } void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { @@ -227,9 +229,12 @@ private: [[maybe_unused]] AudoutParams audio_params{}; - /// This is the event handle used to check if the audio buffer was released - Kernel::KEvent buffer_event; Core::Memory::Memory& main_memory; + + KernelHelpers::ServiceContext service_context; + + /// This is the event handle used to check if the audio buffer was released + Kernel::KEvent* buffer_event; }; AudOutU::AudOutU(Core::System& system_) : ServiceFramework{system_, "audout:u"} { diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index b769fe959..cdb2a9521 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -15,10 +15,7 @@ #include "common/string_util.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/k_event.h" -#include "core/hle/kernel/k_readable_event.h" -#include "core/hle/kernel/k_writable_event.h" #include "core/hle/kernel/kernel.h" #include "core/hle/service/audio/audren_u.h" #include "core/hle/service/audio/errors.h" @@ -30,7 +27,7 @@ public: explicit IAudioRenderer(Core::System& system_, const AudioCommon::AudioRendererParameter& audren_params, const std::size_t instance_number) - : ServiceFramework{system_, "IAudioRenderer"}, system_event{system.Kernel()} { + : ServiceFramework{system_, "IAudioRenderer"}, service_context{system_, "IAudioRenderer"} { // clang-format off static const FunctionInfo functions[] = { {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"}, @@ -49,17 +46,20 @@ public: // clang-format on RegisterHandlers(functions); - Kernel::KAutoObject::Create(std::addressof(system_event)); - system_event.Initialize("IAudioRenderer:SystemEvent"); + system_event = service_context.CreateEvent("IAudioRenderer:SystemEvent"); renderer = std::make_unique<AudioCore::AudioRenderer>( system.CoreTiming(), system.Memory(), audren_params, [this]() { const auto guard = LockService(); - system_event.GetWritableEvent().Signal(); + system_event->GetWritableEvent().Signal(); }, instance_number); } + ~IAudioRenderer() override { + service_context.CloseEvent(system_event); + } + private: void GetSampleRate(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); @@ -130,7 +130,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(system_event.GetReadableEvent()); + rb.PushCopyObjects(system_event->GetReadableEvent()); } void SetRenderingTimeLimit(Kernel::HLERequestContext& ctx) { @@ -164,14 +164,16 @@ private: rb.Push(ERR_NOT_SUPPORTED); } - Kernel::KEvent system_event; + KernelHelpers::ServiceContext service_context; + + Kernel::KEvent* system_event; std::unique_ptr<AudioCore::AudioRenderer> renderer; u32 rendering_time_limit_percent = 100; }; class IAudioDevice final : public ServiceFramework<IAudioDevice> { public: - explicit IAudioDevice(Core::System& system_, Kernel::KEvent& buffer_event_, u32_le revision_) + explicit IAudioDevice(Core::System& system_, Kernel::KEvent* buffer_event_, u32_le revision_) : ServiceFramework{system_, "IAudioDevice"}, buffer_event{buffer_event_}, revision{ revision_} { static const FunctionInfo functions[] = { @@ -187,7 +189,8 @@ public: {10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"}, {11, &IAudioDevice::QueryAudioDeviceInputEvent, "QueryAudioDeviceInputEvent"}, {12, &IAudioDevice::QueryAudioDeviceOutputEvent, "QueryAudioDeviceOutputEvent"}, - {13, nullptr, "GetAudioSystemMasterVolumeSetting"}, + {13, nullptr, "GetActiveAudioOutputDeviceName"}, + {14, nullptr, "ListAudioOutputDeviceName"}, }; RegisterHandlers(functions); } @@ -278,11 +281,11 @@ private: void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_Audio, "(STUBBED) called"); - buffer_event.GetWritableEvent().Signal(); + buffer_event->GetWritableEvent().Signal(); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(buffer_event.GetReadableEvent()); + rb.PushCopyObjects(buffer_event->GetReadableEvent()); } void GetActiveChannelCount(Kernel::HLERequestContext& ctx) { @@ -299,7 +302,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(buffer_event.GetReadableEvent()); + rb.PushCopyObjects(buffer_event->GetReadableEvent()); } void QueryAudioDeviceOutputEvent(Kernel::HLERequestContext& ctx) { @@ -307,16 +310,15 @@ private: IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(buffer_event.GetReadableEvent()); + rb.PushCopyObjects(buffer_event->GetReadableEvent()); } - Kernel::KEvent& buffer_event; + Kernel::KEvent* buffer_event; u32_le revision = 0; }; AudRenU::AudRenU(Core::System& system_) - : ServiceFramework{system_, "audren:u"}, buffer_event{system.Kernel()} { - + : ServiceFramework{system_, "audren:u"}, service_context{system_, "audren:u"} { // clang-format off static const FunctionInfo functions[] = { {0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"}, @@ -329,11 +331,12 @@ AudRenU::AudRenU(Core::System& system_) RegisterHandlers(functions); - Kernel::KAutoObject::Create(std::addressof(buffer_event)); - buffer_event.Initialize("IAudioOutBufferReleasedEvent"); + buffer_event = service_context.CreateEvent("IAudioOutBufferReleasedEvent"); } -AudRenU::~AudRenU() = default; +AudRenU::~AudRenU() { + service_context.CloseEvent(buffer_event); +} void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h index 0ee6f9542..5922b4b27 100644 --- a/src/core/hle/service/audio/audren_u.h +++ b/src/core/hle/service/audio/audren_u.h @@ -4,7 +4,7 @@ #pragma once -#include "core/hle/kernel/k_event.h" +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" namespace Core { @@ -31,8 +31,10 @@ private: void OpenAudioRendererImpl(Kernel::HLERequestContext& ctx); + KernelHelpers::ServiceContext service_context; + std::size_t audren_instance_count = 0; - Kernel::KEvent buffer_event; + Kernel::KEvent* buffer_event; }; // Describes a particular audio feature that may be supported in a particular revision. diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index 33a6dbbb6..7da1f2969 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp @@ -13,7 +13,6 @@ #include "common/assert.h" #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/audio/hwopus.h" namespace Service::Audio { diff --git a/src/core/hle/service/bcat/backend/backend.cpp b/src/core/hle/service/bcat/backend/backend.cpp index a78544c88..4c7d3bb6e 100644 --- a/src/core/hle/service/bcat/backend/backend.cpp +++ b/src/core/hle/service/bcat/backend/backend.cpp @@ -5,22 +5,24 @@ #include "common/hex_util.h" #include "common/logging/log.h" #include "core/core.h" -#include "core/hle/kernel/k_readable_event.h" -#include "core/hle/kernel/k_writable_event.h" +#include "core/hle/kernel/k_event.h" #include "core/hle/lock.h" #include "core/hle/service/bcat/backend/backend.h" namespace Service::BCAT { -ProgressServiceBackend::ProgressServiceBackend(Kernel::KernelCore& kernel, - std::string_view event_name) - : update_event{kernel} { - Kernel::KAutoObject::Create(std::addressof(update_event)); - update_event.Initialize("ProgressServiceBackend:UpdateEvent:" + std::string(event_name)); +ProgressServiceBackend::ProgressServiceBackend(Core::System& system, std::string_view event_name) + : service_context{system, "ProgressServiceBackend"} { + update_event = service_context.CreateEvent("ProgressServiceBackend:UpdateEvent:" + + std::string(event_name)); +} + +ProgressServiceBackend::~ProgressServiceBackend() { + service_context.CloseEvent(update_event); } Kernel::KReadableEvent& ProgressServiceBackend::GetEvent() { - return update_event.GetReadableEvent(); + return update_event->GetReadableEvent(); } DeliveryCacheProgressImpl& ProgressServiceBackend::GetImpl() { @@ -88,9 +90,9 @@ void ProgressServiceBackend::FinishDownload(ResultCode result) { void ProgressServiceBackend::SignalUpdate() { if (need_hle_lock) { std::lock_guard lock(HLE::g_hle_lock); - update_event.GetWritableEvent().Signal(); + update_event->GetWritableEvent().Signal(); } else { - update_event.GetWritableEvent().Signal(); + update_event->GetWritableEvent().Signal(); } } diff --git a/src/core/hle/service/bcat/backend/backend.h b/src/core/hle/service/bcat/backend/backend.h index e79a9c2ad..749e046c7 100644 --- a/src/core/hle/service/bcat/backend/backend.h +++ b/src/core/hle/service/bcat/backend/backend.h @@ -11,8 +11,8 @@ #include "common/common_types.h" #include "core/file_sys/vfs_types.h" -#include "core/hle/kernel/k_event.h" #include "core/hle/result.h" +#include "core/hle/service/kernel_helpers.h" namespace Core { class System; @@ -70,6 +70,8 @@ class ProgressServiceBackend { friend class IBcatService; public: + ~ProgressServiceBackend(); + // Clients should call this with true if any of the functions are going to be called from a // non-HLE thread and this class need to lock the hle mutex. (default is false) void SetNeedHLELock(bool need); @@ -97,15 +99,17 @@ public: void FinishDownload(ResultCode result); private: - explicit ProgressServiceBackend(Kernel::KernelCore& kernel, std::string_view event_name); + explicit ProgressServiceBackend(Core::System& system, std::string_view event_name); Kernel::KReadableEvent& GetEvent(); DeliveryCacheProgressImpl& GetImpl(); void SignalUpdate(); + KernelHelpers::ServiceContext service_context; + DeliveryCacheProgressImpl impl{}; - Kernel::KEvent update_event; + Kernel::KEvent* update_event; bool need_hle_lock = false; }; diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp deleted file mode 100644 index 7ca7f2aac..000000000 --- a/src/core/hle/service/bcat/backend/boxcat.cpp +++ /dev/null @@ -1,548 +0,0 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <fmt/ostream.h> - -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wshadow" -#ifndef __clang__ -#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" -#endif -#endif -#include <httplib.h> -#include <mbedtls/sha256.h> -#include <nlohmann/json.hpp> -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif - -#include "common/fs/file.h" -#include "common/fs/fs.h" -#include "common/fs/path_util.h" -#include "common/hex_util.h" -#include "common/logging/log.h" -#include "common/settings.h" -#include "core/core.h" -#include "core/file_sys/vfs.h" -#include "core/file_sys/vfs_libzip.h" -#include "core/file_sys/vfs_vector.h" -#include "core/frontend/applets/error.h" -#include "core/hle/service/am/applets/applets.h" -#include "core/hle/service/bcat/backend/boxcat.h" - -namespace Service::BCAT { -namespace { - -// Prevents conflicts with windows macro called CreateFile -FileSys::VirtualFile VfsCreateFileWrap(FileSys::VirtualDir dir, std::string_view name) { - return dir->CreateFile(name); -} - -// Prevents conflicts with windows macro called DeleteFile -bool VfsDeleteFileWrap(FileSys::VirtualDir dir, std::string_view name) { - return dir->DeleteFile(name); -} - -constexpr ResultCode ERROR_GENERAL_BCAT_FAILURE{ErrorModule::BCAT, 1}; - -constexpr char BOXCAT_HOSTNAME[] = "api.yuzu-emu.org"; - -// Formatted using fmt with arg[0] = hex title id -constexpr char BOXCAT_PATHNAME_DATA[] = "/game-assets/{:016X}/boxcat"; -constexpr char BOXCAT_PATHNAME_LAUNCHPARAM[] = "/game-assets/{:016X}/launchparam"; - -constexpr char BOXCAT_PATHNAME_EVENTS[] = "/game-assets/boxcat/events"; - -constexpr char BOXCAT_API_VERSION[] = "1"; -constexpr char BOXCAT_CLIENT_TYPE[] = "yuzu"; - -// HTTP status codes for Boxcat -enum class ResponseStatus { - Ok = 200, ///< Operation completed successfully. - BadClientVersion = 301, ///< The Boxcat-Client-Version doesn't match the server. - NoUpdate = 304, ///< The digest provided would match the new data, no need to update. - NoMatchTitleId = 404, ///< The title ID provided doesn't have a boxcat implementation. - NoMatchBuildId = 406, ///< The build ID provided is blacklisted (potentially because of format - ///< issues or whatnot) and has no data. -}; - -enum class DownloadResult { - Success = 0, - NoResponse, - GeneralWebError, - NoMatchTitleId, - NoMatchBuildId, - InvalidContentType, - GeneralFSError, - BadClientVersion, -}; - -constexpr std::array<const char*, 8> DOWNLOAD_RESULT_LOG_MESSAGES{ - "Success", - "There was no response from the server.", - "There was a general web error code returned from the server.", - "The title ID of the current game doesn't have a boxcat implementation. If you believe an " - "implementation should be added, contact yuzu support.", - "The build ID of the current version of the game is marked as incompatible with the current " - "BCAT distribution. Try upgrading or downgrading your game version or contacting yuzu support.", - "The content type of the web response was invalid.", - "There was a general filesystem error while saving the zip file.", - "The server is either too new or too old to serve the request. Try using the latest version of " - "an official release of yuzu.", -}; - -std::ostream& operator<<(std::ostream& os, DownloadResult result) { - return os << DOWNLOAD_RESULT_LOG_MESSAGES.at(static_cast<std::size_t>(result)); -} - -constexpr u32 PORT = 443; -constexpr u32 TIMEOUT_SECONDS = 30; -[[maybe_unused]] constexpr u64 VFS_COPY_BLOCK_SIZE = 1ULL << 24; // 4MB - -std::filesystem::path GetBINFilePath(u64 title_id) { - return Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / "bcat" / - fmt::format("{:016X}/launchparam.bin", title_id); -} - -std::filesystem::path GetZIPFilePath(u64 title_id) { - return Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / "bcat" / - fmt::format("{:016X}/data.zip", title_id); -} - -// If the error is something the user should know about (build ID mismatch, bad client version), -// display an error. -void HandleDownloadDisplayResult(const AM::Applets::AppletManager& applet_manager, - DownloadResult res) { - if (res == DownloadResult::Success || res == DownloadResult::NoResponse || - res == DownloadResult::GeneralWebError || res == DownloadResult::GeneralFSError || - res == DownloadResult::NoMatchTitleId || res == DownloadResult::InvalidContentType) { - return; - } - - const auto& frontend{applet_manager.GetAppletFrontendSet()}; - frontend.error->ShowCustomErrorText( - ResultUnknown, "There was an error while attempting to use Boxcat.", - DOWNLOAD_RESULT_LOG_MESSAGES[static_cast<std::size_t>(res)], [] {}); -} - -bool VfsRawCopyProgress(FileSys::VirtualFile src, FileSys::VirtualFile dest, - std::string_view dir_name, ProgressServiceBackend& progress, - std::size_t block_size = 0x1000) { - if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) - return false; - if (!dest->Resize(src->GetSize())) - return false; - - progress.StartDownloadingFile(dir_name, src->GetName(), src->GetSize()); - - std::vector<u8> temp(std::min(block_size, src->GetSize())); - for (std::size_t i = 0; i < src->GetSize(); i += block_size) { - const auto read = std::min(block_size, src->GetSize() - i); - - if (src->Read(temp.data(), read, i) != read) { - return false; - } - - if (dest->Write(temp.data(), read, i) != read) { - return false; - } - - progress.UpdateFileProgress(i); - } - - progress.FinishDownloadingFile(); - - return true; -} - -bool VfsRawCopyDProgressSingle(FileSys::VirtualDir src, FileSys::VirtualDir dest, - ProgressServiceBackend& progress, std::size_t block_size = 0x1000) { - if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) - return false; - - for (const auto& file : src->GetFiles()) { - const auto out_file = VfsCreateFileWrap(dest, file->GetName()); - if (!VfsRawCopyProgress(file, out_file, src->GetName(), progress, block_size)) { - return false; - } - } - progress.CommitDirectory(src->GetName()); - - return true; -} - -bool VfsRawCopyDProgress(FileSys::VirtualDir src, FileSys::VirtualDir dest, - ProgressServiceBackend& progress, std::size_t block_size = 0x1000) { - if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) - return false; - - for (const auto& dir : src->GetSubdirectories()) { - const auto out = dest->CreateSubdirectory(dir->GetName()); - if (!VfsRawCopyDProgressSingle(dir, out, progress, block_size)) { - return false; - } - } - - return true; -} - -} // Anonymous namespace - -class Boxcat::Client { -public: - Client(std::filesystem::path path_, u64 title_id_, u64 build_id_) - : path(std::move(path_)), title_id(title_id_), build_id(build_id_) {} - - DownloadResult DownloadDataZip() { - return DownloadInternal(fmt::format(BOXCAT_PATHNAME_DATA, title_id), TIMEOUT_SECONDS, - "application/zip"); - } - - DownloadResult DownloadLaunchParam() { - return DownloadInternal(fmt::format(BOXCAT_PATHNAME_LAUNCHPARAM, title_id), - TIMEOUT_SECONDS / 3, "application/octet-stream"); - } - -private: - DownloadResult DownloadInternal(const std::string& resolved_path, u32 timeout_seconds, - const std::string& content_type_name) { - if (client == nullptr) { - client = std::make_unique<httplib::SSLClient>(BOXCAT_HOSTNAME, PORT); - client->set_connection_timeout(timeout_seconds); - client->set_read_timeout(timeout_seconds); - client->set_write_timeout(timeout_seconds); - } - - httplib::Headers headers{ - {std::string("Game-Assets-API-Version"), std::string(BOXCAT_API_VERSION)}, - {std::string("Boxcat-Client-Type"), std::string(BOXCAT_CLIENT_TYPE)}, - {std::string("Game-Build-Id"), fmt::format("{:016X}", build_id)}, - }; - - if (Common::FS::Exists(path)) { - Common::FS::IOFile file{path, Common::FS::FileAccessMode::Read, - Common::FS::FileType::BinaryFile}; - if (file.IsOpen()) { - std::vector<u8> bytes(file.GetSize()); - void(file.Read(bytes)); - const auto digest = DigestFile(bytes); - headers.insert({std::string("If-None-Match"), Common::HexToString(digest, false)}); - } - } - - const auto response = client->Get(resolved_path.c_str(), headers); - if (response == nullptr) - return DownloadResult::NoResponse; - - if (response->status == static_cast<int>(ResponseStatus::NoUpdate)) - return DownloadResult::Success; - if (response->status == static_cast<int>(ResponseStatus::BadClientVersion)) - return DownloadResult::BadClientVersion; - if (response->status == static_cast<int>(ResponseStatus::NoMatchTitleId)) - return DownloadResult::NoMatchTitleId; - if (response->status == static_cast<int>(ResponseStatus::NoMatchBuildId)) - return DownloadResult::NoMatchBuildId; - if (response->status != static_cast<int>(ResponseStatus::Ok)) - return DownloadResult::GeneralWebError; - - const auto content_type = response->headers.find("content-type"); - if (content_type == response->headers.end() || - content_type->second.find(content_type_name) == std::string::npos) { - return DownloadResult::InvalidContentType; - } - - if (!Common::FS::CreateDirs(path)) { - return DownloadResult::GeneralFSError; - } - - Common::FS::IOFile file{path, Common::FS::FileAccessMode::Append, - Common::FS::FileType::BinaryFile}; - if (!file.IsOpen()) { - return DownloadResult::GeneralFSError; - } - - if (!file.SetSize(response->body.size())) { - return DownloadResult::GeneralFSError; - } - - if (file.Write(response->body) != response->body.size()) { - return DownloadResult::GeneralFSError; - } - - return DownloadResult::Success; - } - - using Digest = std::array<u8, 0x20>; - static Digest DigestFile(std::vector<u8> bytes) { - Digest out{}; - mbedtls_sha256_ret(bytes.data(), bytes.size(), out.data(), 0); - return out; - } - - std::unique_ptr<httplib::SSLClient> client; - std::filesystem::path path; - u64 title_id; - u64 build_id; -}; - -Boxcat::Boxcat(AM::Applets::AppletManager& applet_manager_, DirectoryGetter getter) - : Backend(std::move(getter)), applet_manager{applet_manager_} {} - -Boxcat::~Boxcat() = default; - -void SynchronizeInternal(AM::Applets::AppletManager& applet_manager, DirectoryGetter dir_getter, - TitleIDVersion title, ProgressServiceBackend& progress, - std::optional<std::string> dir_name = {}) { - progress.SetNeedHLELock(true); - - if (Settings::values.bcat_boxcat_local) { - LOG_INFO(Service_BCAT, "Boxcat using local data by override, skipping download."); - const auto dir = dir_getter(title.title_id); - if (dir) - progress.SetTotalSize(dir->GetSize()); - progress.FinishDownload(ResultSuccess); - return; - } - - const auto zip_path = GetZIPFilePath(title.title_id); - Boxcat::Client client{zip_path, title.title_id, title.build_id}; - - progress.StartConnecting(); - - const auto res = client.DownloadDataZip(); - if (res != DownloadResult::Success) { - LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res); - - if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) { - Common::FS::RemoveFile(zip_path); - } - - HandleDownloadDisplayResult(applet_manager, res); - progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE); - return; - } - - progress.StartProcessingDataList(); - - Common::FS::IOFile zip{zip_path, Common::FS::FileAccessMode::Read, - Common::FS::FileType::BinaryFile}; - const auto size = zip.GetSize(); - std::vector<u8> bytes(size); - if (!zip.IsOpen() || size == 0 || zip.Read(bytes) != bytes.size()) { - LOG_ERROR(Service_BCAT, "Boxcat failed to read ZIP file at path '{}'!", - Common::FS::PathToUTF8String(zip_path)); - progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE); - return; - } - - const auto extracted = FileSys::ExtractZIP(std::make_shared<FileSys::VectorVfsFile>(bytes)); - if (extracted == nullptr) { - LOG_ERROR(Service_BCAT, "Boxcat failed to extract ZIP file!"); - progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE); - return; - } - - if (dir_name == std::nullopt) { - progress.SetTotalSize(extracted->GetSize()); - - const auto target_dir = dir_getter(title.title_id); - if (target_dir == nullptr || !VfsRawCopyDProgress(extracted, target_dir, progress)) { - LOG_ERROR(Service_BCAT, "Boxcat failed to copy extracted ZIP to target directory!"); - progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE); - return; - } - } else { - const auto target_dir = dir_getter(title.title_id); - if (target_dir == nullptr) { - LOG_ERROR(Service_BCAT, "Boxcat failed to get directory for title ID!"); - progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE); - return; - } - - const auto target_sub = target_dir->GetSubdirectory(*dir_name); - const auto source_sub = extracted->GetSubdirectory(*dir_name); - - progress.SetTotalSize(source_sub->GetSize()); - - std::vector<std::string> filenames; - { - const auto files = target_sub->GetFiles(); - std::transform(files.begin(), files.end(), std::back_inserter(filenames), - [](const auto& vfile) { return vfile->GetName(); }); - } - - for (const auto& filename : filenames) { - VfsDeleteFileWrap(target_sub, filename); - } - - if (target_sub == nullptr || source_sub == nullptr || - !VfsRawCopyDProgressSingle(source_sub, target_sub, progress)) { - LOG_ERROR(Service_BCAT, "Boxcat failed to copy extracted ZIP to target directory!"); - progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE); - return; - } - } - - progress.FinishDownload(ResultSuccess); -} - -bool Boxcat::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) { - is_syncing.exchange(true); - - std::thread([this, title, &progress] { - SynchronizeInternal(applet_manager, dir_getter, title, progress); - }).detach(); - - return true; -} - -bool Boxcat::SynchronizeDirectory(TitleIDVersion title, std::string name, - ProgressServiceBackend& progress) { - is_syncing.exchange(true); - - std::thread([this, title, name, &progress] { - SynchronizeInternal(applet_manager, dir_getter, title, progress, name); - }).detach(); - - return true; -} - -bool Boxcat::Clear(u64 title_id) { - if (Settings::values.bcat_boxcat_local) { - LOG_INFO(Service_BCAT, "Boxcat using local data by override, skipping clear."); - return true; - } - - const auto dir = dir_getter(title_id); - - std::vector<std::string> dirnames; - - for (const auto& subdir : dir->GetSubdirectories()) - dirnames.push_back(subdir->GetName()); - - for (const auto& subdir : dirnames) { - if (!dir->DeleteSubdirectoryRecursive(subdir)) - return false; - } - - return true; -} - -void Boxcat::SetPassphrase(u64 title_id, const Passphrase& passphrase) { - LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id, - Common::HexToString(passphrase)); -} - -std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title) { - const auto bin_file_path = GetBINFilePath(title.title_id); - - if (Settings::values.bcat_boxcat_local) { - LOG_INFO(Service_BCAT, "Boxcat using local data by override, skipping download."); - } else { - Client launch_client{bin_file_path, title.title_id, title.build_id}; - - const auto res = launch_client.DownloadLaunchParam(); - if (res != DownloadResult::Success) { - LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res); - - if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) { - Common::FS::RemoveFile(bin_file_path); - } - - HandleDownloadDisplayResult(applet_manager, res); - return std::nullopt; - } - } - - Common::FS::IOFile bin{bin_file_path, Common::FS::FileAccessMode::Read, - Common::FS::FileType::BinaryFile}; - const auto size = bin.GetSize(); - std::vector<u8> bytes(size); - if (!bin.IsOpen() || size == 0 || bin.Read(bytes) != bytes.size()) { - LOG_ERROR(Service_BCAT, "Boxcat failed to read launch parameter binary at path '{}'!", - Common::FS::PathToUTF8String(bin_file_path)); - return std::nullopt; - } - - return bytes; -} - -Boxcat::StatusResult Boxcat::GetStatus(std::optional<std::string>& global, - std::map<std::string, EventStatus>& games) { - httplib::SSLClient client{BOXCAT_HOSTNAME, static_cast<int>(PORT)}; - client.set_connection_timeout(static_cast<int>(TIMEOUT_SECONDS)); - client.set_read_timeout(static_cast<int>(TIMEOUT_SECONDS)); - client.set_write_timeout(static_cast<int>(TIMEOUT_SECONDS)); - - httplib::Headers headers{ - {std::string("Game-Assets-API-Version"), std::string(BOXCAT_API_VERSION)}, - {std::string("Boxcat-Client-Type"), std::string(BOXCAT_CLIENT_TYPE)}, - }; - - if (!client.is_valid()) { - LOG_ERROR(Service_BCAT, "Client is invalid, going offline!"); - return StatusResult::Offline; - } - - if (!client.is_socket_open()) { - LOG_ERROR(Service_BCAT, "Failed to open socket, going offline!"); - return StatusResult::Offline; - } - - const auto response = client.Get(BOXCAT_PATHNAME_EVENTS, headers); - if (response == nullptr) - return StatusResult::Offline; - - if (response->status == static_cast<int>(ResponseStatus::BadClientVersion)) - return StatusResult::BadClientVersion; - - try { - nlohmann::json json = nlohmann::json::parse(response->body); - - if (!json["online"].get<bool>()) - return StatusResult::Offline; - - if (json["global"].is_null()) - global = std::nullopt; - else - global = json["global"].get<std::string>(); - - if (json["games"].is_array()) { - for (const auto& object : json["games"]) { - if (object.is_object() && object.find("name") != object.end()) { - EventStatus detail{}; - if (object["header"].is_string()) { - detail.header = object["header"].get<std::string>(); - } else { - detail.header = std::nullopt; - } - - if (object["footer"].is_string()) { - detail.footer = object["footer"].get<std::string>(); - } else { - detail.footer = std::nullopt; - } - - if (object["events"].is_array()) { - for (const auto& event : object["events"]) { - if (!event.is_string()) - continue; - detail.events.push_back(event.get<std::string>()); - } - } - - games.insert_or_assign(object["name"], std::move(detail)); - } - } - } - - return StatusResult::Success; - } catch (const nlohmann::json::parse_error& error) { - LOG_ERROR(Service_BCAT, "{}", error.what()); - return StatusResult::ParseError; - } -} - -} // namespace Service::BCAT diff --git a/src/core/hle/service/bcat/backend/boxcat.h b/src/core/hle/service/bcat/backend/boxcat.h deleted file mode 100644 index d65b42e58..000000000 --- a/src/core/hle/service/bcat/backend/boxcat.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <atomic> -#include <map> -#include <optional> -#include "core/hle/service/bcat/backend/backend.h" - -namespace Service::AM::Applets { -class AppletManager; -} - -namespace Service::BCAT { - -struct EventStatus { - std::optional<std::string> header; - std::optional<std::string> footer; - std::vector<std::string> events; -}; - -/// Boxcat is yuzu's custom backend implementation of Nintendo's BCAT service. It is free to use and -/// doesn't require a switch or nintendo account. The content is controlled by the yuzu team. -class Boxcat final : public Backend { - friend void SynchronizeInternal(AM::Applets::AppletManager& applet_manager, - DirectoryGetter dir_getter, TitleIDVersion title, - ProgressServiceBackend& progress, - std::optional<std::string> dir_name); - -public: - explicit Boxcat(AM::Applets::AppletManager& applet_manager_, DirectoryGetter getter); - ~Boxcat() override; - - bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) override; - bool SynchronizeDirectory(TitleIDVersion title, std::string name, - ProgressServiceBackend& progress) override; - - bool Clear(u64 title_id) override; - - void SetPassphrase(u64 title_id, const Passphrase& passphrase) override; - - std::optional<std::vector<u8>> GetLaunchParameter(TitleIDVersion title) override; - - enum class StatusResult { - Success, - Offline, - ParseError, - BadClientVersion, - }; - - static StatusResult GetStatus(std::optional<std::string>& global, - std::map<std::string, EventStatus>& games); - -private: - std::atomic_bool is_syncing{false}; - - class Client; - std::unique_ptr<Client> client; - AM::Applets::AppletManager& applet_manager; -}; - -} // namespace Service::BCAT diff --git a/src/core/hle/service/bcat/bcat_module.cpp b/src/core/hle/service/bcat/bcat_module.cpp index 72294eb2e..27e9b8df8 100644 --- a/src/core/hle/service/bcat/bcat_module.cpp +++ b/src/core/hle/service/bcat/bcat_module.cpp @@ -4,7 +4,6 @@ #include <cctype> #include <mbedtls/md5.h> -#include "backend/boxcat.h" #include "common/hex_util.h" #include "common/logging/log.h" #include "common/settings.h" @@ -128,8 +127,8 @@ public: explicit IBcatService(Core::System& system_, Backend& backend_) : ServiceFramework{system_, "IBcatService"}, backend{backend_}, progress{{ - ProgressServiceBackend{system_.Kernel(), "Normal"}, - ProgressServiceBackend{system_.Kernel(), "Directory"}, + ProgressServiceBackend{system_, "Normal"}, + ProgressServiceBackend{system_, "Directory"}, }} { // clang-format off static const FunctionInfo functions[] = { @@ -578,12 +577,6 @@ void Module::Interface::CreateDeliveryCacheStorageServiceWithApplicationId( std::unique_ptr<Backend> CreateBackendFromSettings([[maybe_unused]] Core::System& system, DirectoryGetter getter) { -#ifdef YUZU_ENABLE_BOXCAT - if (Settings::values.bcat_backend.GetValue() == "boxcat") { - return std::make_unique<Boxcat>(system.GetAppletManager(), std::move(getter)); - } -#endif - return std::make_unique<NullBackend>(std::move(getter)); } diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp index 46da438ef..088a1a18a 100644 --- a/src/core/hle/service/btdrv/btdrv.cpp +++ b/src/core/hle/service/btdrv/btdrv.cpp @@ -5,11 +5,10 @@ #include "common/logging/log.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/k_event.h" -#include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/kernel.h" #include "core/hle/service/btdrv/btdrv.h" +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" @@ -18,7 +17,7 @@ namespace Service::BtDrv { class Bt final : public ServiceFramework<Bt> { public: explicit Bt(Core::System& system_) - : ServiceFramework{system_, "bt"}, register_event{system.Kernel()} { + : ServiceFramework{system_, "bt"}, service_context{system_, "bt"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "LeClientReadCharacteristic"}, @@ -35,8 +34,11 @@ public: // clang-format on RegisterHandlers(functions); - Kernel::KAutoObject::Create(std::addressof(register_event)); - register_event.Initialize("BT:RegisterEvent"); + register_event = service_context.CreateEvent("BT:RegisterEvent"); + } + + ~Bt() override { + service_context.CloseEvent(register_event); } private: @@ -45,10 +47,12 @@ private: IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(register_event.GetReadableEvent()); + rb.PushCopyObjects(register_event->GetReadableEvent()); } - Kernel::KEvent register_event; + KernelHelpers::ServiceContext service_context; + + Kernel::KEvent* register_event; }; class BtDrv final : public ServiceFramework<BtDrv> { @@ -175,6 +179,10 @@ public: {143, nullptr, "GetAudioControlInputState"}, {144, nullptr, "AcquireAudioConnectionStateChangedEvent"}, {145, nullptr, "GetConnectedAudioDevice"}, + {146, nullptr, "CloseAudioControlInput"}, + {147, nullptr, "RegisterAudioControlNotification"}, + {148, nullptr, "SendAudioControlPassthroughCommand"}, + {149, nullptr, "SendAudioControlSetAbsoluteVolumeCommand"}, {256, nullptr, "IsManufacturingMode"}, {257, nullptr, "EmulateBluetoothCrash"}, {258, nullptr, "GetBleChannelMap"}, diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp index 3ab29036a..7aabacc19 100644 --- a/src/core/hle/service/btm/btm.cpp +++ b/src/core/hle/service/btm/btm.cpp @@ -7,11 +7,10 @@ #include "common/logging/log.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/k_event.h" -#include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/kernel.h" #include "core/hle/service/btm/btm.h" +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" namespace Service::BTM { @@ -19,9 +18,7 @@ namespace Service::BTM { class IBtmUserCore final : public ServiceFramework<IBtmUserCore> { public: explicit IBtmUserCore(Core::System& system_) - : ServiceFramework{system_, "IBtmUserCore"}, scan_event{system.Kernel()}, - connection_event{system.Kernel()}, service_discovery{system.Kernel()}, - config_event{system.Kernel()} { + : ServiceFramework{system_, "IBtmUserCore"}, service_context{system_, "IBtmUserCore"} { // clang-format off static const FunctionInfo functions[] = { {0, &IBtmUserCore::AcquireBleScanEvent, "AcquireBleScanEvent"}, @@ -60,15 +57,17 @@ public: // clang-format on RegisterHandlers(functions); - Kernel::KAutoObject::Create(std::addressof(scan_event)); - Kernel::KAutoObject::Create(std::addressof(connection_event)); - Kernel::KAutoObject::Create(std::addressof(service_discovery)); - Kernel::KAutoObject::Create(std::addressof(config_event)); + scan_event = service_context.CreateEvent("IBtmUserCore:ScanEvent"); + connection_event = service_context.CreateEvent("IBtmUserCore:ConnectionEvent"); + service_discovery_event = service_context.CreateEvent("IBtmUserCore:DiscoveryEvent"); + config_event = service_context.CreateEvent("IBtmUserCore:ConfigEvent"); + } - scan_event.Initialize("IBtmUserCore:ScanEvent"); - connection_event.Initialize("IBtmUserCore:ConnectionEvent"); - service_discovery.Initialize("IBtmUserCore:Discovery"); - config_event.Initialize("IBtmUserCore:ConfigEvent"); + ~IBtmUserCore() override { + service_context.CloseEvent(scan_event); + service_context.CloseEvent(connection_event); + service_context.CloseEvent(service_discovery_event); + service_context.CloseEvent(config_event); } private: @@ -77,7 +76,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(scan_event.GetReadableEvent()); + rb.PushCopyObjects(scan_event->GetReadableEvent()); } void AcquireBleConnectionEvent(Kernel::HLERequestContext& ctx) { @@ -85,7 +84,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(connection_event.GetReadableEvent()); + rb.PushCopyObjects(connection_event->GetReadableEvent()); } void AcquireBleServiceDiscoveryEvent(Kernel::HLERequestContext& ctx) { @@ -93,7 +92,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(service_discovery.GetReadableEvent()); + rb.PushCopyObjects(service_discovery_event->GetReadableEvent()); } void AcquireBleMtuConfigEvent(Kernel::HLERequestContext& ctx) { @@ -101,13 +100,15 @@ private: IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(config_event.GetReadableEvent()); + rb.PushCopyObjects(config_event->GetReadableEvent()); } - Kernel::KEvent scan_event; - Kernel::KEvent connection_event; - Kernel::KEvent service_discovery; - Kernel::KEvent config_event; + KernelHelpers::ServiceContext service_context; + + Kernel::KEvent* scan_event; + Kernel::KEvent* connection_event; + Kernel::KEvent* service_discovery_event; + Kernel::KEvent* config_event; }; class BTM_USR final : public ServiceFramework<BTM_USR> { diff --git a/src/core/hle/service/caps/caps.h b/src/core/hle/service/caps/caps.h index 3c4290c88..b18adcb9d 100644 --- a/src/core/hle/service/caps/caps.h +++ b/src/core/hle/service/caps/caps.h @@ -4,7 +4,8 @@ #pragma once -#include "core/hle/service/service.h" +#include "common/common_funcs.h" +#include "common/common_types.h" namespace Core { class System; diff --git a/src/core/hle/service/caps/caps_ss.cpp b/src/core/hle/service/caps/caps_ss.cpp index 2b5314691..33a976ddf 100644 --- a/src/core/hle/service/caps/caps_ss.cpp +++ b/src/core/hle/service/caps/caps_ss.cpp @@ -15,6 +15,7 @@ CAPS_SS::CAPS_SS(Core::System& system_) : ServiceFramework{system_, "caps:ss"} { {204, nullptr, "SaveEditedScreenShotEx0"}, {206, nullptr, "Unknown206"}, {208, nullptr, "SaveScreenShotOfMovieEx1"}, + {1000, nullptr, "Unknown1000"}, }; // clang-format on diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp index 110c7cb1c..f6184acc9 100644 --- a/src/core/hle/service/es/es.cpp +++ b/src/core/hle/service/es/es.cpp @@ -55,6 +55,8 @@ public: {36, nullptr, "DeleteAllInactiveELicenseRequiredPersonalizedTicket"}, {37, nullptr, "OwnTicket2"}, {38, nullptr, "OwnTicket3"}, + {39, nullptr, "DeleteAllInactivePersonalizedTicket"}, + {40, nullptr, "DeletePrepurchaseRecordByNintendoAccountId"}, {501, nullptr, "Unknown501"}, {502, nullptr, "Unknown502"}, {503, nullptr, "GetTitleKey"}, @@ -88,11 +90,15 @@ public: {1503, nullptr, "Unknown1503"}, {1504, nullptr, "Unknown1504"}, {1505, nullptr, "Unknown1505"}, + {1506, nullptr, "Unknown1506"}, {2000, nullptr, "Unknown2000"}, {2001, nullptr, "Unknown2001"}, + {2002, nullptr, "Unknown2002"}, + {2003, nullptr, "Unknown2003"}, {2100, nullptr, "Unknown2100"}, {2501, nullptr, "Unknown2501"}, {2502, nullptr, "Unknown2502"}, + {2601, nullptr, "Unknown2601"}, {3001, nullptr, "Unknown3001"}, {3002, nullptr, "Unknown3002"}, }; diff --git a/src/core/hle/service/fgm/fgm.cpp b/src/core/hle/service/fgm/fgm.cpp index 25c6c0194..d7a638f96 100644 --- a/src/core/hle/service/fgm/fgm.cpp +++ b/src/core/hle/service/fgm/fgm.cpp @@ -5,7 +5,6 @@ #include <memory> #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/fgm/fgm.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" diff --git a/src/core/hle/service/filesystem/fsp_ldr.cpp b/src/core/hle/service/filesystem/fsp_ldr.cpp index 1f6c17ba5..f112ae9d0 100644 --- a/src/core/hle/service/filesystem/fsp_ldr.cpp +++ b/src/core/hle/service/filesystem/fsp_ldr.cpp @@ -3,7 +3,6 @@ // Refer to the license.txt file included. #include "core/hle/service/filesystem/fsp_ldr.h" -#include "core/hle/service/service.h" namespace Service::FileSystem { diff --git a/src/core/hle/service/filesystem/fsp_pr.cpp b/src/core/hle/service/filesystem/fsp_pr.cpp index 00e4d1662..9b7f7d861 100644 --- a/src/core/hle/service/filesystem/fsp_pr.cpp +++ b/src/core/hle/service/filesystem/fsp_pr.cpp @@ -3,7 +3,6 @@ // Refer to the license.txt file included. #include "core/hle/service/filesystem/fsp_pr.h" -#include "core/hle/service/service.h" namespace Service::FileSystem { diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index b58c152ce..68c9240ae 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp @@ -8,11 +8,10 @@ #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_event.h" -#include "core/hle/kernel/k_readable_event.h" -#include "core/hle/kernel/k_writable_event.h" #include "core/hle/service/friend/errors.h" #include "core/hle/service/friend/friend.h" #include "core/hle/service/friend/friend_interface.h" +#include "core/hle/service/kernel_helpers.h" namespace Service::Friend { @@ -184,9 +183,9 @@ private: class INotificationService final : public ServiceFramework<INotificationService> { public: - explicit INotificationService(Common::UUID uuid_, Core::System& system_) - : ServiceFramework{system_, "INotificationService"}, uuid{uuid_}, notification_event{ - system.Kernel()} { + explicit INotificationService(Core::System& system_, Common::UUID uuid_) + : ServiceFramework{system_, "INotificationService"}, uuid{uuid_}, + service_context{system_, "INotificationService"} { // clang-format off static const FunctionInfo functions[] = { {0, &INotificationService::GetEvent, "GetEvent"}, @@ -197,8 +196,11 @@ public: RegisterHandlers(functions); - Kernel::KAutoObject::Create(std::addressof(notification_event)); - notification_event.Initialize("INotificationService:NotifyEvent"); + notification_event = service_context.CreateEvent("INotificationService:NotifyEvent"); + } + + ~INotificationService() override { + service_context.CloseEvent(notification_event); } private: @@ -207,7 +209,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(notification_event.GetReadableEvent()); + rb.PushCopyObjects(notification_event->GetReadableEvent()); } void Clear(Kernel::HLERequestContext& ctx) { @@ -272,8 +274,10 @@ private: bool has_received_friend_request; }; - Common::UUID uuid{Common::INVALID_UUID}; - Kernel::KEvent notification_event; + Common::UUID uuid; + KernelHelpers::ServiceContext service_context; + + Kernel::KEvent* notification_event; std::queue<SizedNotificationInfo> notifications; States states{}; }; @@ -293,7 +297,7 @@ void Module::Interface::CreateNotificationService(Kernel::HLERequestContext& ctx IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface<INotificationService>(uuid, system); + rb.PushIpcInterface<INotificationService>(system, uuid); } Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_, diff --git a/src/core/hle/service/glue/arp.cpp b/src/core/hle/service/glue/arp.cpp index 5a3b54cc1..70cd63c6b 100644 --- a/src/core/hle/service/glue/arp.cpp +++ b/src/core/hle/service/glue/arp.cpp @@ -8,13 +8,11 @@ #include "core/core.h" #include "core/file_sys/control_metadata.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/kernel.h" #include "core/hle/service/glue/arp.h" #include "core/hle/service/glue/errors.h" #include "core/hle/service/glue/glue_manager.h" -#include "core/hle/service/service.h" namespace Service::Glue { diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 4fcc6f93a..9ee146caf 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -507,6 +507,7 @@ private: LarkNesRight = 18, Lucia = 19, Verification = 20, + Lagon = 21, }; struct NPadEntry { diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index a1707a72a..043320d50 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -8,12 +8,9 @@ #include "common/settings.h" #include "core/core.h" #include "core/core_timing.h" -#include "core/core_timing_util.h" #include "core/frontend/emu_window.h" #include "core/frontend/input.h" -#include "core/hardware_properties.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/k_client_port.h" #include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_shared_memory.h" #include "core/hle/kernel/k_transfer_memory.h" @@ -23,7 +20,6 @@ #include "core/hle/service/hid/hid.h" #include "core/hle/service/hid/irs.h" #include "core/hle/service/hid/xcd.h" -#include "core/hle/service/service.h" #include "core/memory.h" #include "core/hle/service/hid/controllers/console_sixaxis.h" @@ -106,7 +102,7 @@ void IAppletResource::DeactivateController(HidController controller) { controllers[static_cast<size_t>(controller)]->DeactivateController(); } -IAppletResource ::~IAppletResource() { +IAppletResource::~IAppletResource() { system.CoreTiming().UnscheduleEvent(pad_update_event, 0); system.CoreTiming().UnscheduleEvent(motion_update_event, 0); } @@ -239,6 +235,12 @@ Hid::Hid(Core::System& system_) {81, &Hid::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"}, {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, {83, &Hid::IsFirmwareUpdateAvailableForSixAxisSensor, "IsFirmwareUpdateAvailableForSixAxisSensor"}, + {84, nullptr, "EnableSixAxisSensorUnalteredPassthrough"}, + {85, nullptr, "IsSixAxisSensorUnalteredPassthroughEnabled"}, + {86, nullptr, "StoreSixAxisSensorCalibrationParameter"}, + {87, nullptr, "LoadSixAxisSensorCalibrationParameter"}, + {88, nullptr, "GetSixAxisSensorIcInformation"}, + {89, nullptr, "ResetIsSixAxisSensorDeviceNewlyAssigned"}, {91, &Hid::ActivateGesture, "ActivateGesture"}, {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, @@ -1656,6 +1658,9 @@ public: {12, nullptr, "UnsetTouchScreenAutoPilotState"}, {13, nullptr, "GetTouchScreenConfiguration"}, {14, nullptr, "ProcessTouchScreenAutoTune"}, + {15, nullptr, "ForceStopTouchScreenManagement"}, + {16, nullptr, "ForceRestartTouchScreenManagement"}, + {17, nullptr, "IsTouchScreenManaged"}, {20, nullptr, "DeactivateMouse"}, {21, nullptr, "SetMouseAutoPilotState"}, {22, nullptr, "UnsetMouseAutoPilotState"}, diff --git a/src/core/hle/service/lbl/lbl.cpp b/src/core/hle/service/lbl/lbl.cpp index 24890c830..5c8ae029c 100644 --- a/src/core/hle/service/lbl/lbl.cpp +++ b/src/core/hle/service/lbl/lbl.cpp @@ -2,11 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cmath> #include <memory> #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/lbl/lbl.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp index 9d863486a..0b907824d 100644 --- a/src/core/hle/service/mii/mii.cpp +++ b/src/core/hle/service/mii/mii.cpp @@ -6,7 +6,6 @@ #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/mii/mii.h" #include "core/hle/service/mii/mii_manager.h" #include "core/hle/service/service.h" diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp index b014ea826..f77037842 100644 --- a/src/core/hle/service/nfc/nfc.cpp +++ b/src/core/hle/service/nfc/nfc.cpp @@ -7,7 +7,6 @@ #include "common/logging/log.h" #include "common/settings.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/nfc/nfc.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 5f1ca029d..6791f20a5 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp @@ -8,9 +8,8 @@ #include "common/logging/log.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/k_readable_event.h" +#include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_thread.h" -#include "core/hle/kernel/k_writable_event.h" #include "core/hle/kernel/kernel.h" #include "core/hle/lock.h" #include "core/hle/service/nfp/nfp.h" @@ -23,18 +22,21 @@ constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152); Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_, const char* name) - : ServiceFramework{system_, name}, nfc_tag_load{system.Kernel()}, module{std::move(module_)} { - Kernel::KAutoObject::Create(std::addressof(nfc_tag_load)); - nfc_tag_load.Initialize("IUser:NFCTagDetected"); + : ServiceFramework{system_, name}, module{std::move(module_)}, service_context{system_, + "NFP::IUser"} { + nfc_tag_load = service_context.CreateEvent("NFP::IUser:NFCTagDetected"); } -Module::Interface::~Interface() = default; +Module::Interface::~Interface() { + service_context.CloseEvent(nfc_tag_load); +} class IUser final : public ServiceFramework<IUser> { public: - explicit IUser(Module::Interface& nfp_interface_, Core::System& system_) + explicit IUser(Module::Interface& nfp_interface_, Core::System& system_, + KernelHelpers::ServiceContext& service_context_) : ServiceFramework{system_, "NFP::IUser"}, nfp_interface{nfp_interface_}, - deactivate_event{system.Kernel()}, availability_change_event{system.Kernel()} { + service_context{service_context_} { static const FunctionInfo functions[] = { {0, &IUser::Initialize, "Initialize"}, {1, &IUser::Finalize, "Finalize"}, @@ -64,11 +66,14 @@ public: }; RegisterHandlers(functions); - Kernel::KAutoObject::Create(std::addressof(deactivate_event)); - Kernel::KAutoObject::Create(std::addressof(availability_change_event)); + deactivate_event = service_context.CreateEvent("NFP::IUser:DeactivateEvent"); + availability_change_event = + service_context.CreateEvent("NFP::IUser:AvailabilityChangeEvent"); + } - deactivate_event.Initialize("IUser:DeactivateEvent"); - availability_change_event.Initialize("IUser:AvailabilityChangeEvent"); + ~IUser() override { + service_context.CloseEvent(deactivate_event); + service_context.CloseEvent(availability_change_event); } private: @@ -166,7 +171,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(deactivate_event.GetReadableEvent()); + rb.PushCopyObjects(deactivate_event->GetReadableEvent()); } void StopDetection(Kernel::HLERequestContext& ctx) { @@ -175,7 +180,7 @@ private: switch (device_state) { case DeviceState::TagFound: case DeviceState::TagNearby: - deactivate_event.GetWritableEvent().Signal(); + deactivate_event->GetWritableEvent().Signal(); device_state = DeviceState::Initialized; break; case DeviceState::SearchingForTag: @@ -264,7 +269,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(availability_change_event.GetReadableEvent()); + rb.PushCopyObjects(availability_change_event->GetReadableEvent()); } void GetRegisterInfo(Kernel::HLERequestContext& ctx) { @@ -313,14 +318,16 @@ private: rb.PushRaw<u32>(0); // This is from the GetCommonInfo stub } + Module::Interface& nfp_interface; + KernelHelpers::ServiceContext& service_context; + bool has_attached_handle{}; const u64 device_handle{0}; // Npad device 1 const u32 npad_id{0}; // Player 1 controller State state{State::NonInitialized}; DeviceState device_state{DeviceState::Initialized}; - Module::Interface& nfp_interface; - Kernel::KEvent deactivate_event; - Kernel::KEvent availability_change_event; + Kernel::KEvent* deactivate_event; + Kernel::KEvent* availability_change_event; }; void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { @@ -328,7 +335,7 @@ void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface<IUser>(*this, system); + rb.PushIpcInterface<IUser>(*this, system, service_context); } bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) { @@ -338,12 +345,12 @@ bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) { } std::memcpy(&amiibo, buffer.data(), sizeof(amiibo)); - nfc_tag_load.GetWritableEvent().Signal(); + nfc_tag_load->GetWritableEvent().Signal(); return true; } Kernel::KReadableEvent& Module::Interface::GetNFCEvent() { - return nfc_tag_load.GetReadableEvent(); + return nfc_tag_load->GetReadableEvent(); } const Module::Interface::AmiiboFile& Module::Interface::GetAmiiboBuffer() const { diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h index 5e4e49bc6..95c127efb 100644 --- a/src/core/hle/service/nfp/nfp.h +++ b/src/core/hle/service/nfp/nfp.h @@ -7,7 +7,7 @@ #include <array> #include <vector> -#include "core/hle/kernel/k_event.h" +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" namespace Kernel { @@ -42,12 +42,13 @@ public: Kernel::KReadableEvent& GetNFCEvent(); const AmiiboFile& GetAmiiboBuffer() const; - private: - Kernel::KEvent nfc_tag_load; - AmiiboFile amiibo{}; - protected: std::shared_ptr<Module> module; + + private: + KernelHelpers::ServiceContext service_context; + Kernel::KEvent* nfc_tag_load; + AmiiboFile amiibo{}; }; }; diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index 9decb9290..f13dc8b0d 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -6,10 +6,21 @@ #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_event.h" -#include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/nifm/nifm.h" #include "core/hle/service/service.h" + +namespace { + +// Avoids name conflict with Windows' CreateEvent macro. +[[nodiscard]] Kernel::KEvent* CreateKEvent(Service::KernelHelpers::ServiceContext& service_context, + std::string&& name) { + return service_context.CreateEvent(std::move(name)); +} + +} // Anonymous namespace + #include "core/network/network.h" #include "core/network/network_interface.h" @@ -129,7 +140,7 @@ public: class IRequest final : public ServiceFramework<IRequest> { public: explicit IRequest(Core::System& system_) - : ServiceFramework{system_, "IRequest"}, event1{system.Kernel()}, event2{system.Kernel()} { + : ServiceFramework{system_, "IRequest"}, service_context{system_, "IRequest"} { static const FunctionInfo functions[] = { {0, &IRequest::GetRequestState, "GetRequestState"}, {1, &IRequest::GetResult, "GetResult"}, @@ -159,11 +170,13 @@ public: }; RegisterHandlers(functions); - Kernel::KAutoObject::Create(std::addressof(event1)); - Kernel::KAutoObject::Create(std::addressof(event2)); + event1 = CreateKEvent(service_context, "IRequest:Event1"); + event2 = CreateKEvent(service_context, "IRequest:Event2"); + } - event1.Initialize("IRequest:Event1"); - event2.Initialize("IRequest:Event2"); + ~IRequest() override { + service_context.CloseEvent(event1); + service_context.CloseEvent(event2); } private: @@ -199,7 +212,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 2}; rb.Push(ResultSuccess); - rb.PushCopyObjects(event1.GetReadableEvent(), event2.GetReadableEvent()); + rb.PushCopyObjects(event1->GetReadableEvent(), event2->GetReadableEvent()); } void Cancel(Kernel::HLERequestContext& ctx) { @@ -230,7 +243,10 @@ private: rb.Push<u32>(0); } - Kernel::KEvent event1, event2; + KernelHelpers::ServiceContext service_context; + + Kernel::KEvent* event1; + Kernel::KEvent* event2; }; class INetworkProfile final : public ServiceFramework<INetworkProfile> { diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp index 7447cc38f..30fb060b8 100644 --- a/src/core/hle/service/nim/nim.cpp +++ b/src/core/hle/service/nim/nim.cpp @@ -7,9 +7,8 @@ #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_event.h" -#include "core/hle/kernel/k_readable_event.h" -#include "core/hle/kernel/k_writable_event.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/nim/nim.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" @@ -301,7 +300,7 @@ class IEnsureNetworkClockAvailabilityService final public: explicit IEnsureNetworkClockAvailabilityService(Core::System& system_) : ServiceFramework{system_, "IEnsureNetworkClockAvailabilityService"}, - finished_event{system.Kernel()} { + service_context{system_, "IEnsureNetworkClockAvailabilityService"} { static const FunctionInfo functions[] = { {0, &IEnsureNetworkClockAvailabilityService::StartTask, "StartTask"}, {1, &IEnsureNetworkClockAvailabilityService::GetFinishNotificationEvent, @@ -313,17 +312,19 @@ public: }; RegisterHandlers(functions); - Kernel::KAutoObject::Create(std::addressof(finished_event)); - finished_event.Initialize("IEnsureNetworkClockAvailabilityService:FinishEvent"); + finished_event = + service_context.CreateEvent("IEnsureNetworkClockAvailabilityService:FinishEvent"); } -private: - Kernel::KEvent finished_event; + ~IEnsureNetworkClockAvailabilityService() override { + service_context.CloseEvent(finished_event); + } +private: void StartTask(Kernel::HLERequestContext& ctx) { // No need to connect to the internet, just finish the task straight away. LOG_DEBUG(Service_NIM, "called"); - finished_event.GetWritableEvent().Signal(); + finished_event->GetWritableEvent().Signal(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } @@ -333,7 +334,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(finished_event.GetReadableEvent()); + rb.PushCopyObjects(finished_event->GetReadableEvent()); } void GetResult(Kernel::HLERequestContext& ctx) { @@ -345,7 +346,7 @@ private: void Cancel(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NIM, "called"); - finished_event.GetWritableEvent().Clear(); + finished_event->GetWritableEvent().Clear(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } @@ -368,6 +369,10 @@ private: rb.Push(ResultSuccess); rb.PushRaw<s64>(server_time); } + + KernelHelpers::ServiceContext service_context; + + Kernel::KEvent* finished_event; }; class NTC final : public ServiceFramework<NTC> { diff --git a/src/core/hle/service/npns/npns.cpp b/src/core/hle/service/npns/npns.cpp index e4c703da4..32533cd94 100644 --- a/src/core/hle/service/npns/npns.cpp +++ b/src/core/hle/service/npns/npns.cpp @@ -31,6 +31,7 @@ public: {24, nullptr, "DestroyTokenWithApplicationId"}, {25, nullptr, "QueryIsTokenValid"}, {26, nullptr, "ListenToMyApplicationId"}, + {27, nullptr, "DestroyTokenAll"}, {31, nullptr, "UploadTokenToBaaS"}, {32, nullptr, "DestroyTokenForBaaS"}, {33, nullptr, "CreateTokenForBaaS"}, diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index 8ce1f3296..931b48f72 100644 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp @@ -9,7 +9,6 @@ #include "core/file_sys/patch_manager.h" #include "core/file_sys/vfs.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/ns/errors.h" #include "core/hle/service/ns/language.h" #include "core/hle/service/ns/ns.h" diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index a33e47d0b..4ee8c5733 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -16,7 +16,7 @@ namespace Service::Nvidia::Devices { nvdisp_disp0::nvdisp_disp0(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_) : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)} {} -nvdisp_disp0 ::~nvdisp_disp0() = default; +nvdisp_disp0::~nvdisp_disp0() = default; NvResult nvdisp_disp0::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { @@ -48,8 +48,9 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3 addr, offset, width, height, stride, format); const auto pixel_format = static_cast<Tegra::FramebufferConfig::PixelFormat>(format); - const Tegra::FramebufferConfig framebuffer{addr, offset, width, height, - stride, pixel_format, transform, crop_rect}; + const auto transform_flags = static_cast<Tegra::FramebufferConfig::TransformFlags>(transform); + const Tegra::FramebufferConfig framebuffer{addr, offset, width, height, + stride, pixel_format, transform_flags, crop_rect}; system.GetPerfStats().EndSystemFrame(); system.GPU().SwapBuffers(&framebuffer); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index 775e76330..8b4867ca7 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -111,7 +111,6 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector event.event->GetWritableEvent().Signal(); return NvResult::Success; } - auto lock = gpu.LockSync(); const u32 current_syncpoint_value = event.fence.value; const s32 diff = current_syncpoint_value - params.threshold; if (diff >= 0) { @@ -132,23 +131,24 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector } EventState status = events_interface.status[event_id]; - if (event_id < MaxNvEvents || status == EventState::Free || status == EventState::Registered) { - events_interface.SetEventStatus(event_id, EventState::Waiting); - events_interface.assigned_syncpt[event_id] = params.syncpt_id; - events_interface.assigned_value[event_id] = target_value; - if (is_async) { - params.value = params.syncpt_id << 4; - } else { - params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000; - } - params.value |= event_id; - event.event->GetWritableEvent().Clear(); - gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value); + const bool bad_parameter = status != EventState::Free && status != EventState::Registered; + if (bad_parameter) { std::memcpy(output.data(), ¶ms, sizeof(params)); - return NvResult::Timeout; + return NvResult::BadParameter; } + events_interface.SetEventStatus(event_id, EventState::Waiting); + events_interface.assigned_syncpt[event_id] = params.syncpt_id; + events_interface.assigned_value[event_id] = target_value; + if (is_async) { + params.value = params.syncpt_id << 4; + } else { + params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000; + } + params.value |= event_id; + event.event->GetWritableEvent().Clear(); + gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value); std::memcpy(output.data(), ¶ms, sizeof(params)); - return NvResult::BadParameter; + return NvResult::Timeout; } NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index c0a380088..54ac105d5 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -13,6 +13,14 @@ #include "video_core/memory_manager.h" namespace Service::Nvidia::Devices { +namespace { +Tegra::CommandHeader BuildFenceAction(Tegra::GPU::FenceOperation op, u32 syncpoint_id) { + Tegra::GPU::FenceAction result{}; + result.op.Assign(op); + result.syncpoint_id.Assign(syncpoint_id); + return {result.raw}; +} +} // namespace nvhost_gpu::nvhost_gpu(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, SyncpointManager& syncpoint_manager_) @@ -187,7 +195,7 @@ static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) { {fence.value}, Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1, Tegra::SubmissionMode::Increasing), - Tegra::GPU::FenceAction::Build(Tegra::GPU::FenceOperation::Acquire, fence.id), + BuildFenceAction(Tegra::GPU::FenceOperation::Acquire, fence.id), }; } @@ -200,8 +208,7 @@ static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(Fence fence, for (u32 count = 0; count < add_increment; ++count) { result.emplace_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1, Tegra::SubmissionMode::Increasing)); - result.emplace_back( - Tegra::GPU::FenceAction::Build(Tegra::GPU::FenceOperation::Increment, fence.id)); + result.emplace_back(BuildFenceAction(Tegra::GPU::FenceOperation::Increment, fence.id)); } return result; diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 3ead813b0..a22811ec1 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -13,28 +13,20 @@ #include "common/thread.h" #include "core/core.h" #include "core/core_timing.h" -#include "core/core_timing_util.h" -#include "core/hardware_properties.h" #include "core/hle/kernel/k_readable_event.h" -#include "core/hle/kernel/kernel.h" #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvflinger/buffer_queue.h" #include "core/hle/service/nvflinger/nvflinger.h" #include "core/hle/service/vi/display/vi_display.h" #include "core/hle/service/vi/layer/vi_layer.h" -#include "core/perf_stats.h" -#include "video_core/renderer_base.h" +#include "video_core/gpu.h" namespace Service::NVFlinger { constexpr auto frame_ns = std::chrono::nanoseconds{1000000000 / 60}; -void NVFlinger::VSyncThread(NVFlinger& nv_flinger) { - nv_flinger.SplitVSync(); -} - -void NVFlinger::SplitVSync() { +void NVFlinger::SplitVSync(std::stop_token stop_token) { system.RegisterHostThread(); std::string name = "yuzu:VSyncThread"; MicroProfileOnThreadCreate(name.c_str()); @@ -45,7 +37,7 @@ void NVFlinger::SplitVSync() { Common::SetCurrentThreadName(name.c_str()); Common::SetCurrentThreadPriority(Common::ThreadPriority::High); s64 delay = 0; - while (is_running) { + while (!stop_token.stop_requested()) { guard->lock(); const s64 time_start = system.CoreTiming().GetGlobalTimeNs().count(); Compose(); @@ -55,7 +47,7 @@ void NVFlinger::SplitVSync() { const s64 next_time = std::max<s64>(0, ticks - time_passed - delay); guard->unlock(); if (next_time > 0) { - wait_event->WaitFor(std::chrono::nanoseconds{next_time}); + std::this_thread::sleep_for(std::chrono::nanoseconds{next_time}); } delay = (system.CoreTiming().GetGlobalTimeNs().count() - time_end) - next_time; } @@ -84,9 +76,7 @@ NVFlinger::NVFlinger(Core::System& system_) }); if (system.IsMulticore()) { - is_running = true; - wait_event = std::make_unique<Common::Event>(); - vsync_thread = std::make_unique<std::thread>(VSyncThread, std::ref(*this)); + vsync_thread = std::jthread([this](std::stop_token token) { SplitVSync(token); }); } else { system.CoreTiming().ScheduleEvent(frame_ns, composition_event); } @@ -96,14 +86,7 @@ NVFlinger::~NVFlinger() { for (auto& buffer_queue : buffer_queues) { buffer_queue->Disconnect(); } - - if (system.IsMulticore()) { - is_running = false; - wait_event->Set(); - vsync_thread->join(); - vsync_thread.reset(); - wait_event.reset(); - } else { + if (!system.IsMulticore()) { system.CoreTiming().UnscheduleEvent(composition_event, 0); } } diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 6d84cafb4..7935cf773 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -4,13 +4,10 @@ #pragma once -#include <atomic> #include <list> #include <memory> #include <mutex> #include <optional> -#include <string> -#include <string_view> #include <thread> #include <vector> @@ -109,9 +106,7 @@ private: /// Creates a layer with the specified layer ID in the desired display. void CreateLayerAtId(VI::Display& display, u64 layer_id); - static void VSyncThread(NVFlinger& nv_flinger); - - void SplitVSync(); + void SplitVSync(std::stop_token stop_token); std::shared_ptr<Nvidia::Module> nvdrv; @@ -133,9 +128,7 @@ private: Core::System& system; - std::unique_ptr<std::thread> vsync_thread; - std::unique_ptr<Common::Event> wait_event; - std::atomic<bool> is_running{}; + std::jthread vsync_thread; KernelHelpers::ServiceContext service_context; }; diff --git a/src/core/hle/service/olsc/olsc.cpp b/src/core/hle/service/olsc/olsc.cpp index 3bbe1bfe2..39a8031a5 100644 --- a/src/core/hle/service/olsc/olsc.cpp +++ b/src/core/hle/service/olsc/olsc.cpp @@ -3,7 +3,6 @@ // Refer to the license.txt file included. #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/olsc/olsc.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" diff --git a/src/core/hle/service/ptm/psm.cpp b/src/core/hle/service/ptm/psm.cpp index d9897c5c5..22ff5269c 100644 --- a/src/core/hle/service/ptm/psm.cpp +++ b/src/core/hle/service/ptm/psm.cpp @@ -8,9 +8,8 @@ #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_event.h" -#include "core/hle/kernel/k_readable_event.h" -#include "core/hle/kernel/k_writable_event.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/ptm/psm.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" @@ -20,7 +19,7 @@ namespace Service::PSM { class IPsmSession final : public ServiceFramework<IPsmSession> { public: explicit IPsmSession(Core::System& system_) - : ServiceFramework{system_, "IPsmSession"}, state_change_event{system.Kernel()} { + : ServiceFramework{system_, "IPsmSession"}, service_context{system_, "IPsmSession"} { // clang-format off static const FunctionInfo functions[] = { {0, &IPsmSession::BindStateChangeEvent, "BindStateChangeEvent"}, @@ -33,27 +32,28 @@ public: RegisterHandlers(functions); - Kernel::KAutoObject::Create(std::addressof(state_change_event)); - state_change_event.Initialize("IPsmSession::state_change_event"); + state_change_event = service_context.CreateEvent("IPsmSession::state_change_event"); } - ~IPsmSession() override = default; + ~IPsmSession() override { + service_context.CloseEvent(state_change_event); + } void SignalChargerTypeChanged() { if (should_signal && should_signal_charger_type) { - state_change_event.GetWritableEvent().Signal(); + state_change_event->GetWritableEvent().Signal(); } } void SignalPowerSupplyChanged() { if (should_signal && should_signal_power_supply) { - state_change_event.GetWritableEvent().Signal(); + state_change_event->GetWritableEvent().Signal(); } } void SignalBatteryVoltageStateChanged() { if (should_signal && should_signal_battery_voltage) { - state_change_event.GetWritableEvent().Signal(); + state_change_event->GetWritableEvent().Signal(); } } @@ -65,7 +65,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(state_change_event.GetReadableEvent()); + rb.PushCopyObjects(state_change_event->GetReadableEvent()); } void UnbindStateChangeEvent(Kernel::HLERequestContext& ctx) { @@ -110,11 +110,13 @@ private: rb.Push(ResultSuccess); } + KernelHelpers::ServiceContext service_context; + bool should_signal_charger_type{}; bool should_signal_power_supply{}; bool should_signal_battery_voltage{}; bool should_signal{}; - Kernel::KEvent state_change_event; + Kernel::KEvent* state_change_event; }; class PSM final : public ServiceFramework<PSM> { diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp index 8299c6b68..286578b17 100644 --- a/src/core/hle/service/set/set_sys.cpp +++ b/src/core/hle/service/set/set_sys.cpp @@ -7,7 +7,6 @@ #include "core/file_sys/errors.h" #include "core/file_sys/system_archive/system_version.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/k_client_port.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/set/set_sys.h" diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index 7d85ecb6a..b9e765f1d 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp @@ -415,6 +415,18 @@ void BSD::Write(Kernel::HLERequestContext& ctx) { }); } +void BSD::Read(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const s32 fd = rp.Pop<s32>(); + + LOG_WARNING(Service, "(STUBBED) called. fd={} len={}", fd, ctx.GetWriteBufferSize()); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push<u32>(0); // ret + rb.Push<u32>(0); // bsd errno +} + void BSD::Close(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const s32 fd = rp.Pop<s32>(); @@ -855,7 +867,7 @@ BSD::BSD(Core::System& system_, const char* name) : ServiceFramework{system_, na {22, &BSD::Shutdown, "Shutdown"}, {23, nullptr, "ShutdownAllSockets"}, {24, &BSD::Write, "Write"}, - {25, nullptr, "Read"}, + {25, &BSD::Read, "Read"}, {26, &BSD::Close, "Close"}, {27, nullptr, "DuplicateSocket"}, {28, nullptr, "GetResourceStatistics"}, diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h index 1d2df9c61..a387e50df 100644 --- a/src/core/hle/service/sockets/bsd.h +++ b/src/core/hle/service/sockets/bsd.h @@ -5,11 +5,9 @@ #pragma once #include <memory> -#include <string_view> #include <vector> #include "common/common_types.h" -#include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/service.h" #include "core/hle/service/sockets/sockets.h" @@ -135,6 +133,7 @@ private: void Send(Kernel::HLERequestContext& ctx); void SendTo(Kernel::HLERequestContext& ctx); void Write(Kernel::HLERequestContext& ctx); + void Read(Kernel::HLERequestContext& ctx); void Close(Kernel::HLERequestContext& ctx); void EventFd(Kernel::HLERequestContext& ctx); diff --git a/src/core/hle/service/sockets/sfdnsres.h b/src/core/hle/service/sockets/sfdnsres.h index faa6b7d0d..5d3b4dc2d 100644 --- a/src/core/hle/service/sockets/sfdnsres.h +++ b/src/core/hle/service/sockets/sfdnsres.h @@ -4,7 +4,6 @@ #pragma once -#include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/service.h" namespace Core { diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h index 5a65ed2a9..02dbbae40 100644 --- a/src/core/hle/service/sockets/sockets.h +++ b/src/core/hle/service/sockets/sockets.h @@ -4,13 +4,17 @@ #pragma once +#include "common/common_funcs.h" #include "common/common_types.h" -#include "core/hle/service/service.h" namespace Core { class System; } +namespace Service::SM { +class ServiceManager; +} + namespace Service::Sockets { enum class Errno : u32 { diff --git a/src/core/hle/service/spl/spl_module.cpp b/src/core/hle/service/spl/spl_module.cpp index 918633af5..ed4c06260 100644 --- a/src/core/hle/service/spl/spl_module.cpp +++ b/src/core/hle/service/spl/spl_module.cpp @@ -3,10 +3,8 @@ // Refer to the license.txt file included. #include <algorithm> -#include <chrono> #include <cstdlib> #include <ctime> -#include <functional> #include <vector> #include "common/logging/log.h" #include "common/settings.h" diff --git a/src/core/hle/service/ssl/ssl.cpp b/src/core/hle/service/ssl/ssl.cpp index 921f4d776..a81a595ea 100644 --- a/src/core/hle/service/ssl/ssl.cpp +++ b/src/core/hle/service/ssl/ssl.cpp @@ -3,7 +3,6 @@ // Refer to the license.txt file included. #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" #include "core/hle/service/ssl/ssl.h" diff --git a/src/core/hle/service/time/standard_user_system_clock_core.cpp b/src/core/hle/service/time/standard_user_system_clock_core.cpp index ef79ab917..e94220a44 100644 --- a/src/core/hle/service/time/standard_user_system_clock_core.cpp +++ b/src/core/hle/service/time/standard_user_system_clock_core.cpp @@ -4,6 +4,7 @@ #include "common/assert.h" #include "core/core.h" +#include "core/hle/kernel/k_event.h" #include "core/hle/service/time/standard_local_system_clock_core.h" #include "core/hle/service/time/standard_network_system_clock_core.h" #include "core/hle/service/time/standard_user_system_clock_core.h" @@ -16,10 +17,15 @@ StandardUserSystemClockCore::StandardUserSystemClockCore( : SystemClockCore(local_system_clock_core_.GetSteadyClockCore()), local_system_clock_core{local_system_clock_core_}, network_system_clock_core{network_system_clock_core_}, - auto_correction_time{SteadyClockTimePoint::GetRandom()}, auto_correction_event{ - system_.Kernel()} { - Kernel::KAutoObject::Create(std::addressof(auto_correction_event)); - auto_correction_event.Initialize("StandardUserSystemClockCore:AutoCorrectionEvent"); + auto_correction_time{SteadyClockTimePoint::GetRandom()}, service_context{ + system_, + "StandardUserSystemClockCore"} { + auto_correction_event = + service_context.CreateEvent("StandardUserSystemClockCore:AutoCorrectionEvent"); +} + +StandardUserSystemClockCore::~StandardUserSystemClockCore() { + service_context.CloseEvent(auto_correction_event); } ResultCode StandardUserSystemClockCore::SetAutomaticCorrectionEnabled(Core::System& system, diff --git a/src/core/hle/service/time/standard_user_system_clock_core.h b/src/core/hle/service/time/standard_user_system_clock_core.h index bf9ec5e42..b7cb2b045 100644 --- a/src/core/hle/service/time/standard_user_system_clock_core.h +++ b/src/core/hle/service/time/standard_user_system_clock_core.h @@ -4,7 +4,7 @@ #pragma once -#include "core/hle/kernel/k_event.h" +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/time/clock_types.h" #include "core/hle/service/time/system_clock_core.h" @@ -27,6 +27,8 @@ public: StandardNetworkSystemClockCore& network_system_clock_core_, Core::System& system_); + ~StandardUserSystemClockCore() override; + ResultCode SetAutomaticCorrectionEnabled(Core::System& system, bool value); ResultCode GetClockContext(Core::System& system, SystemClockContext& ctx) const override; @@ -55,7 +57,8 @@ private: StandardNetworkSystemClockCore& network_system_clock_core; bool auto_correction_enabled{}; SteadyClockTimePoint auto_correction_time; - Kernel::KEvent auto_correction_event; + KernelHelpers::ServiceContext service_context; + Kernel::KEvent* auto_correction_event; }; } // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/system_clock_context_update_callback.h b/src/core/hle/service/time/system_clock_context_update_callback.h index 797954958..6936397a5 100644 --- a/src/core/hle/service/time/system_clock_context_update_callback.h +++ b/src/core/hle/service/time/system_clock_context_update_callback.h @@ -4,6 +4,7 @@ #pragma once +#include <memory> #include <vector> #include "core/hle/service/time/clock_types.h" diff --git a/src/core/hle/service/time/system_clock_core.cpp b/src/core/hle/service/time/system_clock_core.cpp index bd334bbef..5c2354cdd 100644 --- a/src/core/hle/service/time/system_clock_core.cpp +++ b/src/core/hle/service/time/system_clock_core.cpp @@ -13,7 +13,7 @@ SystemClockCore::SystemClockCore(SteadyClockCore& steady_clock_core_) context.steady_time_point.clock_source_id = steady_clock_core.GetClockSourceId(); } -SystemClockCore ::~SystemClockCore() = default; +SystemClockCore::~SystemClockCore() = default; ResultCode SystemClockCore::GetCurrentTime(Core::System& system, s64& posix_time) const { posix_time = 0; diff --git a/src/core/hle/service/time/system_clock_core.h b/src/core/hle/service/time/system_clock_core.h index 83d0e5d62..b9237ad28 100644 --- a/src/core/hle/service/time/system_clock_core.h +++ b/src/core/hle/service/time/system_clock_core.h @@ -4,6 +4,8 @@ #pragma once +#include <memory> + #include "common/common_types.h" #include "core/hle/service/time/clock_types.h" diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index 8fdd5076f..d84a111c2 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp @@ -8,11 +8,11 @@ #include "core/core_timing_util.h" #include "core/hardware_properties.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/k_client_port.h" #include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/kernel.h" #include "core/hle/service/time/time.h" #include "core/hle/service/time/time_interface.h" +#include "core/hle/service/time/time_manager.h" #include "core/hle/service/time/time_sharedmemory.h" #include "core/hle/service/time/time_zone_service.h" diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h index ce9c479c6..30e2cd369 100644 --- a/src/core/hle/service/time/time.h +++ b/src/core/hle/service/time/time.h @@ -6,7 +6,6 @@ #include "core/hle/service/service.h" #include "core/hle/service/time/clock_types.h" -#include "core/hle/service/time/time_manager.h" namespace Core { class System; diff --git a/src/core/hle/service/time/time_zone_service.cpp b/src/core/hle/service/time/time_zone_service.cpp index 5c3108768..3871e7316 100644 --- a/src/core/hle/service/time/time_zone_service.cpp +++ b/src/core/hle/service/time/time_zone_service.cpp @@ -10,8 +10,8 @@ namespace Service::Time { -ITimeZoneService ::ITimeZoneService(Core::System& system_, - TimeZone::TimeZoneContentManager& time_zone_manager_) +ITimeZoneService::ITimeZoneService(Core::System& system_, + TimeZone::TimeZoneContentManager& time_zone_manager_) : ServiceFramework{system_, "ITimeZoneService"}, time_zone_content_manager{time_zone_manager_} { static const FunctionInfo functions[] = { {0, &ITimeZoneService::GetDeviceLocationName, "GetDeviceLocationName"}, diff --git a/src/core/hle/service/usb/usb.cpp b/src/core/hle/service/usb/usb.cpp index 7f436c3bb..502dfbb4a 100644 --- a/src/core/hle/service/usb/usb.cpp +++ b/src/core/hle/service/usb/usb.cpp @@ -6,7 +6,6 @@ #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" #include "core/hle/service/usb/usb.h" @@ -97,7 +96,7 @@ public: {3, nullptr, "GetAlternateInterface"}, {4, nullptr, "GetCurrentFrame"}, {5, nullptr, "CtrlXferAsync"}, - {6, nullptr, "Unknown6"}, + {6, nullptr, "GetCtrlXferCompletionEvent"}, {7, nullptr, "GetCtrlXferReport"}, {8, nullptr, "ResetDevice"}, {9, nullptr, "OpenUsbEp"}, @@ -183,8 +182,8 @@ public: {4, nullptr, "GetHostPdcFirmwareRevision"}, {5, nullptr, "GetHostPdcManufactureId"}, {6, nullptr, "GetHostPdcDeviceId"}, - {7, nullptr, "AwakeCradle"}, - {8, nullptr, "SleepCradle"}, + {7, nullptr, "EnableCradleRecovery"}, + {8, nullptr, "DisableCradleRecovery"}, }; // clang-format on diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 8e8fc40ca..be3d52d54 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -831,6 +831,7 @@ public: {6010, nullptr, "GetLayerPresentationAllFencesExpiredEvent"}, {6011, nullptr, "EnableLayerAutoClearTransitionBuffer"}, {6012, nullptr, "DisableLayerAutoClearTransitionBuffer"}, + {6013, nullptr, "SetLayerOpacity"}, {7000, nullptr, "SetContentVisibility"}, {8000, nullptr, "SetConductorLayer"}, {8001, nullptr, "SetTimestampTracking"}, diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h index eec531d54..2fd7f8e61 100644 --- a/src/core/hle/service/vi/vi.h +++ b/src/core/hle/service/vi/vi.h @@ -4,7 +4,6 @@ #pragma once -#include <memory> #include "common/common_types.h" namespace Core { diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 51c4dea26..88d6ec908 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -587,7 +587,11 @@ void Memory::UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) { bool Memory::IsValidVirtualAddress(const VAddr vaddr) const { const Kernel::KProcess& process = *system.CurrentProcess(); const auto& page_table = process.PageTable().PageTableImpl(); - const auto [pointer, type] = page_table.pointers[vaddr >> PAGE_BITS].PointerType(); + const size_t page = vaddr >> PAGE_BITS; + if (page >= page_table.pointers.size()) { + return false; + } + const auto [pointer, type] = page_table.pointers[page].PointerType(); return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory; } diff --git a/src/core/network/network.cpp b/src/core/network/network.cpp index 72eea52f0..a3e0664b9 100644 --- a/src/core/network/network.cpp +++ b/src/core/network/network.cpp @@ -366,8 +366,6 @@ std::optional<IPv4Address> GetHostIPv4Address() { if (res != network_interfaces.end()) { char ip_addr[16] = {}; ASSERT(inet_ntop(AF_INET, &res->ip_address, ip_addr, sizeof(ip_addr)) != nullptr); - LOG_INFO(Network, "IP address: {}", ip_addr); - return TranslateIPv4(res->ip_address); } else { LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface); diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index 18d7d8817..f3907c65a 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp @@ -346,8 +346,8 @@ void InputSubsystem::ReloadInputDevices() { impl->udp->ReloadSockets(); } -std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers([ - [maybe_unused]] Polling::DeviceType type) const { +std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers( + [[maybe_unused]] Polling::DeviceType type) const { #ifdef HAVE_SDL2 return impl->sdl->GetPollers(type); #else diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index 03888b7cb..ab6211b29 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -254,11 +254,25 @@ public: } bool IsJoyconLeft() const { - return std::strstr(GetControllerName().c_str(), "Joy-Con Left") != nullptr; + const std::string controller_name = GetControllerName(); + if (std::strstr(controller_name.c_str(), "Joy-Con Left") != nullptr) { + return true; + } + if (std::strstr(controller_name.c_str(), "Joy-Con (L)") != nullptr) { + return true; + } + return false; } bool IsJoyconRight() const { - return std::strstr(GetControllerName().c_str(), "Joy-Con Right") != nullptr; + const std::string controller_name = GetControllerName(); + if (std::strstr(controller_name.c_str(), "Joy-Con Right") != nullptr) { + return true; + } + if (std::strstr(controller_name.c_str(), "Joy-Con (R)") != nullptr) { + return true; + } + return false; } std::string GetControllerName() const { diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp index 580063fa9..170db269a 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp @@ -58,8 +58,8 @@ void GetCbuf(EmitContext& ctx, std::string_view ret, const IR::Value& binding, const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())}; const auto cbuf_cast{fmt::format("{}({}[{}]{{}})", cast, cbuf, index)}; const auto extraction{num_bits == 32 ? cbuf_cast - : fmt ::format("bitfieldExtract({},int({}),{})", cbuf_cast, - bit_offset, num_bits)}; + : fmt::format("bitfieldExtract({},int({}),{})", cbuf_cast, + bit_offset, num_bits)}; if (!component_indexing_bug) { const auto result{fmt::format(fmt::runtime(extraction), swizzle)}; ctx.Add("{}={};", ret, result); diff --git a/src/shader_recompiler/object_pool.h b/src/shader_recompiler/object_pool.h index f3b12d04b..a12ddcc8f 100644 --- a/src/shader_recompiler/object_pool.h +++ b/src/shader_recompiler/object_pool.h @@ -11,14 +11,16 @@ namespace Shader { template <typename T> -requires std::is_destructible_v<T> class ObjectPool { +requires std::is_destructible_v<T> +class ObjectPool { public: explicit ObjectPool(size_t chunk_size = 8192) : new_chunk_size{chunk_size} { node = &chunks.emplace_back(new_chunk_size); } template <typename... Args> - requires std::is_constructible_v<T, Args...>[[nodiscard]] T* Create(Args&&... args) { + requires std::is_constructible_v<T, Args...> + [[nodiscard]] T* Create(Args&&... args) { return std::construct_at(Memory(), std::forward<Args>(args)...); } diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 7bfd57369..d350c9b36 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -570,13 +570,12 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am ForEachWrittenRange(*cpu_src_address, amount, mirror); // This subtraction in this order is important for overlapping copies. common_ranges.subtract(subtract_interval); - bool atleast_1_download = tmp_intervals.size() != 0; - for (const IntervalType add_interval : tmp_intervals) { + const bool has_new_downloads = tmp_intervals.size() != 0; + for (const IntervalType& add_interval : tmp_intervals) { common_ranges.add(add_interval); } - runtime.CopyBuffer(dest_buffer, src_buffer, copies); - if (atleast_1_download) { + if (has_new_downloads) { dest_buffer.MarkRegionAsGpuModified(*cpu_dest_address, amount); } std::vector<u8> tmp_buffer(amount); diff --git a/src/video_core/cdma_pusher.cpp b/src/video_core/cdma_pusher.cpp index 8b86ad050..a8c4b4415 100644 --- a/src/video_core/cdma_pusher.cpp +++ b/src/video_core/cdma_pusher.cpp @@ -24,6 +24,7 @@ #include "command_classes/vic.h" #include "video_core/cdma_pusher.h" #include "video_core/command_classes/nvdec_common.h" +#include "video_core/command_classes/sync_manager.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/gpu.h" #include "video_core/memory_manager.h" diff --git a/src/video_core/cdma_pusher.h b/src/video_core/cdma_pusher.h index 1bada44dd..87b49d6ea 100644 --- a/src/video_core/cdma_pusher.h +++ b/src/video_core/cdma_pusher.h @@ -9,13 +9,13 @@ #include "common/bit_field.h" #include "common/common_types.h" -#include "video_core/command_classes/sync_manager.h" namespace Tegra { class GPU; class Host1x; class Nvdec; +class SyncptIncrManager; class Vic; enum class ChSubmissionMode : u32 { diff --git a/src/video_core/command_classes/vic.cpp b/src/video_core/command_classes/vic.cpp index 0ee07f398..dc768b952 100644 --- a/src/video_core/command_classes/vic.cpp +++ b/src/video_core/command_classes/vic.cpp @@ -16,6 +16,7 @@ extern "C" { } #include "common/assert.h" +#include "common/bit_field.h" #include "common/logging/log.h" #include "video_core/command_classes/nvdec.h" @@ -26,6 +27,25 @@ extern "C" { #include "video_core/textures/decoders.h" namespace Tegra { +namespace { +enum class VideoPixelFormat : u64_le { + RGBA8 = 0x1f, + BGRA8 = 0x20, + RGBX8 = 0x23, + Yuv420 = 0x44, +}; +} // Anonymous namespace + +union VicConfig { + u64_le raw{}; + BitField<0, 7, VideoPixelFormat> pixel_format; + BitField<7, 2, u64_le> chroma_loc_horiz; + BitField<9, 2, u64_le> chroma_loc_vert; + BitField<11, 4, u64_le> block_linear_kind; + BitField<15, 4, u64_le> block_linear_height_log2; + BitField<32, 14, u64_le> surface_width_minus1; + BitField<46, 14, u64_le> surface_height_minus1; +}; Vic::Vic(GPU& gpu_, std::shared_ptr<Nvdec> nvdec_processor_) : gpu(gpu_), @@ -65,134 +85,156 @@ void Vic::Execute() { if (!frame) { return; } - const auto pixel_format = static_cast<VideoPixelFormat>(config.pixel_format.Value()); - switch (pixel_format) { + const u64 surface_width = config.surface_width_minus1 + 1; + const u64 surface_height = config.surface_height_minus1 + 1; + if (static_cast<u64>(frame->width) != surface_width || + static_cast<u64>(frame->height) > surface_height) { + // TODO: Properly support multiple video streams with differing frame dimensions + LOG_WARNING(Debug, + "Frame dimensions {}x{} can't be safely decoded into surface dimensions {}x{}", + frame->width, frame->height, surface_width, surface_height); + return; + } + switch (config.pixel_format) { + case VideoPixelFormat::RGBA8: case VideoPixelFormat::BGRA8: - case VideoPixelFormat::RGBA8: { - LOG_TRACE(Service_NVDRV, "Writing RGB Frame"); + case VideoPixelFormat::RGBX8: + WriteRGBFrame(frame, config); + break; + case VideoPixelFormat::Yuv420: + WriteYUVFrame(frame, config); + break; + default: + UNIMPLEMENTED_MSG("Unknown video pixel format {:X}", config.pixel_format.Value()); + break; + } +} - if (scaler_ctx == nullptr || frame->width != scaler_width || - frame->height != scaler_height) { - const AVPixelFormat target_format = - (pixel_format == VideoPixelFormat::RGBA8) ? AV_PIX_FMT_RGBA : AV_PIX_FMT_BGRA; +void Vic::WriteRGBFrame(const AVFrame* frame, const VicConfig& config) { + LOG_TRACE(Service_NVDRV, "Writing RGB Frame"); + + if (!scaler_ctx || frame->width != scaler_width || frame->height != scaler_height) { + const AVPixelFormat target_format = [pixel_format = config.pixel_format]() { + switch (pixel_format) { + case VideoPixelFormat::RGBA8: + return AV_PIX_FMT_RGBA; + case VideoPixelFormat::BGRA8: + return AV_PIX_FMT_BGRA; + case VideoPixelFormat::RGBX8: + return AV_PIX_FMT_RGB0; + default: + return AV_PIX_FMT_RGBA; + } + }(); + + sws_freeContext(scaler_ctx); + // Frames are decoded into either YUV420 or NV12 formats. Convert to desired RGB format + scaler_ctx = sws_getContext(frame->width, frame->height, + static_cast<AVPixelFormat>(frame->format), frame->width, + frame->height, target_format, 0, nullptr, nullptr, nullptr); + scaler_width = frame->width; + scaler_height = frame->height; + converted_frame_buffer.reset(); + } + // Get Converted frame + const u32 width = static_cast<u32>(frame->width); + const u32 height = static_cast<u32>(frame->height); + const std::size_t linear_size = width * height * 4; + + // Only allocate frame_buffer once per stream, as the size is not expected to change + if (!converted_frame_buffer) { + converted_frame_buffer = AVMallocPtr{static_cast<u8*>(av_malloc(linear_size)), av_free}; + } + const std::array<int, 4> converted_stride{frame->width * 4, frame->height * 4, 0, 0}; + u8* const converted_frame_buf_addr{converted_frame_buffer.get()}; + + sws_scale(scaler_ctx, frame->data, frame->linesize, 0, frame->height, &converted_frame_buf_addr, + converted_stride.data()); + + const u32 blk_kind = static_cast<u32>(config.block_linear_kind); + if (blk_kind != 0) { + // swizzle pitch linear to block linear + const u32 block_height = static_cast<u32>(config.block_linear_height_log2); + const auto size = Texture::CalculateSize(true, 4, width, height, 1, block_height, 0); + luma_buffer.resize(size); + Texture::SwizzleSubrect(width, height, width * 4, width, 4, luma_buffer.data(), + converted_frame_buffer.get(), block_height, 0, 0); + + gpu.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(), size); + } else { + // send pitch linear frame + gpu.MemoryManager().WriteBlock(output_surface_luma_address, converted_frame_buf_addr, + linear_size); + } +} - sws_freeContext(scaler_ctx); - scaler_ctx = nullptr; +void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) { + LOG_TRACE(Service_NVDRV, "Writing YUV420 Frame"); - // Frames are decoded into either YUV420 or NV12 formats. Convert to desired format - scaler_ctx = sws_getContext(frame->width, frame->height, - static_cast<AVPixelFormat>(frame->format), frame->width, - frame->height, target_format, 0, nullptr, nullptr, nullptr); + const std::size_t surface_width = config.surface_width_minus1 + 1; + const std::size_t surface_height = config.surface_height_minus1 + 1; + const auto frame_width = std::min(surface_width, static_cast<size_t>(frame->width)); + const auto frame_height = std::min(surface_height, static_cast<size_t>(frame->height)); + const std::size_t aligned_width = (surface_width + 0xff) & ~0xffUL; - scaler_width = frame->width; - scaler_height = frame->height; - } - // Get Converted frame - const u32 width = static_cast<u32>(frame->width); - const u32 height = static_cast<u32>(frame->height); - const std::size_t linear_size = width * height * 4; - - // Only allocate frame_buffer once per stream, as the size is not expected to change - if (!converted_frame_buffer) { - converted_frame_buffer = AVMallocPtr{static_cast<u8*>(av_malloc(linear_size)), av_free}; + const auto stride = static_cast<size_t>(frame->linesize[0]); + + luma_buffer.resize(aligned_width * surface_height); + chroma_buffer.resize(aligned_width * surface_height / 2); + + // Populate luma buffer + const u8* luma_src = frame->data[0]; + for (std::size_t y = 0; y < frame_height; ++y) { + const std::size_t src = y * stride; + const std::size_t dst = y * aligned_width; + for (std::size_t x = 0; x < frame_width; ++x) { + luma_buffer[dst + x] = luma_src[src + x]; } - const std::array<int, 4> converted_stride{frame->width * 4, frame->height * 4, 0, 0}; - u8* const converted_frame_buf_addr{converted_frame_buffer.get()}; - - sws_scale(scaler_ctx, frame->data, frame->linesize, 0, frame->height, - &converted_frame_buf_addr, converted_stride.data()); - - const u32 blk_kind = static_cast<u32>(config.block_linear_kind); - if (blk_kind != 0) { - // swizzle pitch linear to block linear - const u32 block_height = static_cast<u32>(config.block_linear_height_log2); - const auto size = - Tegra::Texture::CalculateSize(true, 4, width, height, 1, block_height, 0); - luma_buffer.resize(size); - Tegra::Texture::SwizzleSubrect(width, height, width * 4, width, 4, luma_buffer.data(), - converted_frame_buffer.get(), block_height, 0, 0); - - gpu.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(), size); - } else { - // send pitch linear frame - gpu.MemoryManager().WriteBlock(output_surface_luma_address, converted_frame_buf_addr, - linear_size); + } + gpu.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(), + luma_buffer.size()); + + // Chroma + const std::size_t half_height = frame_height / 2; + const auto half_stride = static_cast<size_t>(frame->linesize[1]); + + switch (frame->format) { + case AV_PIX_FMT_YUV420P: { + // Frame from FFmpeg software + // Populate chroma buffer from both channels with interleaving. + const std::size_t half_width = frame_width / 2; + const u8* chroma_b_src = frame->data[1]; + const u8* chroma_r_src = frame->data[2]; + for (std::size_t y = 0; y < half_height; ++y) { + const std::size_t src = y * half_stride; + const std::size_t dst = y * aligned_width; + + for (std::size_t x = 0; x < half_width; ++x) { + chroma_buffer[dst + x * 2] = chroma_b_src[src + x]; + chroma_buffer[dst + x * 2 + 1] = chroma_r_src[src + x]; + } } break; } - case VideoPixelFormat::Yuv420: { - LOG_TRACE(Service_NVDRV, "Writing YUV420 Frame"); - - const std::size_t surface_width = config.surface_width_minus1 + 1; - const std::size_t surface_height = config.surface_height_minus1 + 1; - const auto frame_width = std::min(surface_width, static_cast<size_t>(frame->width)); - const auto frame_height = std::min(surface_height, static_cast<size_t>(frame->height)); - const std::size_t aligned_width = (surface_width + 0xff) & ~0xffUL; - - const auto stride = static_cast<size_t>(frame->linesize[0]); - - luma_buffer.resize(aligned_width * surface_height); - chroma_buffer.resize(aligned_width * surface_height / 2); - - // Populate luma buffer - const u8* luma_src = frame->data[0]; - for (std::size_t y = 0; y < frame_height; ++y) { + case AV_PIX_FMT_NV12: { + // Frame from VA-API hardware + // This is already interleaved so just copy + const u8* chroma_src = frame->data[1]; + for (std::size_t y = 0; y < half_height; ++y) { const std::size_t src = y * stride; const std::size_t dst = y * aligned_width; for (std::size_t x = 0; x < frame_width; ++x) { - luma_buffer[dst + x] = luma_src[src + x]; - } - } - gpu.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(), - luma_buffer.size()); - - // Chroma - const std::size_t half_height = frame_height / 2; - const auto half_stride = static_cast<size_t>(frame->linesize[1]); - - switch (frame->format) { - case AV_PIX_FMT_YUV420P: { - // Frame from FFmpeg software - // Populate chroma buffer from both channels with interleaving. - const std::size_t half_width = frame_width / 2; - const u8* chroma_b_src = frame->data[1]; - const u8* chroma_r_src = frame->data[2]; - for (std::size_t y = 0; y < half_height; ++y) { - const std::size_t src = y * half_stride; - const std::size_t dst = y * aligned_width; - - for (std::size_t x = 0; x < half_width; ++x) { - chroma_buffer[dst + x * 2] = chroma_b_src[src + x]; - chroma_buffer[dst + x * 2 + 1] = chroma_r_src[src + x]; - } + chroma_buffer[dst + x] = chroma_src[src + x]; } - break; - } - case AV_PIX_FMT_NV12: { - // Frame from VA-API hardware - // This is already interleaved so just copy - const u8* chroma_src = frame->data[1]; - for (std::size_t y = 0; y < half_height; ++y) { - const std::size_t src = y * stride; - const std::size_t dst = y * aligned_width; - for (std::size_t x = 0; x < frame_width; ++x) { - chroma_buffer[dst + x] = chroma_src[src + x]; - } - } - break; - } - default: - UNREACHABLE(); - break; } - gpu.MemoryManager().WriteBlock(output_surface_chroma_address, chroma_buffer.data(), - chroma_buffer.size()); break; } default: - UNIMPLEMENTED_MSG("Unknown video pixel format {}", config.pixel_format.Value()); + UNREACHABLE(); break; } + gpu.MemoryManager().WriteBlock(output_surface_chroma_address, chroma_buffer.data(), + chroma_buffer.size()); } } // namespace Tegra diff --git a/src/video_core/command_classes/vic.h b/src/video_core/command_classes/vic.h index 74246e08c..6d4cdfd57 100644 --- a/src/video_core/command_classes/vic.h +++ b/src/video_core/command_classes/vic.h @@ -6,7 +6,6 @@ #include <memory> #include <vector> -#include "common/bit_field.h" #include "common/common_types.h" struct SwsContext; @@ -14,6 +13,7 @@ struct SwsContext; namespace Tegra { class GPU; class Nvdec; +union VicConfig; class Vic { public: @@ -27,6 +27,7 @@ public: }; explicit Vic(GPU& gpu, std::shared_ptr<Nvdec> nvdec_processor); + ~Vic(); /// Write to the device state. @@ -35,22 +36,9 @@ public: private: void Execute(); - enum class VideoPixelFormat : u64_le { - RGBA8 = 0x1f, - BGRA8 = 0x20, - Yuv420 = 0x44, - }; + void WriteRGBFrame(const AVFrame* frame, const VicConfig& config); - union VicConfig { - u64_le raw{}; - BitField<0, 7, u64_le> pixel_format; - BitField<7, 2, u64_le> chroma_loc_horiz; - BitField<9, 2, u64_le> chroma_loc_vert; - BitField<11, 4, u64_le> block_linear_kind; - BitField<15, 4, u64_le> block_linear_height_log2; - BitField<32, 14, u64_le> surface_width_minus1; - BitField<46, 14, u64_le> surface_height_minus1; - }; + void WriteYUVFrame(const AVFrame* frame, const VicConfig& config); GPU& gpu; std::shared_ptr<Tegra::Nvdec> nvdec_processor; diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 7f4ca6282..f22342dfb 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -6,6 +6,7 @@ #include <array> #include <bitset> +#include <cmath> #include <limits> #include <optional> #include <type_traits> diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index c7ec1eac9..67388d980 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp @@ -82,41 +82,41 @@ void MaxwellDMA::Launch() { } void MaxwellDMA::CopyPitchToPitch() { - // When `multi_line_enable` bit is disabled the copy is performed as if we were copying a 1D - // buffer of length `line_length_in`. - // Otherwise we copy a 2D image of dimensions (line_length_in, line_count). - auto& accelerate = rasterizer->AccessAccelerateDMA(); - if (!regs.launch_dma.multi_line_enable) { - const bool is_buffer_clear = regs.launch_dma.remap_enable != 0 && - regs.remap_const.dst_x == RemapConst::Swizzle::CONST_A; - // TODO: allow multisized components. - if (is_buffer_clear) { - ASSERT(regs.remap_const.component_size_minus_one == 3); - accelerate.BufferClear(regs.offset_out, regs.line_length_in, regs.remap_consta_value); - std::vector<u32> tmp_buffer(regs.line_length_in, regs.remap_consta_value); - memory_manager.WriteBlockUnsafe(regs.offset_out, - reinterpret_cast<u8*>(tmp_buffer.data()), - regs.line_length_in * sizeof(u32)); - return; - } - UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0); - if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) { - std::vector<u8> tmp_buffer(regs.line_length_in); - memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(), regs.line_length_in); - memory_manager.WriteBlock(regs.offset_out, tmp_buffer.data(), regs.line_length_in); + // When `multi_line_enable` bit is enabled we copy a 2D image of dimensions + // (line_length_in, line_count). + // Otherwise the copy is performed as if we were copying a 1D buffer of length line_length_in. + const bool remap_enabled = regs.launch_dma.remap_enable != 0; + if (regs.launch_dma.multi_line_enable) { + UNIMPLEMENTED_IF(remap_enabled); + + // Perform a line-by-line copy. + // We're going to take a subrect of size (line_length_in, line_count) from the source + // rectangle. There is no need to manually flush/invalidate the regions because CopyBlock + // does that for us. + for (u32 line = 0; line < regs.line_count; ++line) { + const GPUVAddr source_line = regs.offset_in + static_cast<size_t>(line) * regs.pitch_in; + const GPUVAddr dest_line = regs.offset_out + static_cast<size_t>(line) * regs.pitch_out; + memory_manager.CopyBlock(dest_line, source_line, regs.line_length_in); } return; } - - UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0); - - // Perform a line-by-line copy. - // We're going to take a subrect of size (line_length_in, line_count) from the source rectangle. - // There is no need to manually flush/invalidate the regions because CopyBlock does that for us. - for (u32 line = 0; line < regs.line_count; ++line) { - const GPUVAddr source_line = regs.offset_in + static_cast<size_t>(line) * regs.pitch_in; - const GPUVAddr dest_line = regs.offset_out + static_cast<size_t>(line) * regs.pitch_out; - memory_manager.CopyBlock(dest_line, source_line, regs.line_length_in); + // TODO: allow multisized components. + auto& accelerate = rasterizer->AccessAccelerateDMA(); + const bool is_const_a_dst = regs.remap_const.dst_x == RemapConst::Swizzle::CONST_A; + const bool is_buffer_clear = remap_enabled && is_const_a_dst; + if (is_buffer_clear) { + ASSERT(regs.remap_const.component_size_minus_one == 3); + accelerate.BufferClear(regs.offset_out, regs.line_length_in, regs.remap_consta_value); + std::vector<u32> tmp_buffer(regs.line_length_in, regs.remap_consta_value); + memory_manager.WriteBlockUnsafe(regs.offset_out, reinterpret_cast<u8*>(tmp_buffer.data()), + regs.line_length_in * sizeof(u32)); + return; + } + UNIMPLEMENTED_IF(remap_enabled); + if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) { + std::vector<u8> tmp_buffer(regs.line_length_in); + memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(), regs.line_length_in); + memory_manager.WriteBlock(regs.offset_out, tmp_buffer.data(), regs.line_length_in); } } diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h index 9e457ae16..a04514425 100644 --- a/src/video_core/engines/maxwell_dma.h +++ b/src/video_core/engines/maxwell_dma.h @@ -175,7 +175,7 @@ public: static_assert(sizeof(LaunchDMA) == 4); struct RemapConst { - enum Swizzle : u32 { + enum class Swizzle : u32 { SRC_X = 0, SRC_Y = 1, SRC_Z = 2, diff --git a/src/video_core/framebuffer_config.h b/src/video_core/framebuffer_config.h index b86c3a757..b1d455e30 100644 --- a/src/video_core/framebuffer_config.h +++ b/src/video_core/framebuffer_config.h @@ -4,8 +4,10 @@ #pragma once -namespace Tegra { +#include "common/common_types.h" +#include "common/math_util.h" +namespace Tegra { /** * Struct describing framebuffer configuration */ @@ -16,6 +18,21 @@ struct FramebufferConfig { B8G8R8A8_UNORM = 5, }; + enum class TransformFlags : u32 { + /// No transform flags are set + Unset = 0x00, + /// Flip source image horizontally (around the vertical axis) + FlipH = 0x01, + /// Flip source image vertically (around the horizontal axis) + FlipV = 0x02, + /// Rotate source image 90 degrees clockwise + Rotate90 = 0x04, + /// Rotate source image 180 degrees + Rotate180 = 0x03, + /// Rotate source image 270 degrees clockwise + Rotate270 = 0x07, + }; + VAddr address{}; u32 offset{}; u32 width{}; @@ -23,7 +40,6 @@ struct FramebufferConfig { u32 stride{}; PixelFormat pixel_format{}; - using TransformFlags = Service::NVFlinger::BufferQueue::BufferTransformFlags; TransformFlags transform_flags{}; Common::Rectangle<int> crop_rect; }; diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 2ae3639b5..ab7c21a49 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -2,540 +2,913 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <array> +#include <atomic> #include <chrono> +#include <condition_variable> +#include <list> +#include <memory> #include "common/assert.h" #include "common/microprofile.h" #include "common/settings.h" #include "core/core.h" #include "core/core_timing.h" -#include "core/core_timing_util.h" #include "core/frontend/emu_window.h" #include "core/hardware_interrupt_manager.h" -#include "core/memory.h" +#include "core/hle/service/nvdrv/nvdata.h" +#include "core/hle/service/nvflinger/buffer_queue.h" #include "core/perf_stats.h" +#include "video_core/cdma_pusher.h" +#include "video_core/dma_pusher.h" #include "video_core/engines/fermi_2d.h" #include "video_core/engines/kepler_compute.h" #include "video_core/engines/kepler_memory.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/engines/maxwell_dma.h" #include "video_core/gpu.h" +#include "video_core/gpu_thread.h" #include "video_core/memory_manager.h" #include "video_core/renderer_base.h" #include "video_core/shader_notify.h" -#include "video_core/video_core.h" namespace Tegra { MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192)); -GPU::GPU(Core::System& system_, bool is_async_, bool use_nvdec_) - : system{system_}, memory_manager{std::make_unique<Tegra::MemoryManager>(system)}, - dma_pusher{std::make_unique<Tegra::DmaPusher>(system, *this)}, use_nvdec{use_nvdec_}, - maxwell_3d{std::make_unique<Engines::Maxwell3D>(system, *memory_manager)}, - fermi_2d{std::make_unique<Engines::Fermi2D>()}, - kepler_compute{std::make_unique<Engines::KeplerCompute>(system, *memory_manager)}, - maxwell_dma{std::make_unique<Engines::MaxwellDMA>(system, *memory_manager)}, - kepler_memory{std::make_unique<Engines::KeplerMemory>(system, *memory_manager)}, - shader_notify{std::make_unique<VideoCore::ShaderNotify>()}, is_async{is_async_}, - gpu_thread{system_, is_async_} {} +struct GPU::Impl { + explicit Impl(GPU& gpu_, Core::System& system_, bool is_async_, bool use_nvdec_) + : gpu{gpu_}, system{system_}, memory_manager{std::make_unique<Tegra::MemoryManager>( + system)}, + dma_pusher{std::make_unique<Tegra::DmaPusher>(system, gpu)}, use_nvdec{use_nvdec_}, + maxwell_3d{std::make_unique<Engines::Maxwell3D>(system, *memory_manager)}, + fermi_2d{std::make_unique<Engines::Fermi2D>()}, + kepler_compute{std::make_unique<Engines::KeplerCompute>(system, *memory_manager)}, + maxwell_dma{std::make_unique<Engines::MaxwellDMA>(system, *memory_manager)}, + kepler_memory{std::make_unique<Engines::KeplerMemory>(system, *memory_manager)}, + shader_notify{std::make_unique<VideoCore::ShaderNotify>()}, is_async{is_async_}, + gpu_thread{system_, is_async_} {} + + ~Impl() = default; + + /// Binds a renderer to the GPU. + void BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer_) { + renderer = std::move(renderer_); + rasterizer = renderer->ReadRasterizer(); + + memory_manager->BindRasterizer(rasterizer); + maxwell_3d->BindRasterizer(rasterizer); + fermi_2d->BindRasterizer(rasterizer); + kepler_compute->BindRasterizer(rasterizer); + maxwell_dma->BindRasterizer(rasterizer); + } -GPU::~GPU() = default; + /// Calls a GPU method. + void CallMethod(const GPU::MethodCall& method_call) { + LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method_call.method, + method_call.subchannel); + + ASSERT(method_call.subchannel < bound_engines.size()); + + if (ExecuteMethodOnEngine(method_call.method)) { + CallEngineMethod(method_call); + } else { + CallPullerMethod(method_call); + } + } + + /// Calls a GPU multivalue method. + void CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, + u32 methods_pending) { + LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method, subchannel); -void GPU::BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer_) { - renderer = std::move(renderer_); - rasterizer = renderer->ReadRasterizer(); + ASSERT(subchannel < bound_engines.size()); + + if (ExecuteMethodOnEngine(method)) { + CallEngineMultiMethod(method, subchannel, base_start, amount, methods_pending); + } else { + for (std::size_t i = 0; i < amount; i++) { + CallPullerMethod(GPU::MethodCall{ + method, + base_start[i], + subchannel, + methods_pending - static_cast<u32>(i), + }); + } + } + } + + /// Flush all current written commands into the host GPU for execution. + void FlushCommands() { + rasterizer->FlushCommands(); + } + + /// Synchronizes CPU writes with Host GPU memory. + void SyncGuestHost() { + rasterizer->SyncGuestHost(); + } + + /// Signal the ending of command list. + void OnCommandListEnd() { + if (is_async) { + // This command only applies to asynchronous GPU mode + gpu_thread.OnCommandListEnd(); + } + } + + /// Request a host GPU memory flush from the CPU. + [[nodiscard]] u64 RequestFlush(VAddr addr, std::size_t size) { + std::unique_lock lck{flush_request_mutex}; + const u64 fence = ++last_flush_fence; + flush_requests.emplace_back(fence, addr, size); + return fence; + } + + /// Obtains current flush request fence id. + [[nodiscard]] u64 CurrentFlushRequestFence() const { + return current_flush_fence.load(std::memory_order_relaxed); + } + + /// Tick pending requests within the GPU. + void TickWork() { + std::unique_lock lck{flush_request_mutex}; + while (!flush_requests.empty()) { + auto& request = flush_requests.front(); + const u64 fence = request.fence; + const VAddr addr = request.addr; + const std::size_t size = request.size; + flush_requests.pop_front(); + flush_request_mutex.unlock(); + rasterizer->FlushRegion(addr, size); + current_flush_fence.store(fence); + flush_request_mutex.lock(); + } + } + + /// Returns a reference to the Maxwell3D GPU engine. + [[nodiscard]] Engines::Maxwell3D& Maxwell3D() { + return *maxwell_3d; + } + + /// Returns a const reference to the Maxwell3D GPU engine. + [[nodiscard]] const Engines::Maxwell3D& Maxwell3D() const { + return *maxwell_3d; + } + + /// Returns a reference to the KeplerCompute GPU engine. + [[nodiscard]] Engines::KeplerCompute& KeplerCompute() { + return *kepler_compute; + } + + /// Returns a reference to the KeplerCompute GPU engine. + [[nodiscard]] const Engines::KeplerCompute& KeplerCompute() const { + return *kepler_compute; + } + + /// Returns a reference to the GPU memory manager. + [[nodiscard]] Tegra::MemoryManager& MemoryManager() { + return *memory_manager; + } + + /// Returns a const reference to the GPU memory manager. + [[nodiscard]] const Tegra::MemoryManager& MemoryManager() const { + return *memory_manager; + } + + /// Returns a reference to the GPU DMA pusher. + [[nodiscard]] Tegra::DmaPusher& DmaPusher() { + return *dma_pusher; + } + + /// Returns a const reference to the GPU DMA pusher. + [[nodiscard]] const Tegra::DmaPusher& DmaPusher() const { + return *dma_pusher; + } + + /// Returns a reference to the GPU CDMA pusher. + [[nodiscard]] Tegra::CDmaPusher& CDmaPusher() { + return *cdma_pusher; + } + + /// Returns a const reference to the GPU CDMA pusher. + [[nodiscard]] const Tegra::CDmaPusher& CDmaPusher() const { + return *cdma_pusher; + } + + /// Returns a reference to the underlying renderer. + [[nodiscard]] VideoCore::RendererBase& Renderer() { + return *renderer; + } + + /// Returns a const reference to the underlying renderer. + [[nodiscard]] const VideoCore::RendererBase& Renderer() const { + return *renderer; + } + + /// Returns a reference to the shader notifier. + [[nodiscard]] VideoCore::ShaderNotify& ShaderNotify() { + return *shader_notify; + } + + /// Returns a const reference to the shader notifier. + [[nodiscard]] const VideoCore::ShaderNotify& ShaderNotify() const { + return *shader_notify; + } + + /// Allows the CPU/NvFlinger to wait on the GPU before presenting a frame. + void WaitFence(u32 syncpoint_id, u32 value) { + // Synced GPU, is always in sync + if (!is_async) { + return; + } + if (syncpoint_id == UINT32_MAX) { + // TODO: Research what this does. + LOG_ERROR(HW_GPU, "Waiting for syncpoint -1 not implemented"); + return; + } + MICROPROFILE_SCOPE(GPU_wait); + std::unique_lock lock{sync_mutex}; + sync_cv.wait(lock, [=, this] { + if (shutting_down.load(std::memory_order_relaxed)) { + // We're shutting down, ensure no threads continue to wait for the next syncpoint + return true; + } + return syncpoints.at(syncpoint_id).load() >= value; + }); + } + + void IncrementSyncPoint(u32 syncpoint_id) { + auto& syncpoint = syncpoints.at(syncpoint_id); + syncpoint++; + std::lock_guard lock{sync_mutex}; + sync_cv.notify_all(); + auto& interrupt = syncpt_interrupts.at(syncpoint_id); + if (!interrupt.empty()) { + u32 value = syncpoint.load(); + auto it = interrupt.begin(); + while (it != interrupt.end()) { + if (value >= *it) { + TriggerCpuInterrupt(syncpoint_id, *it); + it = interrupt.erase(it); + continue; + } + it++; + } + } + } + + [[nodiscard]] u32 GetSyncpointValue(u32 syncpoint_id) const { + return syncpoints.at(syncpoint_id).load(); + } + + void RegisterSyncptInterrupt(u32 syncpoint_id, u32 value) { + std::lock_guard lock{sync_mutex}; + auto& interrupt = syncpt_interrupts.at(syncpoint_id); + bool contains = std::any_of(interrupt.begin(), interrupt.end(), + [value](u32 in_value) { return in_value == value; }); + if (contains) { + return; + } + interrupt.emplace_back(value); + } + + [[nodiscard]] bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value) { + std::lock_guard lock{sync_mutex}; + auto& interrupt = syncpt_interrupts.at(syncpoint_id); + const auto iter = + std::find_if(interrupt.begin(), interrupt.end(), + [value](u32 interrupt_value) { return value == interrupt_value; }); + + if (iter == interrupt.end()) { + return false; + } + interrupt.erase(iter); + return true; + } + + [[nodiscard]] u64 GetTicks() const { + // This values were reversed engineered by fincs from NVN + // The gpu clock is reported in units of 385/625 nanoseconds + constexpr u64 gpu_ticks_num = 384; + constexpr u64 gpu_ticks_den = 625; + + u64 nanoseconds = system.CoreTiming().GetGlobalTimeNs().count(); + if (Settings::values.use_fast_gpu_time.GetValue()) { + nanoseconds /= 256; + } + const u64 nanoseconds_num = nanoseconds / gpu_ticks_den; + const u64 nanoseconds_rem = nanoseconds % gpu_ticks_den; + return nanoseconds_num * gpu_ticks_num + (nanoseconds_rem * gpu_ticks_num) / gpu_ticks_den; + } + + [[nodiscard]] bool IsAsync() const { + return is_async; + } + + [[nodiscard]] bool UseNvdec() const { + return use_nvdec; + } + + void RendererFrameEndNotify() { + system.GetPerfStats().EndGameFrame(); + } + + /// Performs any additional setup necessary in order to begin GPU emulation. + /// This can be used to launch any necessary threads and register any necessary + /// core timing events. + void Start() { + gpu_thread.StartThread(*renderer, renderer->Context(), *dma_pusher); + cpu_context = renderer->GetRenderWindow().CreateSharedContext(); + cpu_context->MakeCurrent(); + } + + /// Obtain the CPU Context + void ObtainContext() { + cpu_context->MakeCurrent(); + } + + /// Release the CPU Context + void ReleaseContext() { + cpu_context->DoneCurrent(); + } + + /// Push GPU command entries to be processed + void PushGPUEntries(Tegra::CommandList&& entries) { + gpu_thread.SubmitList(std::move(entries)); + } + + /// Push GPU command buffer entries to be processed + void PushCommandBuffer(Tegra::ChCommandHeaderList& entries) { + if (!use_nvdec) { + return; + } + + if (!cdma_pusher) { + cdma_pusher = std::make_unique<Tegra::CDmaPusher>(gpu); + } + + // SubmitCommandBuffer would make the nvdec operations async, this is not currently working + // TODO(ameerj): RE proper async nvdec operation + // gpu_thread.SubmitCommandBuffer(std::move(entries)); + + cdma_pusher->ProcessEntries(std::move(entries)); + } + + /// Frees the CDMAPusher instance to free up resources + void ClearCdmaInstance() { + cdma_pusher.reset(); + } + + /// Swap buffers (render frame) + void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { + gpu_thread.SwapBuffers(framebuffer); + } + + /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory + void FlushRegion(VAddr addr, u64 size) { + gpu_thread.FlushRegion(addr, size); + } + + /// Notify rasterizer that any caches of the specified region should be invalidated + void InvalidateRegion(VAddr addr, u64 size) { + gpu_thread.InvalidateRegion(addr, size); + } + + /// Notify rasterizer that any caches of the specified region should be flushed and invalidated + void FlushAndInvalidateRegion(VAddr addr, u64 size) { + gpu_thread.FlushAndInvalidateRegion(addr, size); + } + + void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const { + auto& interrupt_manager = system.InterruptManager(); + interrupt_manager.GPUInterruptSyncpt(syncpoint_id, value); + } + + void ProcessBindMethod(const GPU::MethodCall& method_call) { + // Bind the current subchannel to the desired engine id. + LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", method_call.subchannel, + method_call.argument); + const auto engine_id = static_cast<EngineID>(method_call.argument); + bound_engines[method_call.subchannel] = static_cast<EngineID>(engine_id); + switch (engine_id) { + case EngineID::FERMI_TWOD_A: + dma_pusher->BindSubchannel(fermi_2d.get(), method_call.subchannel); + break; + case EngineID::MAXWELL_B: + dma_pusher->BindSubchannel(maxwell_3d.get(), method_call.subchannel); + break; + case EngineID::KEPLER_COMPUTE_B: + dma_pusher->BindSubchannel(kepler_compute.get(), method_call.subchannel); + break; + case EngineID::MAXWELL_DMA_COPY_A: + dma_pusher->BindSubchannel(maxwell_dma.get(), method_call.subchannel); + break; + case EngineID::KEPLER_INLINE_TO_MEMORY_B: + dma_pusher->BindSubchannel(kepler_memory.get(), method_call.subchannel); + break; + default: + UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id); + } + } - memory_manager->BindRasterizer(rasterizer); - maxwell_3d->BindRasterizer(rasterizer); - fermi_2d->BindRasterizer(rasterizer); - kepler_compute->BindRasterizer(rasterizer); - maxwell_dma->BindRasterizer(rasterizer); + void ProcessFenceActionMethod() { + switch (regs.fence_action.op) { + case GPU::FenceOperation::Acquire: + WaitFence(regs.fence_action.syncpoint_id, regs.fence_value); + break; + case GPU::FenceOperation::Increment: + IncrementSyncPoint(regs.fence_action.syncpoint_id); + break; + default: + UNIMPLEMENTED_MSG("Unimplemented operation {}", regs.fence_action.op.Value()); + } + } + + void ProcessWaitForInterruptMethod() { + // TODO(bunnei) ImplementMe + LOG_WARNING(HW_GPU, "(STUBBED) called"); + } + + void ProcessSemaphoreTriggerMethod() { + const auto semaphoreOperationMask = 0xF; + const auto op = + static_cast<GpuSemaphoreOperation>(regs.semaphore_trigger & semaphoreOperationMask); + if (op == GpuSemaphoreOperation::WriteLong) { + struct Block { + u32 sequence; + u32 zeros = 0; + u64 timestamp; + }; + + Block block{}; + block.sequence = regs.semaphore_sequence; + // TODO(Kmather73): Generate a real GPU timestamp and write it here instead of + // CoreTiming + block.timestamp = GetTicks(); + memory_manager->WriteBlock(regs.semaphore_address.SemaphoreAddress(), &block, + sizeof(block)); + } else { + const u32 word{memory_manager->Read<u32>(regs.semaphore_address.SemaphoreAddress())}; + if ((op == GpuSemaphoreOperation::AcquireEqual && word == regs.semaphore_sequence) || + (op == GpuSemaphoreOperation::AcquireGequal && + static_cast<s32>(word - regs.semaphore_sequence) > 0) || + (op == GpuSemaphoreOperation::AcquireMask && (word & regs.semaphore_sequence))) { + // Nothing to do in this case + } else { + regs.acquire_source = true; + regs.acquire_value = regs.semaphore_sequence; + if (op == GpuSemaphoreOperation::AcquireEqual) { + regs.acquire_active = true; + regs.acquire_mode = false; + } else if (op == GpuSemaphoreOperation::AcquireGequal) { + regs.acquire_active = true; + regs.acquire_mode = true; + } else if (op == GpuSemaphoreOperation::AcquireMask) { + // TODO(kemathe) The acquire mask operation waits for a value that, ANDed with + // semaphore_sequence, gives a non-0 result + LOG_ERROR(HW_GPU, "Invalid semaphore operation AcquireMask not implemented"); + } else { + LOG_ERROR(HW_GPU, "Invalid semaphore operation"); + } + } + } + } + + void ProcessSemaphoreRelease() { + memory_manager->Write<u32>(regs.semaphore_address.SemaphoreAddress(), + regs.semaphore_release); + } + + void ProcessSemaphoreAcquire() { + const u32 word = memory_manager->Read<u32>(regs.semaphore_address.SemaphoreAddress()); + const auto value = regs.semaphore_acquire; + if (word != value) { + regs.acquire_active = true; + regs.acquire_value = value; + // TODO(kemathe73) figure out how to do the acquire_timeout + regs.acquire_mode = false; + regs.acquire_source = false; + } + } + + /// Calls a GPU puller method. + void CallPullerMethod(const GPU::MethodCall& method_call) { + regs.reg_array[method_call.method] = method_call.argument; + const auto method = static_cast<BufferMethods>(method_call.method); + + switch (method) { + case BufferMethods::BindObject: { + ProcessBindMethod(method_call); + break; + } + case BufferMethods::Nop: + case BufferMethods::SemaphoreAddressHigh: + case BufferMethods::SemaphoreAddressLow: + case BufferMethods::SemaphoreSequence: + case BufferMethods::UnkCacheFlush: + case BufferMethods::WrcacheFlush: + case BufferMethods::FenceValue: + break; + case BufferMethods::RefCnt: + rasterizer->SignalReference(); + break; + case BufferMethods::FenceAction: + ProcessFenceActionMethod(); + break; + case BufferMethods::WaitForInterrupt: + ProcessWaitForInterruptMethod(); + break; + case BufferMethods::SemaphoreTrigger: { + ProcessSemaphoreTriggerMethod(); + break; + } + case BufferMethods::NotifyIntr: { + // TODO(Kmather73): Research and implement this method. + LOG_ERROR(HW_GPU, "Special puller engine method NotifyIntr not implemented"); + break; + } + case BufferMethods::Unk28: { + // TODO(Kmather73): Research and implement this method. + LOG_ERROR(HW_GPU, "Special puller engine method Unk28 not implemented"); + break; + } + case BufferMethods::SemaphoreAcquire: { + ProcessSemaphoreAcquire(); + break; + } + case BufferMethods::SemaphoreRelease: { + ProcessSemaphoreRelease(); + break; + } + case BufferMethods::Yield: { + // TODO(Kmather73): Research and implement this method. + LOG_ERROR(HW_GPU, "Special puller engine method Yield not implemented"); + break; + } + default: + LOG_ERROR(HW_GPU, "Special puller engine method {:X} not implemented", method); + break; + } + } + + /// Calls a GPU engine method. + void CallEngineMethod(const GPU::MethodCall& method_call) { + const EngineID engine = bound_engines[method_call.subchannel]; + + switch (engine) { + case EngineID::FERMI_TWOD_A: + fermi_2d->CallMethod(method_call.method, method_call.argument, + method_call.IsLastCall()); + break; + case EngineID::MAXWELL_B: + maxwell_3d->CallMethod(method_call.method, method_call.argument, + method_call.IsLastCall()); + break; + case EngineID::KEPLER_COMPUTE_B: + kepler_compute->CallMethod(method_call.method, method_call.argument, + method_call.IsLastCall()); + break; + case EngineID::MAXWELL_DMA_COPY_A: + maxwell_dma->CallMethod(method_call.method, method_call.argument, + method_call.IsLastCall()); + break; + case EngineID::KEPLER_INLINE_TO_MEMORY_B: + kepler_memory->CallMethod(method_call.method, method_call.argument, + method_call.IsLastCall()); + break; + default: + UNIMPLEMENTED_MSG("Unimplemented engine"); + } + } + + /// Calls a GPU engine multivalue method. + void CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, + u32 methods_pending) { + const EngineID engine = bound_engines[subchannel]; + + switch (engine) { + case EngineID::FERMI_TWOD_A: + fermi_2d->CallMultiMethod(method, base_start, amount, methods_pending); + break; + case EngineID::MAXWELL_B: + maxwell_3d->CallMultiMethod(method, base_start, amount, methods_pending); + break; + case EngineID::KEPLER_COMPUTE_B: + kepler_compute->CallMultiMethod(method, base_start, amount, methods_pending); + break; + case EngineID::MAXWELL_DMA_COPY_A: + maxwell_dma->CallMultiMethod(method, base_start, amount, methods_pending); + break; + case EngineID::KEPLER_INLINE_TO_MEMORY_B: + kepler_memory->CallMultiMethod(method, base_start, amount, methods_pending); + break; + default: + UNIMPLEMENTED_MSG("Unimplemented engine"); + } + } + + /// Determines where the method should be executed. + [[nodiscard]] bool ExecuteMethodOnEngine(u32 method) { + const auto buffer_method = static_cast<BufferMethods>(method); + return buffer_method >= BufferMethods::NonPullerMethods; + } + + struct Regs { + static constexpr size_t NUM_REGS = 0x40; + + union { + struct { + INSERT_PADDING_WORDS_NOINIT(0x4); + struct { + u32 address_high; + u32 address_low; + + [[nodiscard]] GPUVAddr SemaphoreAddress() const { + return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | + address_low); + } + } semaphore_address; + + u32 semaphore_sequence; + u32 semaphore_trigger; + INSERT_PADDING_WORDS_NOINIT(0xC); + + // The pusher and the puller share the reference counter, the pusher only has read + // access + u32 reference_count; + INSERT_PADDING_WORDS_NOINIT(0x5); + + u32 semaphore_acquire; + u32 semaphore_release; + u32 fence_value; + GPU::FenceAction fence_action; + INSERT_PADDING_WORDS_NOINIT(0xE2); + + // Puller state + u32 acquire_mode; + u32 acquire_source; + u32 acquire_active; + u32 acquire_timeout; + u32 acquire_value; + }; + std::array<u32, NUM_REGS> reg_array; + }; + } regs{}; + + GPU& gpu; + Core::System& system; + std::unique_ptr<Tegra::MemoryManager> memory_manager; + std::unique_ptr<Tegra::DmaPusher> dma_pusher; + std::unique_ptr<Tegra::CDmaPusher> cdma_pusher; + std::unique_ptr<VideoCore::RendererBase> renderer; + VideoCore::RasterizerInterface* rasterizer = nullptr; + const bool use_nvdec; + + /// Mapping of command subchannels to their bound engine ids + std::array<EngineID, 8> bound_engines{}; + /// 3D engine + std::unique_ptr<Engines::Maxwell3D> maxwell_3d; + /// 2D engine + std::unique_ptr<Engines::Fermi2D> fermi_2d; + /// Compute engine + std::unique_ptr<Engines::KeplerCompute> kepler_compute; + /// DMA engine + std::unique_ptr<Engines::MaxwellDMA> maxwell_dma; + /// Inline memory engine + std::unique_ptr<Engines::KeplerMemory> kepler_memory; + /// Shader build notifier + std::unique_ptr<VideoCore::ShaderNotify> shader_notify; + /// When true, we are about to shut down emulation session, so terminate outstanding tasks + std::atomic_bool shutting_down{}; + + std::array<std::atomic<u32>, Service::Nvidia::MaxSyncPoints> syncpoints{}; + + std::array<std::list<u32>, Service::Nvidia::MaxSyncPoints> syncpt_interrupts; + + std::mutex sync_mutex; + std::mutex device_mutex; + + std::condition_variable sync_cv; + + struct FlushRequest { + explicit FlushRequest(u64 fence_, VAddr addr_, std::size_t size_) + : fence{fence_}, addr{addr_}, size{size_} {} + u64 fence; + VAddr addr; + std::size_t size; + }; + + std::list<FlushRequest> flush_requests; + std::atomic<u64> current_flush_fence{}; + u64 last_flush_fence{}; + std::mutex flush_request_mutex; + + const bool is_async; + + VideoCommon::GPUThread::ThreadManager gpu_thread; + std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context; + +#define ASSERT_REG_POSITION(field_name, position) \ + static_assert(offsetof(Regs, field_name) == position * 4, \ + "Field " #field_name " has invalid position") + + ASSERT_REG_POSITION(semaphore_address, 0x4); + ASSERT_REG_POSITION(semaphore_sequence, 0x6); + ASSERT_REG_POSITION(semaphore_trigger, 0x7); + ASSERT_REG_POSITION(reference_count, 0x14); + ASSERT_REG_POSITION(semaphore_acquire, 0x1A); + ASSERT_REG_POSITION(semaphore_release, 0x1B); + ASSERT_REG_POSITION(fence_value, 0x1C); + ASSERT_REG_POSITION(fence_action, 0x1D); + + ASSERT_REG_POSITION(acquire_mode, 0x100); + ASSERT_REG_POSITION(acquire_source, 0x101); + ASSERT_REG_POSITION(acquire_active, 0x102); + ASSERT_REG_POSITION(acquire_timeout, 0x103); + ASSERT_REG_POSITION(acquire_value, 0x104); + +#undef ASSERT_REG_POSITION + + enum class GpuSemaphoreOperation { + AcquireEqual = 0x1, + WriteLong = 0x2, + AcquireGequal = 0x4, + AcquireMask = 0x8, + }; +}; + +GPU::GPU(Core::System& system, bool is_async, bool use_nvdec) + : impl{std::make_unique<Impl>(*this, system, is_async, use_nvdec)} {} + +GPU::~GPU() = default; + +void GPU::BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer) { + impl->BindRenderer(std::move(renderer)); } -Engines::Maxwell3D& GPU::Maxwell3D() { - return *maxwell_3d; +void GPU::CallMethod(const MethodCall& method_call) { + impl->CallMethod(method_call); } -const Engines::Maxwell3D& GPU::Maxwell3D() const { - return *maxwell_3d; +void GPU::CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, + u32 methods_pending) { + impl->CallMultiMethod(method, subchannel, base_start, amount, methods_pending); } -Engines::KeplerCompute& GPU::KeplerCompute() { - return *kepler_compute; +void GPU::FlushCommands() { + impl->FlushCommands(); } -const Engines::KeplerCompute& GPU::KeplerCompute() const { - return *kepler_compute; +void GPU::SyncGuestHost() { + impl->SyncGuestHost(); } -MemoryManager& GPU::MemoryManager() { - return *memory_manager; +void GPU::OnCommandListEnd() { + impl->OnCommandListEnd(); } -const MemoryManager& GPU::MemoryManager() const { - return *memory_manager; +u64 GPU::RequestFlush(VAddr addr, std::size_t size) { + return impl->RequestFlush(addr, size); } -DmaPusher& GPU::DmaPusher() { - return *dma_pusher; +u64 GPU::CurrentFlushRequestFence() const { + return impl->CurrentFlushRequestFence(); } -Tegra::CDmaPusher& GPU::CDmaPusher() { - return *cdma_pusher; +void GPU::TickWork() { + impl->TickWork(); } -const DmaPusher& GPU::DmaPusher() const { - return *dma_pusher; +Engines::Maxwell3D& GPU::Maxwell3D() { + return impl->Maxwell3D(); } -const Tegra::CDmaPusher& GPU::CDmaPusher() const { - return *cdma_pusher; +const Engines::Maxwell3D& GPU::Maxwell3D() const { + return impl->Maxwell3D(); } -void GPU::WaitFence(u32 syncpoint_id, u32 value) { - // Synced GPU, is always in sync - if (!is_async) { - return; - } - if (syncpoint_id == UINT32_MAX) { - // TODO: Research what this does. - LOG_ERROR(HW_GPU, "Waiting for syncpoint -1 not implemented"); - return; - } - MICROPROFILE_SCOPE(GPU_wait); - std::unique_lock lock{sync_mutex}; - sync_cv.wait(lock, [=, this] { - if (shutting_down.load(std::memory_order_relaxed)) { - // We're shutting down, ensure no threads continue to wait for the next syncpoint - return true; - } - return syncpoints.at(syncpoint_id).load() >= value; - }); -} - -void GPU::IncrementSyncPoint(const u32 syncpoint_id) { - auto& syncpoint = syncpoints.at(syncpoint_id); - syncpoint++; - std::lock_guard lock{sync_mutex}; - sync_cv.notify_all(); - auto& interrupt = syncpt_interrupts.at(syncpoint_id); - if (!interrupt.empty()) { - u32 value = syncpoint.load(); - auto it = interrupt.begin(); - while (it != interrupt.end()) { - if (value >= *it) { - TriggerCpuInterrupt(syncpoint_id, *it); - it = interrupt.erase(it); - continue; - } - it++; - } - } +Engines::KeplerCompute& GPU::KeplerCompute() { + return impl->KeplerCompute(); } -u32 GPU::GetSyncpointValue(const u32 syncpoint_id) const { - return syncpoints.at(syncpoint_id).load(); +const Engines::KeplerCompute& GPU::KeplerCompute() const { + return impl->KeplerCompute(); } -void GPU::RegisterSyncptInterrupt(const u32 syncpoint_id, const u32 value) { - auto& interrupt = syncpt_interrupts.at(syncpoint_id); - bool contains = std::any_of(interrupt.begin(), interrupt.end(), - [value](u32 in_value) { return in_value == value; }); - if (contains) { - return; - } - interrupt.emplace_back(value); +Tegra::MemoryManager& GPU::MemoryManager() { + return impl->MemoryManager(); } -bool GPU::CancelSyncptInterrupt(const u32 syncpoint_id, const u32 value) { - std::lock_guard lock{sync_mutex}; - auto& interrupt = syncpt_interrupts.at(syncpoint_id); - const auto iter = - std::find_if(interrupt.begin(), interrupt.end(), - [value](u32 interrupt_value) { return value == interrupt_value; }); +const Tegra::MemoryManager& GPU::MemoryManager() const { + return impl->MemoryManager(); +} - if (iter == interrupt.end()) { - return false; - } - interrupt.erase(iter); - return true; +Tegra::DmaPusher& GPU::DmaPusher() { + return impl->DmaPusher(); } -u64 GPU::RequestFlush(VAddr addr, std::size_t size) { - std::unique_lock lck{flush_request_mutex}; - const u64 fence = ++last_flush_fence; - flush_requests.emplace_back(fence, addr, size); - return fence; +const Tegra::DmaPusher& GPU::DmaPusher() const { + return impl->DmaPusher(); } -void GPU::TickWork() { - std::unique_lock lck{flush_request_mutex}; - while (!flush_requests.empty()) { - auto& request = flush_requests.front(); - const u64 fence = request.fence; - const VAddr addr = request.addr; - const std::size_t size = request.size; - flush_requests.pop_front(); - flush_request_mutex.unlock(); - rasterizer->FlushRegion(addr, size); - current_flush_fence.store(fence); - flush_request_mutex.lock(); - } +Tegra::CDmaPusher& GPU::CDmaPusher() { + return impl->CDmaPusher(); } -u64 GPU::GetTicks() const { - // This values were reversed engineered by fincs from NVN - // The gpu clock is reported in units of 385/625 nanoseconds - constexpr u64 gpu_ticks_num = 384; - constexpr u64 gpu_ticks_den = 625; +const Tegra::CDmaPusher& GPU::CDmaPusher() const { + return impl->CDmaPusher(); +} - u64 nanoseconds = system.CoreTiming().GetGlobalTimeNs().count(); - if (Settings::values.use_fast_gpu_time.GetValue()) { - nanoseconds /= 256; - } - const u64 nanoseconds_num = nanoseconds / gpu_ticks_den; - const u64 nanoseconds_rem = nanoseconds % gpu_ticks_den; - return nanoseconds_num * gpu_ticks_num + (nanoseconds_rem * gpu_ticks_num) / gpu_ticks_den; +VideoCore::RendererBase& GPU::Renderer() { + return impl->Renderer(); } -void GPU::RendererFrameEndNotify() { - system.GetPerfStats().EndGameFrame(); +const VideoCore::RendererBase& GPU::Renderer() const { + return impl->Renderer(); } -void GPU::FlushCommands() { - rasterizer->FlushCommands(); +VideoCore::ShaderNotify& GPU::ShaderNotify() { + return impl->ShaderNotify(); } -void GPU::SyncGuestHost() { - rasterizer->SyncGuestHost(); +const VideoCore::ShaderNotify& GPU::ShaderNotify() const { + return impl->ShaderNotify(); } -enum class GpuSemaphoreOperation { - AcquireEqual = 0x1, - WriteLong = 0x2, - AcquireGequal = 0x4, - AcquireMask = 0x8, -}; +void GPU::WaitFence(u32 syncpoint_id, u32 value) { + impl->WaitFence(syncpoint_id, value); +} -void GPU::CallMethod(const MethodCall& method_call) { - LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method_call.method, - method_call.subchannel); +void GPU::IncrementSyncPoint(u32 syncpoint_id) { + impl->IncrementSyncPoint(syncpoint_id); +} - ASSERT(method_call.subchannel < bound_engines.size()); +u32 GPU::GetSyncpointValue(u32 syncpoint_id) const { + return impl->GetSyncpointValue(syncpoint_id); +} - if (ExecuteMethodOnEngine(method_call.method)) { - CallEngineMethod(method_call); - } else { - CallPullerMethod(method_call); - } +void GPU::RegisterSyncptInterrupt(u32 syncpoint_id, u32 value) { + impl->RegisterSyncptInterrupt(syncpoint_id, value); } -void GPU::CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, - u32 methods_pending) { - LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method, subchannel); - - ASSERT(subchannel < bound_engines.size()); - - if (ExecuteMethodOnEngine(method)) { - CallEngineMultiMethod(method, subchannel, base_start, amount, methods_pending); - } else { - for (std::size_t i = 0; i < amount; i++) { - CallPullerMethod(MethodCall{ - method, - base_start[i], - subchannel, - methods_pending - static_cast<u32>(i), - }); - } - } +bool GPU::CancelSyncptInterrupt(u32 syncpoint_id, u32 value) { + return impl->CancelSyncptInterrupt(syncpoint_id, value); } -bool GPU::ExecuteMethodOnEngine(u32 method) { - const auto buffer_method = static_cast<BufferMethods>(method); - return buffer_method >= BufferMethods::NonPullerMethods; -} - -void GPU::CallPullerMethod(const MethodCall& method_call) { - regs.reg_array[method_call.method] = method_call.argument; - const auto method = static_cast<BufferMethods>(method_call.method); - - switch (method) { - case BufferMethods::BindObject: { - ProcessBindMethod(method_call); - break; - } - case BufferMethods::Nop: - case BufferMethods::SemaphoreAddressHigh: - case BufferMethods::SemaphoreAddressLow: - case BufferMethods::SemaphoreSequence: - case BufferMethods::UnkCacheFlush: - case BufferMethods::WrcacheFlush: - case BufferMethods::FenceValue: - break; - case BufferMethods::RefCnt: - rasterizer->SignalReference(); - break; - case BufferMethods::FenceAction: - ProcessFenceActionMethod(); - break; - case BufferMethods::WaitForInterrupt: - ProcessWaitForInterruptMethod(); - break; - case BufferMethods::SemaphoreTrigger: { - ProcessSemaphoreTriggerMethod(); - break; - } - case BufferMethods::NotifyIntr: { - // TODO(Kmather73): Research and implement this method. - LOG_ERROR(HW_GPU, "Special puller engine method NotifyIntr not implemented"); - break; - } - case BufferMethods::Unk28: { - // TODO(Kmather73): Research and implement this method. - LOG_ERROR(HW_GPU, "Special puller engine method Unk28 not implemented"); - break; - } - case BufferMethods::SemaphoreAcquire: { - ProcessSemaphoreAcquire(); - break; - } - case BufferMethods::SemaphoreRelease: { - ProcessSemaphoreRelease(); - break; - } - case BufferMethods::Yield: { - // TODO(Kmather73): Research and implement this method. - LOG_ERROR(HW_GPU, "Special puller engine method Yield not implemented"); - break; - } - default: - LOG_ERROR(HW_GPU, "Special puller engine method {:X} not implemented", method); - break; - } -} - -void GPU::CallEngineMethod(const MethodCall& method_call) { - const EngineID engine = bound_engines[method_call.subchannel]; - - switch (engine) { - case EngineID::FERMI_TWOD_A: - fermi_2d->CallMethod(method_call.method, method_call.argument, method_call.IsLastCall()); - break; - case EngineID::MAXWELL_B: - maxwell_3d->CallMethod(method_call.method, method_call.argument, method_call.IsLastCall()); - break; - case EngineID::KEPLER_COMPUTE_B: - kepler_compute->CallMethod(method_call.method, method_call.argument, - method_call.IsLastCall()); - break; - case EngineID::MAXWELL_DMA_COPY_A: - maxwell_dma->CallMethod(method_call.method, method_call.argument, method_call.IsLastCall()); - break; - case EngineID::KEPLER_INLINE_TO_MEMORY_B: - kepler_memory->CallMethod(method_call.method, method_call.argument, - method_call.IsLastCall()); - break; - default: - UNIMPLEMENTED_MSG("Unimplemented engine"); - } -} - -void GPU::CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, - u32 methods_pending) { - const EngineID engine = bound_engines[subchannel]; - - switch (engine) { - case EngineID::FERMI_TWOD_A: - fermi_2d->CallMultiMethod(method, base_start, amount, methods_pending); - break; - case EngineID::MAXWELL_B: - maxwell_3d->CallMultiMethod(method, base_start, amount, methods_pending); - break; - case EngineID::KEPLER_COMPUTE_B: - kepler_compute->CallMultiMethod(method, base_start, amount, methods_pending); - break; - case EngineID::MAXWELL_DMA_COPY_A: - maxwell_dma->CallMultiMethod(method, base_start, amount, methods_pending); - break; - case EngineID::KEPLER_INLINE_TO_MEMORY_B: - kepler_memory->CallMultiMethod(method, base_start, amount, methods_pending); - break; - default: - UNIMPLEMENTED_MSG("Unimplemented engine"); - } -} - -void GPU::ProcessBindMethod(const MethodCall& method_call) { - // Bind the current subchannel to the desired engine id. - LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", method_call.subchannel, - method_call.argument); - const auto engine_id = static_cast<EngineID>(method_call.argument); - bound_engines[method_call.subchannel] = static_cast<EngineID>(engine_id); - switch (engine_id) { - case EngineID::FERMI_TWOD_A: - dma_pusher->BindSubchannel(fermi_2d.get(), method_call.subchannel); - break; - case EngineID::MAXWELL_B: - dma_pusher->BindSubchannel(maxwell_3d.get(), method_call.subchannel); - break; - case EngineID::KEPLER_COMPUTE_B: - dma_pusher->BindSubchannel(kepler_compute.get(), method_call.subchannel); - break; - case EngineID::MAXWELL_DMA_COPY_A: - dma_pusher->BindSubchannel(maxwell_dma.get(), method_call.subchannel); - break; - case EngineID::KEPLER_INLINE_TO_MEMORY_B: - dma_pusher->BindSubchannel(kepler_memory.get(), method_call.subchannel); - break; - default: - UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id); - } -} - -void GPU::ProcessFenceActionMethod() { - switch (regs.fence_action.op) { - case FenceOperation::Acquire: - WaitFence(regs.fence_action.syncpoint_id, regs.fence_value); - break; - case FenceOperation::Increment: - IncrementSyncPoint(regs.fence_action.syncpoint_id); - break; - default: - UNIMPLEMENTED_MSG("Unimplemented operation {}", regs.fence_action.op.Value()); - } -} - -void GPU::ProcessWaitForInterruptMethod() { - // TODO(bunnei) ImplementMe - LOG_WARNING(HW_GPU, "(STUBBED) called"); -} - -void GPU::ProcessSemaphoreTriggerMethod() { - const auto semaphoreOperationMask = 0xF; - const auto op = - static_cast<GpuSemaphoreOperation>(regs.semaphore_trigger & semaphoreOperationMask); - if (op == GpuSemaphoreOperation::WriteLong) { - struct Block { - u32 sequence; - u32 zeros = 0; - u64 timestamp; - }; +u64 GPU::GetTicks() const { + return impl->GetTicks(); +} - Block block{}; - block.sequence = regs.semaphore_sequence; - // TODO(Kmather73): Generate a real GPU timestamp and write it here instead of - // CoreTiming - block.timestamp = GetTicks(); - memory_manager->WriteBlock(regs.semaphore_address.SemaphoreAddress(), &block, - sizeof(block)); - } else { - const u32 word{memory_manager->Read<u32>(regs.semaphore_address.SemaphoreAddress())}; - if ((op == GpuSemaphoreOperation::AcquireEqual && word == regs.semaphore_sequence) || - (op == GpuSemaphoreOperation::AcquireGequal && - static_cast<s32>(word - regs.semaphore_sequence) > 0) || - (op == GpuSemaphoreOperation::AcquireMask && (word & regs.semaphore_sequence))) { - // Nothing to do in this case - } else { - regs.acquire_source = true; - regs.acquire_value = regs.semaphore_sequence; - if (op == GpuSemaphoreOperation::AcquireEqual) { - regs.acquire_active = true; - regs.acquire_mode = false; - } else if (op == GpuSemaphoreOperation::AcquireGequal) { - regs.acquire_active = true; - regs.acquire_mode = true; - } else if (op == GpuSemaphoreOperation::AcquireMask) { - // TODO(kemathe) The acquire mask operation waits for a value that, ANDed with - // semaphore_sequence, gives a non-0 result - LOG_ERROR(HW_GPU, "Invalid semaphore operation AcquireMask not implemented"); - } else { - LOG_ERROR(HW_GPU, "Invalid semaphore operation"); - } - } - } +bool GPU::IsAsync() const { + return impl->IsAsync(); } -void GPU::ProcessSemaphoreRelease() { - memory_manager->Write<u32>(regs.semaphore_address.SemaphoreAddress(), regs.semaphore_release); +bool GPU::UseNvdec() const { + return impl->UseNvdec(); } -void GPU::ProcessSemaphoreAcquire() { - const u32 word = memory_manager->Read<u32>(regs.semaphore_address.SemaphoreAddress()); - const auto value = regs.semaphore_acquire; - if (word != value) { - regs.acquire_active = true; - regs.acquire_value = value; - // TODO(kemathe73) figure out how to do the acquire_timeout - regs.acquire_mode = false; - regs.acquire_source = false; - } +void GPU::RendererFrameEndNotify() { + impl->RendererFrameEndNotify(); } void GPU::Start() { - gpu_thread.StartThread(*renderer, renderer->Context(), *dma_pusher); - cpu_context = renderer->GetRenderWindow().CreateSharedContext(); - cpu_context->MakeCurrent(); + impl->Start(); } void GPU::ObtainContext() { - cpu_context->MakeCurrent(); + impl->ObtainContext(); } void GPU::ReleaseContext() { - cpu_context->DoneCurrent(); + impl->ReleaseContext(); } void GPU::PushGPUEntries(Tegra::CommandList&& entries) { - gpu_thread.SubmitList(std::move(entries)); + impl->PushGPUEntries(std::move(entries)); } void GPU::PushCommandBuffer(Tegra::ChCommandHeaderList& entries) { - if (!use_nvdec) { - return; - } - - if (!cdma_pusher) { - cdma_pusher = std::make_unique<Tegra::CDmaPusher>(*this); - } - - // SubmitCommandBuffer would make the nvdec operations async, this is not currently working - // TODO(ameerj): RE proper async nvdec operation - // gpu_thread.SubmitCommandBuffer(std::move(entries)); - - cdma_pusher->ProcessEntries(std::move(entries)); + impl->PushCommandBuffer(entries); } void GPU::ClearCdmaInstance() { - cdma_pusher.reset(); + impl->ClearCdmaInstance(); } void GPU::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { - gpu_thread.SwapBuffers(framebuffer); + impl->SwapBuffers(framebuffer); } void GPU::FlushRegion(VAddr addr, u64 size) { - gpu_thread.FlushRegion(addr, size); + impl->FlushRegion(addr, size); } void GPU::InvalidateRegion(VAddr addr, u64 size) { - gpu_thread.InvalidateRegion(addr, size); + impl->InvalidateRegion(addr, size); } void GPU::FlushAndInvalidateRegion(VAddr addr, u64 size) { - gpu_thread.FlushAndInvalidateRegion(addr, size); -} - -void GPU::TriggerCpuInterrupt(const u32 syncpoint_id, const u32 value) const { - auto& interrupt_manager = system.InterruptManager(); - interrupt_manager.GPUInterruptSyncpt(syncpoint_id, value); -} - -void GPU::OnCommandListEnd() { - if (is_async) { - // This command only applies to asynchronous GPU mode - gpu_thread.OnCommandListEnd(); - } + impl->FlushAndInvalidateRegion(addr, size); } } // namespace Tegra diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index e6a02a71b..05e5c94f3 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -4,28 +4,12 @@ #pragma once -#include <array> -#include <atomic> -#include <condition_variable> -#include <list> #include <memory> -#include <mutex> + +#include "common/bit_field.h" #include "common/common_types.h" -#include "core/hle/service/nvdrv/nvdata.h" -#include "core/hle/service/nvflinger/buffer_queue.h" #include "video_core/cdma_pusher.h" -#include "video_core/dma_pusher.h" #include "video_core/framebuffer_config.h" -#include "video_core/gpu_thread.h" - -using CacheAddr = std::uintptr_t; -[[nodiscard]] inline CacheAddr ToCacheAddr(const void* host_ptr) { - return reinterpret_cast<CacheAddr>(host_ptr); -} - -[[nodiscard]] inline u8* FromCacheAddr(CacheAddr cache_addr) { - return reinterpret_cast<u8*>(cache_addr); -} namespace Core { namespace Frontend { @@ -40,6 +24,9 @@ class ShaderNotify; } // namespace VideoCore namespace Tegra { +class DmaPusher; +class CDmaPusher; +struct CommandList; enum class RenderTargetFormat : u32 { NONE = 0x0, @@ -138,7 +125,18 @@ public: } }; - explicit GPU(Core::System& system_, bool is_async_, bool use_nvdec_); + enum class FenceOperation : u32 { + Acquire = 0, + Increment = 1, + }; + + union FenceAction { + u32 raw; + BitField<0, 1, FenceOperation> op; + BitField<8, 24, u32> syncpoint_id; + }; + + explicit GPU(Core::System& system, bool is_async, bool use_nvdec); ~GPU(); /// Binds a renderer to the GPU. @@ -162,9 +160,7 @@ public: [[nodiscard]] u64 RequestFlush(VAddr addr, std::size_t size); /// Obtains current flush request fence id. - [[nodiscard]] u64 CurrentFlushRequestFence() const { - return current_flush_fence.load(std::memory_order_relaxed); - } + [[nodiscard]] u64 CurrentFlushRequestFence() const; /// Tick pending requests within the GPU. void TickWork(); @@ -200,24 +196,16 @@ public: [[nodiscard]] const Tegra::CDmaPusher& CDmaPusher() const; /// Returns a reference to the underlying renderer. - [[nodiscard]] VideoCore::RendererBase& Renderer() { - return *renderer; - } + [[nodiscard]] VideoCore::RendererBase& Renderer(); /// Returns a const reference to the underlying renderer. - [[nodiscard]] const VideoCore::RendererBase& Renderer() const { - return *renderer; - } + [[nodiscard]] const VideoCore::RendererBase& Renderer() const; /// Returns a reference to the shader notifier. - [[nodiscard]] VideoCore::ShaderNotify& ShaderNotify() { - return *shader_notify; - } + [[nodiscard]] VideoCore::ShaderNotify& ShaderNotify(); /// Returns a const reference to the shader notifier. - [[nodiscard]] const VideoCore::ShaderNotify& ShaderNotify() const { - return *shader_notify; - } + [[nodiscard]] const VideoCore::ShaderNotify& ShaderNotify() const; /// Allows the CPU/NvFlinger to wait on the GPU before presenting a frame. void WaitFence(u32 syncpoint_id, u32 value); @@ -232,80 +220,12 @@ public: [[nodiscard]] u64 GetTicks() const; - [[nodiscard]] std::unique_lock<std::mutex> LockSync() { - return std::unique_lock{sync_mutex}; - } - - [[nodiscard]] bool IsAsync() const { - return is_async; - } + [[nodiscard]] bool IsAsync() const; - [[nodiscard]] bool UseNvdec() const { - return use_nvdec; - } + [[nodiscard]] bool UseNvdec() const; void RendererFrameEndNotify(); - enum class FenceOperation : u32 { - Acquire = 0, - Increment = 1, - }; - - union FenceAction { - u32 raw; - BitField<0, 1, FenceOperation> op; - BitField<8, 24, u32> syncpoint_id; - - [[nodiscard]] static CommandHeader Build(FenceOperation op, u32 syncpoint_id) { - FenceAction result{}; - result.op.Assign(op); - result.syncpoint_id.Assign(syncpoint_id); - return {result.raw}; - } - }; - - struct Regs { - static constexpr size_t NUM_REGS = 0x40; - - union { - struct { - INSERT_PADDING_WORDS_NOINIT(0x4); - struct { - u32 address_high; - u32 address_low; - - [[nodiscard]] GPUVAddr SemaphoreAddress() const { - return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | - address_low); - } - } semaphore_address; - - u32 semaphore_sequence; - u32 semaphore_trigger; - INSERT_PADDING_WORDS_NOINIT(0xC); - - // The pusher and the puller share the reference counter, the pusher only has read - // access - u32 reference_count; - INSERT_PADDING_WORDS_NOINIT(0x5); - - u32 semaphore_acquire; - u32 semaphore_release; - u32 fence_value; - FenceAction fence_action; - INSERT_PADDING_WORDS_NOINIT(0xE2); - - // Puller state - u32 acquire_mode; - u32 acquire_source; - u32 acquire_active; - u32 acquire_timeout; - u32 acquire_value; - }; - std::array<u32, NUM_REGS> reg_array; - }; - } regs{}; - /// Performs any additional setup necessary in order to begin GPU emulation. /// This can be used to launch any necessary threads and register any necessary /// core timing events. @@ -338,104 +258,9 @@ public: /// Notify rasterizer that any caches of the specified region should be flushed and invalidated void FlushAndInvalidateRegion(VAddr addr, u64 size); -protected: - void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const; - private: - void ProcessBindMethod(const MethodCall& method_call); - void ProcessFenceActionMethod(); - void ProcessWaitForInterruptMethod(); - void ProcessSemaphoreTriggerMethod(); - void ProcessSemaphoreRelease(); - void ProcessSemaphoreAcquire(); - - /// Calls a GPU puller method. - void CallPullerMethod(const MethodCall& method_call); - - /// Calls a GPU engine method. - void CallEngineMethod(const MethodCall& method_call); - - /// Calls a GPU engine multivalue method. - void CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, - u32 methods_pending); - - /// Determines where the method should be executed. - [[nodiscard]] bool ExecuteMethodOnEngine(u32 method); - -protected: - Core::System& system; - std::unique_ptr<Tegra::MemoryManager> memory_manager; - std::unique_ptr<Tegra::DmaPusher> dma_pusher; - std::unique_ptr<Tegra::CDmaPusher> cdma_pusher; - std::unique_ptr<VideoCore::RendererBase> renderer; - VideoCore::RasterizerInterface* rasterizer = nullptr; - const bool use_nvdec; - -private: - /// Mapping of command subchannels to their bound engine ids - std::array<EngineID, 8> bound_engines = {}; - /// 3D engine - std::unique_ptr<Engines::Maxwell3D> maxwell_3d; - /// 2D engine - std::unique_ptr<Engines::Fermi2D> fermi_2d; - /// Compute engine - std::unique_ptr<Engines::KeplerCompute> kepler_compute; - /// DMA engine - std::unique_ptr<Engines::MaxwellDMA> maxwell_dma; - /// Inline memory engine - std::unique_ptr<Engines::KeplerMemory> kepler_memory; - /// Shader build notifier - std::unique_ptr<VideoCore::ShaderNotify> shader_notify; - /// When true, we are about to shut down emulation session, so terminate outstanding tasks - std::atomic_bool shutting_down{}; - - std::array<std::atomic<u32>, Service::Nvidia::MaxSyncPoints> syncpoints{}; - - std::array<std::list<u32>, Service::Nvidia::MaxSyncPoints> syncpt_interrupts; - - std::mutex sync_mutex; - std::mutex device_mutex; - - std::condition_variable sync_cv; - - struct FlushRequest { - explicit FlushRequest(u64 fence_, VAddr addr_, std::size_t size_) - : fence{fence_}, addr{addr_}, size{size_} {} - u64 fence; - VAddr addr; - std::size_t size; - }; - - std::list<FlushRequest> flush_requests; - std::atomic<u64> current_flush_fence{}; - u64 last_flush_fence{}; - std::mutex flush_request_mutex; - - const bool is_async; - - VideoCommon::GPUThread::ThreadManager gpu_thread; - std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context; + struct Impl; + std::unique_ptr<Impl> impl; }; -#define ASSERT_REG_POSITION(field_name, position) \ - static_assert(offsetof(GPU::Regs, field_name) == position * 4, \ - "Field " #field_name " has invalid position") - -ASSERT_REG_POSITION(semaphore_address, 0x4); -ASSERT_REG_POSITION(semaphore_sequence, 0x6); -ASSERT_REG_POSITION(semaphore_trigger, 0x7); -ASSERT_REG_POSITION(reference_count, 0x14); -ASSERT_REG_POSITION(semaphore_acquire, 0x1A); -ASSERT_REG_POSITION(semaphore_release, 0x1B); -ASSERT_REG_POSITION(fence_value, 0x1C); -ASSERT_REG_POSITION(fence_action, 0x1D); - -ASSERT_REG_POSITION(acquire_mode, 0x100); -ASSERT_REG_POSITION(acquire_source, 0x101); -ASSERT_REG_POSITION(acquire_active, 0x102); -ASSERT_REG_POSITION(acquire_timeout, 0x103); -ASSERT_REG_POSITION(acquire_value, 0x104); - -#undef ASSERT_REG_POSITION - } // namespace Tegra diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h index 91bada925..00984188e 100644 --- a/src/video_core/gpu_thread.h +++ b/src/video_core/gpu_thread.h @@ -130,9 +130,6 @@ public: /// Notify rasterizer that any caches of the specified region should be flushed and invalidated void FlushAndInvalidateRegion(VAddr addr, u64 size); - // Stops the GPU execution and waits for the GPU to finish working - void ShutDown(); - void OnCommandListEnd(); private: diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index c9cff7450..20d748c12 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -6,7 +6,6 @@ set(SHADER_FILES convert_float_to_depth.frag full_screen_triangle.vert opengl_copy_bc4.comp - opengl_copy_bgra.comp opengl_present.frag opengl_present.vert pitch_unswizzle.comp diff --git a/src/video_core/host_shaders/opengl_copy_bgra.comp b/src/video_core/host_shaders/opengl_copy_bgra.comp deleted file mode 100644 index 2571a4abf..000000000 --- a/src/video_core/host_shaders/opengl_copy_bgra.comp +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#version 430 core - -layout (local_size_x = 4, local_size_y = 4) in; - -layout(binding = 0, rgba8) readonly uniform image2DArray bgr_input; -layout(binding = 1, rgba8) writeonly uniform image2DArray bgr_output; - -void main() { - vec4 color = imageLoad(bgr_input, ivec3(gl_GlobalInvocationID)); - imageStore(bgr_output, ivec3(gl_GlobalInvocationID), color.bgra); -} diff --git a/src/video_core/query_cache.h b/src/video_core/query_cache.h index aac851253..73231061a 100644 --- a/src/video_core/query_cache.h +++ b/src/video_core/query_cache.h @@ -8,6 +8,7 @@ #include <array> #include <cstring> #include <iterator> +#include <list> #include <memory> #include <mutex> #include <optional> diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index 07a995f7d..187a28e4d 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp @@ -147,8 +147,7 @@ void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer, void BufferCacheRuntime::ClearBuffer(Buffer& dest_buffer, u32 offset, size_t size, u32 value) { glClearNamedBufferSubData(dest_buffer.Handle(), GL_R32UI, static_cast<GLintptr>(offset), - static_cast<GLsizeiptr>(size / sizeof(u32)), GL_RED, GL_UNSIGNED_INT, - &value); + static_cast<GLsizeiptr>(size), GL_RED, GL_UNSIGNED_INT, &value); } void BufferCacheRuntime::BindIndexBuffer(Buffer& buffer, u32 offset, u32 size) { diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index b0aee6cc1..8c3ca3d82 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp @@ -20,6 +20,7 @@ #include "video_core/surface.h" #include "video_core/texture_cache/formatter.h" #include "video_core/texture_cache/samples_helper.h" +#include "video_core/texture_cache/util.h" namespace OpenGL { namespace { @@ -461,7 +462,7 @@ bool TextureCacheRuntime::CanImageBeCopied(const Image& dst, const Image& src) { if (dst.info.type == ImageType::e3D && dst.info.format == PixelFormat::BC4_UNORM) { return false; } - if (IsPixelFormatBGR(dst.info.format) || IsPixelFormatBGR(src.info.format)) { + if (IsPixelFormatBGR(dst.info.format) != IsPixelFormatBGR(src.info.format)) { return false; } return true; @@ -473,7 +474,7 @@ void TextureCacheRuntime::EmulateCopyImage(Image& dst, Image& src, ASSERT(src.info.type == ImageType::e3D); util_shaders.CopyBC4(dst, src, copies); } else if (IsPixelFormatBGR(dst.info.format) || IsPixelFormatBGR(src.info.format)) { - util_shaders.CopyBGR(dst, src, copies); + bgr_copy_pass.CopyBGR(dst, src, copies); } else { UNREACHABLE(); } @@ -1112,4 +1113,37 @@ Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM framebuffer.handle = handle; } +void BGRCopyPass::CopyBGR(Image& dst_image, Image& src_image, + std::span<const VideoCommon::ImageCopy> copies) { + static constexpr VideoCommon::Offset3D zero_offset{0, 0, 0}; + const u32 requested_pbo_size = + std::max(src_image.unswizzled_size_bytes, dst_image.unswizzled_size_bytes); + + if (bgr_pbo_size < requested_pbo_size) { + bgr_pbo.Create(); + bgr_pbo_size = requested_pbo_size; + glNamedBufferData(bgr_pbo.handle, bgr_pbo_size, nullptr, GL_STREAM_COPY); + } + for (const ImageCopy& copy : copies) { + ASSERT(copy.src_offset == zero_offset); + ASSERT(copy.dst_offset == zero_offset); + + // Copy from source to PBO + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glPixelStorei(GL_PACK_ROW_LENGTH, copy.extent.width); + glBindBuffer(GL_PIXEL_PACK_BUFFER, bgr_pbo.handle); + glGetTextureSubImage(src_image.Handle(), 0, 0, 0, 0, copy.extent.width, copy.extent.height, + copy.src_subresource.num_layers, src_image.GlFormat(), + src_image.GlType(), static_cast<GLsizei>(bgr_pbo_size), nullptr); + + // Copy from PBO to destination in desired GL format + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ROW_LENGTH, copy.extent.width); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, bgr_pbo.handle); + glTextureSubImage3D(dst_image.Handle(), 0, 0, 0, 0, copy.extent.width, copy.extent.height, + copy.dst_subresource.num_layers, dst_image.GlFormat(), + dst_image.GlType(), nullptr); + } +} + } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h index 4a4f6301c..1ca2c90be 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.h +++ b/src/video_core/renderer_opengl/gl_texture_cache.h @@ -12,6 +12,7 @@ #include "shader_recompiler/shader_info.h" #include "video_core/renderer_opengl/gl_resource_manager.h" #include "video_core/renderer_opengl/util_shaders.h" +#include "video_core/texture_cache/image_view_base.h" #include "video_core/texture_cache/texture_cache_base.h" namespace OpenGL { @@ -47,6 +48,19 @@ struct FormatProperties { bool is_compressed; }; +class BGRCopyPass { +public: + BGRCopyPass() = default; + ~BGRCopyPass() = default; + + void CopyBGR(Image& dst_image, Image& src_image, + std::span<const VideoCommon::ImageCopy> copies); + +private: + OGLBuffer bgr_pbo; + size_t bgr_pbo_size{}; +}; + class TextureCacheRuntime { friend Framebuffer; friend Image; @@ -118,6 +132,7 @@ private: const Device& device; StateTracker& state_tracker; UtilShaders util_shaders; + BGRCopyPass bgr_copy_pass; std::array<std::unordered_map<GLenum, FormatProperties>, 3> format_properties; bool has_broken_texture_view_formats = false; @@ -162,6 +177,14 @@ public: return texture.handle; } + GLuint GlFormat() const noexcept { + return gl_format; + } + + GLuint GlType() const noexcept { + return gl_type; + } + private: void CopyBufferToImage(const VideoCommon::BufferImageCopy& copy, size_t buffer_offset); diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index 672f94bfc..39158aa3e 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h @@ -52,7 +52,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> FORMAT_TAB {GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT}, // BC6H_UFLOAT {GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT}, // BC6H_SFLOAT {GL_COMPRESSED_RGBA_ASTC_4x4_KHR}, // ASTC_2D_4X4_UNORM - {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, // B8G8R8A8_UNORM + {GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV}, // B8G8R8A8_UNORM {GL_RGBA32F, GL_RGBA, GL_FLOAT}, // R32G32B32A32_FLOAT {GL_RGBA32I, GL_RGBA_INTEGER, GL_INT}, // R32G32B32A32_SINT {GL_RG32F, GL_RG, GL_FLOAT}, // R32G32_FLOAT @@ -81,7 +81,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> FORMAT_TAB {GL_COMPRESSED_RGBA_ASTC_8x8_KHR}, // ASTC_2D_8X8_UNORM {GL_COMPRESSED_RGBA_ASTC_8x5_KHR}, // ASTC_2D_8X5_UNORM {GL_COMPRESSED_RGBA_ASTC_5x4_KHR}, // ASTC_2D_5X4_UNORM - {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE}, // B8G8R8A8_SRGB + {GL_SRGB8_ALPHA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV}, // B8G8R8A8_SRGB {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT}, // BC1_RGBA_SRGB {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT}, // BC2_SRGB {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}, // BC3_SRGB diff --git a/src/video_core/renderer_opengl/util_shaders.cpp b/src/video_core/renderer_opengl/util_shaders.cpp index 333f35a1c..897c380b3 100644 --- a/src/video_core/renderer_opengl/util_shaders.cpp +++ b/src/video_core/renderer_opengl/util_shaders.cpp @@ -14,7 +14,6 @@ #include "video_core/host_shaders/block_linear_unswizzle_2d_comp.h" #include "video_core/host_shaders/block_linear_unswizzle_3d_comp.h" #include "video_core/host_shaders/opengl_copy_bc4_comp.h" -#include "video_core/host_shaders/opengl_copy_bgra_comp.h" #include "video_core/host_shaders/pitch_unswizzle_comp.h" #include "video_core/renderer_opengl/gl_shader_manager.h" #include "video_core/renderer_opengl/gl_shader_util.h" @@ -44,11 +43,6 @@ namespace { OGLProgram MakeProgram(std::string_view source) { return CreateProgram(source, GL_COMPUTE_SHADER); } - -size_t NumPixelsInCopy(const VideoCommon::ImageCopy& copy) { - return static_cast<size_t>(copy.extent.width * copy.extent.height * - copy.src_subresource.num_layers); -} } // Anonymous namespace UtilShaders::UtilShaders(ProgramManager& program_manager_) @@ -56,7 +50,6 @@ UtilShaders::UtilShaders(ProgramManager& program_manager_) block_linear_unswizzle_2d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_2D_COMP)), block_linear_unswizzle_3d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_3D_COMP)), pitch_unswizzle_program(MakeProgram(PITCH_UNSWIZZLE_COMP)), - copy_bgra_program(MakeProgram(OPENGL_COPY_BGRA_COMP)), copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)) { const auto swizzle_table = Tegra::Texture::MakeSwizzleTable(); swizzle_table_buffer.Create(); @@ -255,43 +248,6 @@ void UtilShaders::CopyBC4(Image& dst_image, Image& src_image, std::span<const Im program_manager.RestoreGuestCompute(); } -void UtilShaders::CopyBGR(Image& dst_image, Image& src_image, - std::span<const VideoCommon::ImageCopy> copies) { - static constexpr GLuint BINDING_INPUT_IMAGE = 0; - static constexpr GLuint BINDING_OUTPUT_IMAGE = 1; - static constexpr VideoCommon::Offset3D zero_offset{0, 0, 0}; - const u32 bytes_per_block = BytesPerBlock(dst_image.info.format); - switch (bytes_per_block) { - case 2: - // BGR565 copy - for (const ImageCopy& copy : copies) { - ASSERT(copy.src_offset == zero_offset); - ASSERT(copy.dst_offset == zero_offset); - bgr_copy_pass.Execute(dst_image, src_image, copy); - } - break; - case 4: { - // BGRA8 copy - program_manager.BindComputeProgram(copy_bgra_program.handle); - constexpr GLenum FORMAT = GL_RGBA8; - for (const ImageCopy& copy : copies) { - ASSERT(copy.src_offset == zero_offset); - ASSERT(copy.dst_offset == zero_offset); - glBindImageTexture(BINDING_INPUT_IMAGE, src_image.StorageHandle(), - copy.src_subresource.base_level, GL_FALSE, 0, GL_READ_ONLY, FORMAT); - glBindImageTexture(BINDING_OUTPUT_IMAGE, dst_image.StorageHandle(), - copy.dst_subresource.base_level, GL_FALSE, 0, GL_WRITE_ONLY, FORMAT); - glDispatchCompute(copy.extent.width, copy.extent.height, copy.extent.depth); - } - program_manager.RestoreGuestCompute(); - break; - } - default: - UNREACHABLE(); - break; - } -} - GLenum StoreFormat(u32 bytes_per_block) { switch (bytes_per_block) { case 1: @@ -309,36 +265,4 @@ GLenum StoreFormat(u32 bytes_per_block) { return GL_R8UI; } -void Bgr565CopyPass::Execute(const Image& dst_image, const Image& src_image, - const ImageCopy& copy) { - if (CopyBufferCreationNeeded(copy)) { - CreateNewCopyBuffer(copy, GL_TEXTURE_2D_ARRAY, GL_RGB565); - } - // Copy from source to PBO - glPixelStorei(GL_PACK_ALIGNMENT, 1); - glPixelStorei(GL_PACK_ROW_LENGTH, copy.extent.width); - glBindBuffer(GL_PIXEL_PACK_BUFFER, bgr16_pbo.handle); - glGetTextureSubImage(src_image.Handle(), 0, 0, 0, 0, copy.extent.width, copy.extent.height, - copy.src_subresource.num_layers, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, - static_cast<GLsizei>(bgr16_pbo_size), nullptr); - - // Copy from PBO to destination in reverse order - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glPixelStorei(GL_UNPACK_ROW_LENGTH, copy.extent.width); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, bgr16_pbo.handle); - glTextureSubImage3D(dst_image.Handle(), 0, 0, 0, 0, copy.extent.width, copy.extent.height, - copy.dst_subresource.num_layers, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV, - nullptr); -} - -bool Bgr565CopyPass::CopyBufferCreationNeeded(const ImageCopy& copy) { - return bgr16_pbo_size < NumPixelsInCopy(copy) * sizeof(u16); -} - -void Bgr565CopyPass::CreateNewCopyBuffer(const ImageCopy& copy, GLenum target, GLuint format) { - bgr16_pbo.Create(); - bgr16_pbo_size = NumPixelsInCopy(copy) * sizeof(u16); - glNamedBufferData(bgr16_pbo.handle, bgr16_pbo_size, nullptr, GL_STREAM_COPY); -} - } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/util_shaders.h b/src/video_core/renderer_opengl/util_shaders.h index ef881e35f..5de95ea7a 100644 --- a/src/video_core/renderer_opengl/util_shaders.h +++ b/src/video_core/renderer_opengl/util_shaders.h @@ -19,22 +19,6 @@ class ProgramManager; struct ImageBufferMap; -class Bgr565CopyPass { -public: - Bgr565CopyPass() = default; - ~Bgr565CopyPass() = default; - - void Execute(const Image& dst_image, const Image& src_image, - const VideoCommon::ImageCopy& copy); - -private: - [[nodiscard]] bool CopyBufferCreationNeeded(const VideoCommon::ImageCopy& copy); - void CreateNewCopyBuffer(const VideoCommon::ImageCopy& copy, GLenum target, GLuint format); - - OGLBuffer bgr16_pbo; - size_t bgr16_pbo_size{}; -}; - class UtilShaders { public: explicit UtilShaders(ProgramManager& program_manager); @@ -55,9 +39,6 @@ public: void CopyBC4(Image& dst_image, Image& src_image, std::span<const VideoCommon::ImageCopy> copies); - void CopyBGR(Image& dst_image, Image& src_image, - std::span<const VideoCommon::ImageCopy> copies); - private: ProgramManager& program_manager; @@ -67,10 +48,7 @@ private: OGLProgram block_linear_unswizzle_2d_program; OGLProgram block_linear_unswizzle_3d_program; OGLProgram pitch_unswizzle_program; - OGLProgram copy_bgra_program; OGLProgram copy_bc4_program; - - Bgr565CopyPass bgr_copy_pass; }; GLenum StoreFormat(u32 bytes_per_block); diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index adb6b7a3b..74822814d 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -97,19 +97,14 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_, Core::Frontend::EmuWindow& emu_window, Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_, std::unique_ptr<Core::Frontend::GraphicsContext> context_) try - : RendererBase(emu_window, std::move(context_)), - telemetry_session(telemetry_session_), - cpu_memory(cpu_memory_), - gpu(gpu_), - library(OpenLibrary()), + : RendererBase(emu_window, std::move(context_)), telemetry_session(telemetry_session_), + cpu_memory(cpu_memory_), gpu(gpu_), library(OpenLibrary()), instance(CreateInstance(library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type, true, Settings::values.renderer_debug.GetValue())), debug_callback(Settings::values.renderer_debug ? CreateDebugCallback(instance) : nullptr), surface(CreateSurface(instance, render_window)), - device(CreateDevice(instance, dld, *surface)), - memory_allocator(device, false), - state_tracker(gpu), - scheduler(device, state_tracker), + device(CreateDevice(instance, dld, *surface)), memory_allocator(device, false), + state_tracker(gpu), scheduler(device, state_tracker), swapchain(*surface, device, scheduler, render_window.GetFramebufferLayout().width, render_window.GetFramebufferLayout().height, false), blit_screen(cpu_memory, render_window, device, memory_allocator, swapchain, scheduler, diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 7c0f91007..8634c3316 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -507,8 +507,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { vertex_attributes.push_back({ .location = static_cast<u32>(index), .binding = 0, - .format = type == 1 ? VK_FORMAT_R32_SFLOAT - : type == 2 ? VK_FORMAT_R32_SINT : VK_FORMAT_R32_UINT, + .format = type == 1 ? VK_FORMAT_R32_SFLOAT + : type == 2 ? VK_FORMAT_R32_SINT + : VK_FORMAT_R32_UINT, .offset = 0, }); } @@ -567,12 +568,21 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { if (!vertex_binding_divisors.empty()) { vertex_input_ci.pNext = &input_divisor_ci; } + const bool has_tess_stages = spv_modules[1] || spv_modules[2]; auto input_assembly_topology = MaxwellToVK::PrimitiveTopology(device, key.state.topology); if (input_assembly_topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST) { - if (!spv_modules[1] && !spv_modules[2]) { + if (!has_tess_stages) { LOG_WARNING(Render_Vulkan, "Patch topology used without tessellation, using points"); input_assembly_topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST; } + } else { + if (has_tess_stages) { + // The Vulkan spec requires patch list IA topology be used with tessellation + // shader stages. Forcing it fixes a crash on some drivers + LOG_WARNING(Render_Vulkan, + "Patch topology not used with tessellation, using patch list"); + input_assembly_topology = VK_PRIMITIVE_TOPOLOGY_PATCH_LIST; + } } const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci{ .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index bd22e4e83..85fc1712f 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -212,7 +212,6 @@ private: vk::CommandBuffer current_cmdbuf; std::unique_ptr<CommandChunk> chunk; - std::jthread worker_thread; State state; @@ -226,6 +225,7 @@ private: std::mutex work_mutex; std::condition_variable_any work_cv; std::condition_variable wait_cv; + std::jthread worker_thread; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index ff979a7ac..06c5fb867 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -21,6 +21,7 @@ #include "video_core/renderer_vulkan/vk_texture_cache.h" #include "video_core/texture_cache/formatter.h" #include "video_core/texture_cache/samples_helper.h" +#include "video_core/texture_cache/util.h" #include "video_core/vulkan_common/vulkan_device.h" #include "video_core/vulkan_common/vulkan_memory_allocator.h" #include "video_core/vulkan_common/vulkan_wrapper.h" @@ -127,7 +128,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) { const auto format_info = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, false, format); VkImageCreateFlags flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; if (info.type == ImageType::e2D && info.resources.layers >= 6 && - info.size.width == info.size.height) { + info.size.width == info.size.height && !device.HasBrokenCubeImageCompability()) { flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; } if (info.type == ImageType::e3D) { diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 6d5a68bfe..b09c468e4 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -4,11 +4,11 @@ #pragma once -#include <compare> #include <span> #include "shader_recompiler/shader_info.h" #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" +#include "video_core/texture_cache/image_view_base.h" #include "video_core/texture_cache/texture_cache_base.h" #include "video_core/vulkan_common/vulkan_memory_allocator.h" #include "video_core/vulkan_common/vulkan_wrapper.h" diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp index 81a878bb2..05850afd0 100644 --- a/src/video_core/shader_environment.cpp +++ b/src/video_core/shader_environment.cpp @@ -16,6 +16,7 @@ #include "common/fs/fs.h" #include "common/logging/log.h" #include "shader_recompiler/environment.h" +#include "video_core/engines/kepler_compute.h" #include "video_core/memory_manager.h" #include "video_core/shader_environment.h" #include "video_core/textures/texture.h" diff --git a/src/video_core/shader_environment.h b/src/video_core/shader_environment.h index 2079979db..6640e53d0 100644 --- a/src/video_core/shader_environment.h +++ b/src/video_core/shader_environment.h @@ -5,13 +5,13 @@ #pragma once #include <array> -#include <atomic> #include <filesystem> #include <iosfwd> #include <limits> #include <memory> #include <optional> #include <span> +#include <stop_token> #include <type_traits> #include <unordered_map> #include <vector> @@ -19,9 +19,7 @@ #include "common/common_types.h" #include "common/unique_function.h" #include "shader_recompiler/environment.h" -#include "video_core/engines/kepler_compute.h" #include "video_core/engines/maxwell_3d.h" -#include "video_core/textures/texture.h" namespace Tegra { class Memorymanager; diff --git a/src/video_core/texture_cache/image_view_info.cpp b/src/video_core/texture_cache/image_view_info.cpp index 6527e14c8..e751f26c7 100644 --- a/src/video_core/texture_cache/image_view_info.cpp +++ b/src/video_core/texture_cache/image_view_info.cpp @@ -8,6 +8,7 @@ #include "video_core/texture_cache/image_view_info.h" #include "video_core/texture_cache/texture_cache_base.h" #include "video_core/texture_cache/types.h" +#include "video_core/texture_cache/util.h" #include "video_core/textures/texture.h" namespace VideoCommon { diff --git a/src/video_core/texture_cache/slot_vector.h b/src/video_core/texture_cache/slot_vector.h index 74cd3c9d8..50df06409 100644 --- a/src/video_core/texture_cache/slot_vector.h +++ b/src/video_core/texture_cache/slot_vector.h @@ -31,8 +31,8 @@ struct SlotId { }; template <class T> -requires std::is_nothrow_move_assignable_v<T>&& - std::is_nothrow_move_constructible_v<T> class SlotVector { +requires std::is_nothrow_move_assignable_v<T> && std::is_nothrow_move_constructible_v<T> +class SlotVector { public: class Iterator { friend SlotVector<T>; diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 24b809242..329df2e49 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -4,10 +4,15 @@ #pragma once +#include <unordered_set> + #include "common/alignment.h" #include "video_core/dirty_flags.h" +#include "video_core/engines/kepler_compute.h" +#include "video_core/texture_cache/image_view_base.h" #include "video_core/texture_cache/samples_helper.h" #include "video_core/texture_cache/texture_cache_base.h" +#include "video_core/texture_cache/util.h" namespace VideoCommon { diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index d7528ed24..2d1893c1c 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -4,13 +4,12 @@ #pragma once -#include <array> #include <mutex> #include <span> #include <type_traits> #include <unordered_map> -#include <unordered_set> #include <vector> +#include <queue> #include "common/common_types.h" #include "common/literals.h" @@ -18,10 +17,6 @@ #include "video_core/compatible_formats.h" #include "video_core/delayed_destruction_ring.h" #include "video_core/engines/fermi_2d.h" -#include "video_core/engines/kepler_compute.h" -#include "video_core/engines/maxwell_3d.h" -#include "video_core/memory_manager.h" -#include "video_core/rasterizer_interface.h" #include "video_core/surface.h" #include "video_core/texture_cache/descriptor_table.h" #include "video_core/texture_cache/image_base.h" @@ -30,7 +25,6 @@ #include "video_core/texture_cache/render_targets.h" #include "video_core/texture_cache/slot_vector.h" #include "video_core/texture_cache/types.h" -#include "video_core/texture_cache/util.h" #include "video_core/textures/texture.h" namespace VideoCommon { diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index c2ec9f76a..6388ed2eb 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -588,22 +588,27 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR ext_extended_dynamic_state = false; } } - sets_per_pool = 64; - if (driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE) { + + const bool is_amd = + driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE; + if (is_amd) { // AMD drivers need a higher amount of Sets per Pool in certain circunstances like in XC2. sets_per_pool = 96; - } - - const bool is_amd = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || - driver_id == VK_DRIVER_ID_MESA_RADV || - driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE; - if (ext_sampler_filter_minmax && is_amd) { - // Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken. + // Disable VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT on AMD GCN4 and lower as it is broken. if (!is_float16_supported) { LOG_WARNING( Render_Vulkan, - "Blacklisting AMD GCN4 and lower for VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME"); + "AMD GCN4 and earlier do not properly support VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT"); + has_broken_cube_compatibility = true; + } + } + const bool is_amd_or_radv = is_amd || driver_id == VK_DRIVER_ID_MESA_RADV; + if (ext_sampler_filter_minmax && is_amd_or_radv) { + // Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken. + if (!is_float16_supported) { + LOG_WARNING(Render_Vulkan, + "Blacklisting AMD GCN4 and earlier for VK_EXT_sampler_filter_minmax"); ext_sampler_filter_minmax = false; } } diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index bc180a32a..d9e74f1aa 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -309,6 +309,11 @@ public: return has_renderdoc || has_nsight_graphics; } + /// Returns true when the device does not properly support cube compatibility. + bool HasBrokenCubeImageCompability() const { + return has_broken_cube_compatibility; + } + /// Returns the vendor name reported from Vulkan. std::string_view GetVendorName() const { return vendor_name; @@ -417,6 +422,7 @@ private: bool ext_conservative_rasterization{}; ///< Support for VK_EXT_conservative_rasterization. bool ext_provoking_vertex{}; ///< Support for VK_EXT_provoking_vertex. bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config. + bool has_broken_cube_compatibility{}; ///< Has broken cube compatiblity bit bool has_renderdoc{}; ///< Has RenderDoc attached bool has_nsight_graphics{}; ///< Has Nsight Graphics attached diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index b6dda283d..402be6a78 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -290,10 +290,6 @@ if (YUZU_USE_QT_WEB_ENGINE) target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_WEB_ENGINE) endif () -if (YUZU_ENABLE_BOXCAT) - target_compile_definitions(yuzu PRIVATE -DYUZU_ENABLE_BOXCAT) -endif () - if(UNIX AND NOT APPLE) install(TARGETS yuzu RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") endif() diff --git a/src/yuzu/applets/qt_web_browser.cpp b/src/yuzu/applets/qt_web_browser.cpp index 652d99570..7d433ca50 100644 --- a/src/yuzu/applets/qt_web_browser.cpp +++ b/src/yuzu/applets/qt_web_browser.cpp @@ -54,6 +54,9 @@ QtNXWebEngineView::QtNXWebEngineView(QWidget* parent, Core::System& system, input_interpreter(std::make_unique<InputInterpreter>(system)), default_profile{QWebEngineProfile::defaultProfile()}, global_settings{QWebEngineSettings::globalSettings()} { + default_profile->setPersistentStoragePath(QString::fromStdString(Common::FS::PathToUTF8String( + Common::FS::GetYuzuPath(Common::FS::YuzuPath::YuzuDir) / "qtwebengine"))); + QWebEngineScript gamepad; QWebEngineScript window_nx; diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 1519a46ed..8b9e186b0 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -302,12 +302,17 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_, connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram, Qt::QueuedConnection); + connect(this, &GRenderWindow::ExitSignal, parent, &GMainWindow::OnExit, Qt::QueuedConnection); } void GRenderWindow::ExecuteProgram(std::size_t program_index) { emit ExecuteProgramSignal(program_index); } +void GRenderWindow::Exit() { + emit ExitSignal(); +} + GRenderWindow::~GRenderWindow() { input_subsystem->Shutdown(); } diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 402dd2ee1..54c4e2142 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -181,6 +181,9 @@ public: */ void ExecuteProgram(std::size_t program_index); + /// Instructs the window to exit the application. + void Exit(); + public slots: void OnEmulationStarting(EmuThread* emu_thread); void OnEmulationStopping(); @@ -191,6 +194,7 @@ signals: void Closed(); void FirstFrameDisplayed(); void ExecuteProgramSignal(std::size_t program_index); + void ExitSignal(); void MouseActivity(); private: diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 27b67fd9e..eb941ce02 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -562,7 +562,11 @@ void Config::ReadControlValues() { ReadTouchscreenValues(); ReadMotionTouchValues(); +#ifdef _WIN32 ReadBasicSetting(Settings::values.enable_raw_input); +#else + Settings::values.enable_raw_input = false; +#endif ReadBasicSetting(Settings::values.emulate_analog_keyboard); Settings::values.mouse_panning = false; ReadBasicSetting(Settings::values.mouse_panning_sensitivity); @@ -705,8 +709,6 @@ void Config::ReadDebuggingValues() { void Config::ReadServiceValues() { qt_config->beginGroup(QStringLiteral("Services")); - ReadBasicSetting(Settings::values.bcat_backend); - ReadBasicSetting(Settings::values.bcat_boxcat_local); ReadBasicSetting(Settings::values.network_interface); qt_config->endGroup(); } @@ -1265,8 +1267,6 @@ void Config::SaveDebuggingValues() { void Config::SaveNetworkValues() { qt_config->beginGroup(QStringLiteral("Services")); - WriteBasicSetting(Settings::values.bcat_backend); - WriteBasicSetting(Settings::values.bcat_boxcat_local); WriteBasicSetting(Settings::values.network_interface); qt_config->endGroup(); diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp index d20fd86b6..b30f09013 100644 --- a/src/yuzu/configuration/configure_input_advanced.cpp +++ b/src/yuzu/configuration/configure_input_advanced.cpp @@ -88,6 +88,10 @@ ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent) connect(ui->buttonMotionTouch, &QPushButton::clicked, this, &ConfigureInputAdvanced::CallMotionTouchConfigDialog); +#ifndef _WIN32 + ui->enable_raw_input->setVisible(false); +#endif + LoadConfiguration(); } diff --git a/src/yuzu/configuration/configure_network.cpp b/src/yuzu/configuration/configure_network.cpp index ae22f1018..cc15d36c2 100644 --- a/src/yuzu/configuration/configure_network.cpp +++ b/src/yuzu/configuration/configure_network.cpp @@ -6,64 +6,25 @@ #include <QtConcurrent/QtConcurrent> #include "common/settings.h" #include "core/core.h" -#include "core/hle/service/bcat/backend/boxcat.h" #include "core/network/network_interface.h" #include "ui_configure_network.h" #include "yuzu/configuration/configure_network.h" -#ifdef YUZU_ENABLE_BOXCAT -namespace { -QString FormatEventStatusString(const Service::BCAT::EventStatus& status) { - QString out; - - if (status.header.has_value()) { - out += QStringLiteral("<i>%1</i><br>").arg(QString::fromStdString(*status.header)); - } - - if (status.events.size() == 1) { - out += QStringLiteral("%1<br>").arg(QString::fromStdString(status.events.front())); - } else { - for (const auto& event : status.events) { - out += QStringLiteral("- %1<br>").arg(QString::fromStdString(event)); - } - } - - if (status.footer.has_value()) { - out += QStringLiteral("<i>%1</i><br>").arg(QString::fromStdString(*status.footer)); - } - - return out; -} -} // Anonymous namespace -#endif - ConfigureNetwork::ConfigureNetwork(QWidget* parent) : QWidget(parent), ui(std::make_unique<Ui::ConfigureNetwork>()) { ui->setupUi(this); - ui->bcat_source->addItem(QStringLiteral("None")); - ui->bcat_empty_label->setHidden(true); - ui->bcat_empty_header->setHidden(true); - -#ifdef YUZU_ENABLE_BOXCAT - ui->bcat_source->addItem(QStringLiteral("Boxcat"), QStringLiteral("boxcat")); -#endif - ui->network_interface->addItem(tr("None")); for (const auto& iface : Network::GetAvailableNetworkInterfaces()) { ui->network_interface->addItem(QString::fromStdString(iface.name)); } - connect(ui->bcat_source, QOverload<int>::of(&QComboBox::currentIndexChanged), this, - &ConfigureNetwork::OnBCATImplChanged); - this->SetConfiguration(); } ConfigureNetwork::~ConfigureNetwork() = default; void ConfigureNetwork::ApplyConfiguration() { - Settings::values.bcat_backend = ui->bcat_source->currentText().toLower().toStdString(); Settings::values.network_interface = ui->network_interface->currentText().toStdString(); } @@ -74,86 +35,8 @@ void ConfigureNetwork::RetranslateUi() { void ConfigureNetwork::SetConfiguration() { const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); - const int index = - ui->bcat_source->findData(QString::fromStdString(Settings::values.bcat_backend.GetValue())); - ui->bcat_source->setCurrentIndex(index == -1 ? 0 : index); - const std::string& network_interface = Settings::values.network_interface.GetValue(); ui->network_interface->setCurrentText(QString::fromStdString(network_interface)); ui->network_interface->setEnabled(runtime_lock); } - -std::pair<QString, QString> ConfigureNetwork::BCATDownloadEvents() { -#ifdef YUZU_ENABLE_BOXCAT - std::optional<std::string> global; - std::map<std::string, Service::BCAT::EventStatus> map; - const auto res = Service::BCAT::Boxcat::GetStatus(global, map); - - switch (res) { - case Service::BCAT::Boxcat::StatusResult::Success: - break; - case Service::BCAT::Boxcat::StatusResult::Offline: - return {QString{}, - tr("The boxcat service is offline or you are not connected to the internet.")}; - case Service::BCAT::Boxcat::StatusResult::ParseError: - return {QString{}, - tr("There was an error while processing the boxcat event data. Contact the yuzu " - "developers.")}; - case Service::BCAT::Boxcat::StatusResult::BadClientVersion: - return {QString{}, - tr("The version of yuzu you are using is either too new or too old for the server. " - "Try updating to the latest official release of yuzu.")}; - } - - if (map.empty()) { - return {QStringLiteral("Current Boxcat Events"), - tr("There are currently no events on boxcat.")}; - } - - QString out; - - if (global.has_value()) { - out += QStringLiteral("%1<br>").arg(QString::fromStdString(*global)); - } - - for (const auto& [key, value] : map) { - out += QStringLiteral("%1<b>%2</b><br>%3") - .arg(out.isEmpty() ? QString{} : QStringLiteral("<br>")) - .arg(QString::fromStdString(key)) - .arg(FormatEventStatusString(value)); - } - return {tr("Current Boxcat Events"), std::move(out)}; -#else - return {tr("Current Boxcat Events"), tr("There are currently no events on boxcat.")}; -#endif -} - -void ConfigureNetwork::OnBCATImplChanged() { -#ifdef YUZU_ENABLE_BOXCAT - const auto boxcat = ui->bcat_source->currentText() == QStringLiteral("Boxcat"); - ui->bcat_empty_header->setHidden(!boxcat); - ui->bcat_empty_label->setHidden(!boxcat); - ui->bcat_empty_header->setText(QString{}); - ui->bcat_empty_label->setText(tr("Yuzu is retrieving the latest boxcat status...")); - - if (!boxcat) - return; - - const auto future = QtConcurrent::run([this] { return BCATDownloadEvents(); }); - - watcher.setFuture(future); - connect(&watcher, &QFutureWatcher<std::pair<QString, QString>>::finished, this, - [this] { OnUpdateBCATEmptyLabel(watcher.result()); }); -#endif -} - -void ConfigureNetwork::OnUpdateBCATEmptyLabel(std::pair<QString, QString> string) { -#ifdef YUZU_ENABLE_BOXCAT - const auto boxcat = ui->bcat_source->currentText() == QStringLiteral("Boxcat"); - if (boxcat) { - ui->bcat_empty_header->setText(string.first); - ui->bcat_empty_label->setText(string.second); - } -#endif -} diff --git a/src/yuzu/configuration/configure_network.h b/src/yuzu/configuration/configure_network.h index 442b68e6b..028fd4acc 100644 --- a/src/yuzu/configuration/configure_network.h +++ b/src/yuzu/configuration/configure_network.h @@ -25,10 +25,5 @@ public: private: void SetConfiguration(); - std::pair<QString, QString> BCATDownloadEvents(); - void OnBCATImplChanged(); - void OnUpdateBCATEmptyLabel(std::pair<QString, QString> string); - std::unique_ptr<Ui::ConfigureNetwork> ui; - QFutureWatcher<std::pair<QString, QString>> watcher{this}; }; diff --git a/src/yuzu/configuration/configure_network.ui b/src/yuzu/configuration/configure_network.ui index 5f9b7e97b..9a79262f0 100644 --- a/src/yuzu/configuration/configure_network.ui +++ b/src/yuzu/configuration/configure_network.ui @@ -35,92 +35,6 @@ </layout> </widget> </item> - <item> - <widget class="QGroupBox" name="groupBox"> - <property name="title"> - <string>BCAT</string> - </property> - <layout class="QGridLayout" name="gridLayout"> - <item row="3" column="0"> - <widget class="QLabel" name="bcat_empty_header"> - <property name="text"> - <string/> - </property> - <property name="alignment"> - <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> - </property> - <property name="wordWrap"> - <bool>true</bool> - </property> - </widget> - </item> - <item row="0" column="0"> - <widget class="QLabel" name="label"> - <property name="maximumSize"> - <size> - <width>16777215</width> - <height>16777215</height> - </size> - </property> - <property name="text"> - <string>BCAT Backend</string> - </property> - </widget> - </item> - <item row="1" column="1" colspan="2"> - <widget class="QLabel" name="label_2"> - <property name="maximumSize"> - <size> - <width>260</width> - <height>16777215</height> - </size> - </property> - <property name="text"> - <string>BCAT is Nintendo's way of sending data to games to engage its community and unlock additional content.</string> - </property> - <property name="wordWrap"> - <bool>true</bool> - </property> - </widget> - </item> - <item row="2" column="1" colspan="2"> - <widget class="QLabel" name="label_3"> - <property name="text"> - <string><html><head/><body><p><a href="https://yuzu-emu.org/help/feature/boxcat"><span style=" text-decoration: underline; color:#0000ff;">Learn more about BCAT, Boxcat, and Current Events</span></a></p></body></html></string> - </property> - <property name="openExternalLinks"> - <bool>true</bool> - </property> - </widget> - </item> - <item row="0" column="1" colspan="2"> - <widget class="QComboBox" name="bcat_source"/> - </item> - <item row="3" column="1" colspan="2"> - <widget class="QLabel" name="bcat_empty_label"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="maximumSize"> - <size> - <width>260</width> - <height>16777215</height> - </size> - </property> - <property name="text"> - <string/> - </property> - <property name="alignment"> - <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> - </property> - <property name="wordWrap"> - <bool>true</bool> - </property> - </widget> - </item> - </layout> - </widget> - </item> </layout> </item> <item> diff --git a/src/yuzu/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp index ac849b01d..136614bf8 100644 --- a/src/yuzu/configuration/configure_profile_manager.cpp +++ b/src/yuzu/configuration/configure_profile_manager.cpp @@ -76,7 +76,7 @@ QString GetProfileUsernameFromUser(QWidget* parent, const QString& description_t } } // Anonymous namespace -ConfigureProfileManager ::ConfigureProfileManager(QWidget* parent) +ConfigureProfileManager::ConfigureProfileManager(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureProfileManager), profile_manager(std::make_unique<Service::Account::ProfileManager>()) { ui->setupUi(this); diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index 99a5df241..24a67ad46 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp @@ -12,7 +12,7 @@ #include "common/assert.h" #include "common/settings.h" #include "core/core.h" -#include "core/hle/service/time/time.h" +#include "core/hle/service/time/time_manager.h" #include "ui_configure_system.h" #include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_system.h" diff --git a/src/yuzu/configuration/configure_tas.cpp b/src/yuzu/configuration/configure_tas.cpp index b666b175a..8e5a4c72d 100644 --- a/src/yuzu/configuration/configure_tas.cpp +++ b/src/yuzu/configuration/configure_tas.cpp @@ -18,6 +18,7 @@ ConfigureTasDialog::ConfigureTasDialog(QWidget* parent) setFocusPolicy(Qt::ClickFocus); setWindowTitle(tr("TAS Configuration")); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); connect(ui->tas_path_button, &QToolButton::pressed, this, [this] { SetDirectory(DirectoryTarget::TAS, ui->tas_path_edit); }); diff --git a/src/yuzu/configuration/configure_tas.ui b/src/yuzu/configuration/configure_tas.ui index 95575ed9d..3972f9083 100644 --- a/src/yuzu/configuration/configure_tas.ui +++ b/src/yuzu/configuration/configure_tas.ui @@ -1,153 +1,194 @@ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> - <class>ConfigureTas</class> - <widget class="QDialog" name="ConfigureTas"> - <layout class="QVBoxLayout" name="verticalLayout_1"> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_1"> - <item> - <widget class="QGroupBox" name="groupBox_1"> - <property name="title"> - <string>TAS</string> - </property> - <layout class="QGridLayout" name="gridLayout_1"> - <item row="0" column="0" colspan="4"> - <widget class="QLabel" name="label_1"> - <property name="text"> - <string>Reads controller input from scripts in the same format as TAS-nx scripts.<br/>For a more detailed explanation please consult the FAQ on the yuzu website.</string> - </property> - </widget> - </item> - <item row="1" column="0" colspan="4"> - <widget class="QLabel" name="label_2"> - <property name="text"> - <string>To check which hotkeys control the playback/recording, please refer to the Hotkey settings (General -> Hotkeys).</string> - </property> - <property name="wordWrap"> - <bool>true</bool> - </property> - </widget> - </item> - <item row="2" column="0" colspan="4"> - <widget class="QLabel" name="label_3"> - <property name="text"> - <string>WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method.</string> - </property> - <property name="wordWrap"> - <bool>true</bool> - </property> - </widget> - </item> - </layout> - </widget> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_2"> - <item> - <widget class="QGroupBox" name="groupBox_2"> - <property name="title"> - <string>Settings</string> - </property> - <layout class="QGridLayout" name="gridLayout_2"> - <item row="0" column="0" colspan="4"> - <widget class="QCheckBox" name="tas_enable"> - <property name="text"> - <string>Enable TAS features</string> - </property> - </widget> - </item> - <item row="1" column="0" colspan="4"> - <widget class="QCheckBox" name="tas_control_swap"> - <property name="text"> - <string>Automatic controller profile swapping</string> - </property> - </widget> - </item> - <item row="2" column="0" colspan="4"> - <widget class="QCheckBox" name="tas_loop_script"> - <property name="text"> - <string>Loop script</string> - </property> - </widget> - </item> - <item row="3" column="0" colspan="4"> - <widget class="QCheckBox" name="tas_pause_on_load"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="text"> - <string>Pause execution during loads</string> - </property> - </widget> - </item> - </layout> - </widget> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_3"> - <item> - <widget class="QGroupBox" name="groupBox_3"> - <property name="title"> - <string>Script Directory</string> - </property> - <layout class="QGridLayout" name="gridLayout_3"> - <item row="0" column="0"> - <widget class="QLabel" name="label_4"> - <property name="text"> - <string>Path</string> - </property> - </widget> - </item> - <item row="0" column="3"> - <widget class="QToolButton" name="tas_path_button"> - <property name="text"> - <string>...</string> - </property> - </widget> - </item> - <item row="0" column="2"> - <widget class="QLineEdit" name="tas_path_edit"/> - </item> - </layout> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QDialogButtonBox" name="buttonBox"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> + <class>ConfigureTas</class> + <widget class="QDialog" name="ConfigureTas"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>337</width> + <height>316</height> + </rect> + </property> + <layout class="QVBoxLayout" name="verticalLayout_1"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_1"> + <item> + <widget class="QGroupBox" name="groupBox_1"> + <property name="title"> + <string>TAS</string> + </property> + <layout class="QGridLayout" name="gridLayout_1"> + <item row="0" column="0" colspan="4"> + <widget class="QLabel" name="label_1"> + <property name="text"> + <string>Reads controller input from scripts in the same format as TAS-nx scripts.<br/>For a more detailed explanation please consult the FAQ on the yuzu website.</string> </property> - <property name="orientation"> - <enum>Qt::Horizontal</enum> + </widget> + </item> + <item row="1" column="0" colspan="4"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>To check which hotkeys control the playback/recording, please refer to the Hotkey settings (General -> Hotkeys).</string> </property> - <property name="standardButtons"> - <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + <property name="wordWrap"> + <bool>true</bool> </property> - </widget> - </item> + </widget> + </item> + <item row="2" column="0" colspan="4"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>WARNING: This is an experimental feature.<br/>It will not play back scripts frame perfectly with the current, imperfect syncing method.</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QGroupBox" name="groupBox_2"> + <property name="title"> + <string>Settings</string> + </property> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="0" column="0" colspan="4"> + <widget class="QCheckBox" name="tas_enable"> + <property name="text"> + <string>Enable TAS features</string> + </property> + </widget> + </item> + <item row="1" column="0" colspan="4"> + <widget class="QCheckBox" name="tas_control_swap"> + <property name="text"> + <string>Automatic controller profile swapping</string> + </property> + </widget> + </item> + <item row="2" column="0" colspan="4"> + <widget class="QCheckBox" name="tas_loop_script"> + <property name="text"> + <string>Loop script</string> + </property> + </widget> + </item> + <item row="3" column="0" colspan="4"> + <widget class="QCheckBox" name="tas_pause_on_load"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Pause execution during loads</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QGroupBox" name="groupBox_3"> + <property name="title"> + <string>Script Directory</string> + </property> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="0" column="0"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Path</string> + </property> + </widget> + </item> + <item row="0" column="3"> + <widget class="QToolButton" name="tas_path_button"> + <property name="text"> + <string>...</string> + </property> + </widget> + </item> + <item row="0" column="2"> + <widget class="QLineEdit" name="tas_path_edit"/> + </item> + </layout> + </widget> + </item> </layout> - </widget> - <resources/> - <connections> - <connection> - <sender>buttonBox</sender> - <signal>accepted()</signal> - <receiver>ConfigureTas</receiver> - <slot>accept()</slot> - </connection> - <connection> - <sender>buttonBox</sender> - <signal>rejected()</signal> - <receiver>ConfigureTas</receiver> - <slot>reject()</slot> - </connection> - </connections> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>ConfigureTas</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel"> + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>ConfigureTas</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel"> + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + </connections> </ui> diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 3c2824362..552c2cc63 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1384,6 +1384,9 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t system.RegisterExecuteProgramCallback( [this](std::size_t program_index) { render_window->ExecuteProgram(program_index); }); + // Register an Exit callback such that Core can exit the currently running application. + system.RegisterExitCallback([this]() { render_window->Exit(); }); + connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); connect(render_window, &GRenderWindow::MouseActivity, this, &GMainWindow::OnMouseActivity); // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views @@ -2469,6 +2472,10 @@ void GMainWindow::OnExecuteProgram(std::size_t program_index) { BootGame(last_filename_booted, 0, program_index); } +void GMainWindow::OnExit() { + OnStopGame(); +} + void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) { OverlayDialog dialog(render_window, Core::System::GetInstance(), error_code, error_text, QString{}, tr("OK"), Qt::AlignLeft | Qt::AlignVCenter); @@ -2913,8 +2920,13 @@ void GMainWindow::UpdateWindowTitle(std::string_view title_name, std::string_vie if (title_name.empty()) { setWindowTitle(QString::fromStdString(window_title)); } else { - const auto run_title = - fmt::format("{} | {} | {} | {}", window_title, title_name, title_version, gpu_vendor); + const auto run_title = [window_title, title_name, title_version, gpu_vendor]() { + if (title_version.empty()) { + return fmt::format("{} | {} | {}", window_title, title_name, gpu_vendor); + } + return fmt::format("{} | {} | {} | {}", window_title, title_name, title_version, + gpu_vendor); + }(); setWindowTitle(QString::fromStdString(run_title)); } } diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 36eed6103..60ce01471 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -153,6 +153,7 @@ signals: public slots: void OnLoadComplete(); void OnExecuteProgram(std::size_t program_index); + void OnExit(); void ControllerSelectorReconfigureControllers( const Core::Frontend::ControllerParameters& parameters); void SoftwareKeyboardInitialize( diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index d74eb7e2b..434518d53 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -518,10 +518,6 @@ void Config::ReadValues() { ReadSetting("WebService", Settings::values.web_api_url); ReadSetting("WebService", Settings::values.yuzu_username); ReadSetting("WebService", Settings::values.yuzu_token); - - // Services - ReadSetting("Services", Settings::values.bcat_backend); - ReadSetting("Services", Settings::values.bcat_boxcat_local); } void Config::Reload() { diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index 72f3213fb..8119a50d8 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h @@ -428,11 +428,6 @@ web_api_url = https://api.yuzu-emu.org yuzu_username = yuzu_token = -[Services] -# The name of the backend to use for BCAT -# If this is set to 'boxcat' boxcat will be used, otherwise a null implementation will be used -bcat_backend = - [AddOns] # Used to disable add-ons # List of title IDs of games that will have add-ons disabled (separated by '|'): |