diff options
50 files changed, 1410 insertions, 1193 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index de2413843..0e064ab44 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -376,7 +376,7 @@ if (ENABLE_SDL2) if (YUZU_USE_BUNDLED_SDL2) # Detect toolchain and platform if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1930) AND ARCHITECTURE_x86_64) - set(SDL2_VER "SDL2-2.0.15-prerelease") + set(SDL2_VER "SDL2-2.0.16") else() message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable YUZU_USE_BUNDLED_SDL2 and provide your own.") endif() @@ -396,7 +396,7 @@ if (ENABLE_SDL2) elseif (YUZU_USE_EXTERNAL_SDL2) message(STATUS "Using SDL2 from externals.") else() - find_package(SDL2 2.0.15 REQUIRED) + find_package(SDL2 2.0.16 REQUIRED) # Some installations don't set SDL2_LIBRARIES if("${SDL2_LIBRARIES}" STREQUAL "") @@ -701,7 +701,7 @@ if (APPLE) elseif (WIN32) # WSAPoll and SHGetKnownFolderPath (AppData/Roaming) didn't exist before WinNT 6.x (Vista) add_definitions(-D_WIN32_WINNT=0x0600 -DWINVER=0x0600) - set(PLATFORM_LIBRARIES winmm ws2_32) + set(PLATFORM_LIBRARIES winmm ws2_32 iphlpapi) if (MINGW) # PSAPI is the Process Status API set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version) diff --git a/externals/SDL b/externals/SDL -Subproject 2f248a2a31c3323ecc37c00ad5e269e347ae392 +Subproject 25f9ed87ff6947d9576fc9d79dee0784e638ac5 diff --git a/src/common/settings.h b/src/common/settings.h index a88ee045d..c65746749 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -4,6 +4,7 @@ #pragma once +#include <algorithm> #include <array> #include <atomic> #include <chrono> @@ -74,14 +75,14 @@ public: */ explicit BasicSetting(const Type& default_val, const std::string& name) : default_value{default_val}, global{default_val}, label{name} {} - ~BasicSetting() = default; + virtual ~BasicSetting() = default; /** * Returns a reference to the setting's value. * * @returns A reference to the setting */ - [[nodiscard]] const Type& GetValue() const { + [[nodiscard]] virtual const Type& GetValue() const { return global; } @@ -90,7 +91,7 @@ public: * * @param value The desired value */ - void SetValue(const Type& value) { + virtual void SetValue(const Type& value) { Type temp{value}; std::swap(global, temp); } @@ -120,7 +121,7 @@ public: * * @returns A reference to the setting */ - const Type& operator=(const Type& value) { + virtual const Type& operator=(const Type& value) { Type temp{value}; std::swap(global, temp); return global; @@ -131,7 +132,7 @@ public: * * @returns A reference to the setting */ - explicit operator const Type&() const { + explicit virtual operator const Type&() const { return global; } @@ -142,6 +143,51 @@ protected: }; /** + * BasicRangedSetting class is intended for use with quantifiable settings that need a more + * restrictive range than implicitly defined by its type. Implements a minimum and maximum that is + * simply used to sanitize SetValue and the assignment overload. + */ +template <typename Type> +class BasicRangedSetting : virtual public BasicSetting<Type> { +public: + /** + * Sets a default value, minimum value, maximum value, and label. + * + * @param default_val Intial value of the setting, and default value of the setting + * @param min_val Sets the minimum allowed value of the setting + * @param max_val Sets the maximum allowed value of the setting + * @param name Label for the setting + */ + explicit BasicRangedSetting(const Type& default_val, const Type& min_val, const Type& max_val, + const std::string& name) + : BasicSetting<Type>{default_val, name}, minimum{min_val}, maximum{max_val} {} + virtual ~BasicRangedSetting() = default; + + /** + * Like BasicSetting's SetValue, except value is clamped to the range of the setting. + * + * @param value The desired value + */ + void SetValue(const Type& value) override { + this->global = std::clamp(value, minimum, maximum); + } + + /** + * Like BasicSetting's assignment overload, except value is clamped to the range of the setting. + * + * @param value The desired value + * @returns A reference to the setting's value + */ + const Type& operator=(const Type& value) override { + this->global = std::clamp(value, minimum, maximum); + return this->global; + } + + const Type minimum; ///< Minimum allowed value of the setting + const Type maximum; ///< Maximum allowed value of the setting +}; + +/** * The Setting class is a slightly more complex version of the BasicSetting class. This adds a * custom setting to switch to when a guest application specifically requires it. The effect is that * other components of the emulator can access the setting's intended value without any need for the @@ -152,7 +198,7 @@ protected: * Like the BasicSetting, this requires setting a default value and label to use. */ template <typename Type> -class Setting final : public BasicSetting<Type> { +class Setting : virtual public BasicSetting<Type> { public: /** * Sets a default value, label, and setting value. @@ -162,7 +208,7 @@ public: */ explicit Setting(const Type& default_val, const std::string& name) : BasicSetting<Type>(default_val, name) {} - ~Setting() = default; + virtual ~Setting() = default; /** * Tells this setting to represent either the global or custom setting when other member @@ -191,7 +237,13 @@ public: * * @returns The required value of the setting */ - [[nodiscard]] const Type& GetValue(bool need_global = false) const { + [[nodiscard]] virtual const Type& GetValue() const override { + if (use_global) { + return this->global; + } + return custom; + } + [[nodiscard]] virtual const Type& GetValue(bool need_global) const { if (use_global || need_global) { return this->global; } @@ -203,7 +255,7 @@ public: * * @param value The new value */ - void SetValue(const Type& value) { + void SetValue(const Type& value) override { Type temp{value}; if (use_global) { std::swap(this->global, temp); @@ -219,7 +271,7 @@ public: * * @returns A reference to the current setting value */ - const Type& operator=(const Type& value) { + const Type& operator=(const Type& value) override { Type temp{value}; if (use_global) { std::swap(this->global, temp); @@ -234,19 +286,88 @@ public: * * @returns A reference to the current setting value */ - explicit operator const Type&() const { + virtual explicit operator const Type&() const override { if (use_global) { return this->global; } return custom; } -private: +protected: bool use_global{true}; ///< The setting's global state Type custom{}; ///< The custom value of the setting }; /** + * RangedSetting is a Setting that implements a maximum and minimum value for its setting. Intended + * for use with quantifiable settings. + */ +template <typename Type> +class RangedSetting final : public BasicRangedSetting<Type>, public Setting<Type> { +public: + /** + * Sets a default value, minimum value, maximum value, and label. + * + * @param default_val Intial value of the setting, and default value of the setting + * @param min_val Sets the minimum allowed value of the setting + * @param max_val Sets the maximum allowed value of the setting + * @param name Label for the setting + */ + explicit RangedSetting(const Type& default_val, const Type& min_val, const Type& max_val, + const std::string& name) + : BasicSetting<Type>{default_val, name}, + BasicRangedSetting<Type>{default_val, min_val, max_val, name}, Setting<Type>{default_val, + name} {} + virtual ~RangedSetting() = default; + + // The following are needed to avoid a MSVC bug + // (source: https://stackoverflow.com/questions/469508) + [[nodiscard]] const Type& GetValue() const override { + return Setting<Type>::GetValue(); + } + [[nodiscard]] const Type& GetValue(bool need_global) const override { + return Setting<Type>::GetValue(need_global); + } + explicit operator const Type&() const override { + if (this->use_global) { + return this->global; + } + return this->custom; + } + + /** + * Like BasicSetting's SetValue, except value is clamped to the range of the setting. Sets the + * appropriate value depending on the global state. + * + * @param value The desired value + */ + void SetValue(const Type& value) override { + const Type temp = std::clamp(value, this->minimum, this->maximum); + if (this->use_global) { + this->global = temp; + } + this->custom = temp; + } + + /** + * Like BasicSetting's assignment overload, except value is clamped to the range of the setting. + * Uses the appropriate value depending on the global state. + * + * @param value The desired value + * @returns A reference to the setting's value + */ + const Type& operator=(const Type& value) override { + const Type temp = std::clamp(value, this->minimum, this->maximum); + if (this->use_global) { + this->global = temp; + return this->global; + } + this->custom = temp; + return this->custom; + } +}; + +/** * The InputSetting class allows for getting a reference to either the global or custom members. * This is required as we cannot easily modify the values of user-defined types within containers * using the SetValue() member function found in the Setting class. The primary purpose of this @@ -289,13 +410,14 @@ struct Values { BasicSetting<std::string> sink_id{"auto", "output_engine"}; BasicSetting<bool> audio_muted{false, "audio_muted"}; Setting<bool> enable_audio_stretching{true, "enable_audio_stretching"}; - Setting<u8> volume{100, "volume"}; + RangedSetting<u8> volume{100, 0, 100, "volume"}; // Core Setting<bool> use_multi_core{true, "use_multi_core"}; // Cpu - Setting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, "cpu_accuracy"}; + RangedSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto, + CPUAccuracy::Unsafe, "cpu_accuracy"}; // TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021 BasicSetting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"}; BasicSetting<bool> cpu_debug_mode{false, "cpu_debug_mode"}; @@ -317,7 +439,8 @@ struct Values { Setting<bool> cpuopt_unsafe_fastmem_check{true, "cpuopt_unsafe_fastmem_check"}; // Renderer - Setting<RendererBackend> renderer_backend{RendererBackend::OpenGL, "backend"}; + RangedSetting<RendererBackend> renderer_backend{ + RendererBackend::OpenGL, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"}; BasicSetting<bool> renderer_debug{false, "debug"}; BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"}; BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"}; @@ -328,26 +451,28 @@ struct Values { Setting<u16> resolution_factor{1, "resolution_factor"}; // *nix platforms may have issues with the borderless windowed fullscreen mode. // Default to exclusive fullscreen on these platforms for now. - Setting<FullscreenMode> fullscreen_mode{ + RangedSetting<FullscreenMode> fullscreen_mode{ #ifdef _WIN32 FullscreenMode::Borderless, #else FullscreenMode::Exclusive, #endif - "fullscreen_mode"}; - Setting<int> aspect_ratio{0, "aspect_ratio"}; - Setting<int> max_anisotropy{0, "max_anisotropy"}; + FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"}; + RangedSetting<int> aspect_ratio{0, 0, 3, "aspect_ratio"}; + RangedSetting<int> max_anisotropy{0, 0, 4, "max_anisotropy"}; Setting<bool> use_speed_limit{true, "use_speed_limit"}; - Setting<u16> speed_limit{100, "speed_limit"}; + RangedSetting<u16> speed_limit{100, 0, 9999, "speed_limit"}; Setting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"}; - Setting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, "gpu_accuracy"}; + RangedSetting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, GPUAccuracy::Normal, + GPUAccuracy::Extreme, "gpu_accuracy"}; Setting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"}; Setting<bool> use_nvdec_emulation{true, "use_nvdec_emulation"}; Setting<bool> accelerate_astc{true, "accelerate_astc"}; Setting<bool> use_vsync{true, "use_vsync"}; - BasicSetting<u16> fps_cap{1000, "fps_cap"}; + BasicRangedSetting<u16> fps_cap{1000, 1, 1000, "fps_cap"}; BasicSetting<bool> disable_fps_limit{false, "disable_fps_limit"}; - Setting<ShaderBackend> shader_backend{ShaderBackend::GLASM, "shader_backend"}; + RangedSetting<ShaderBackend> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL, + ShaderBackend::SPIRV, "shader_backend"}; Setting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; Setting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; Setting<bool> use_caches_gc{false, "use_caches_gc"}; @@ -364,10 +489,10 @@ struct Values { std::chrono::seconds custom_rtc_differential; BasicSetting<s32> current_user{0, "current_user"}; - Setting<s32> language_index{1, "language_index"}; - Setting<s32> region_index{1, "region_index"}; - Setting<s32> time_zone_index{0, "time_zone_index"}; - Setting<s32> sound_index{1, "sound_index"}; + RangedSetting<s32> language_index{1, 0, 16, "language_index"}; + RangedSetting<s32> region_index{1, 0, 6, "region_index"}; + RangedSetting<s32> time_zone_index{0, 0, 45, "time_zone_index"}; + RangedSetting<s32> sound_index{1, 0, 2, "sound_index"}; // Controls InputSetting<std::array<PlayerInput, 10>> players; @@ -384,7 +509,7 @@ struct Values { "udp_input_servers"}; BasicSetting<bool> mouse_panning{false, "mouse_panning"}; - BasicSetting<u8> mouse_panning_sensitivity{10, "mouse_panning_sensitivity"}; + BasicRangedSetting<u8> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"}; BasicSetting<bool> mouse_enabled{false, "mouse_enabled"}; std::string mouse_device; MouseButtonsRaw mouse_buttons; @@ -433,9 +558,10 @@ struct Values { BasicSetting<std::string> log_filter{"*:Info", "log_filter"}; BasicSetting<bool> use_dev_keys{false, "use_dev_keys"}; - // Services + // 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 BasicSetting<bool> enable_telemetry{true, "enable_telemetry"}; diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h index ad04df8ca..8430b9778 100644 --- a/src/common/threadsafe_queue.h +++ b/src/common/threadsafe_queue.h @@ -46,15 +46,13 @@ public: ElementPtr* new_ptr = new ElementPtr(); write_ptr->next.store(new_ptr, std::memory_order_release); write_ptr = new_ptr; + ++size; - const size_t previous_size{size++}; - - // Acquire the mutex and then immediately release it as a fence. + // cv_mutex must be held or else there will be a missed wakeup if the other thread is in the + // line before cv.wait // TODO(bunnei): This can be replaced with C++20 waitable atomics when properly supported. // See discussion on https://github.com/yuzu-emu/yuzu/pull/3173 for details. - if (previous_size == 0) { - std::lock_guard lock{cv_mutex}; - } + std::lock_guard lock{cv_mutex}; cv.notify_one(); } diff --git a/src/common/uuid.h b/src/common/uuid.h index aeb36939a..2353179d8 100644 --- a/src/common/uuid.h +++ b/src/common/uuid.h @@ -69,3 +69,14 @@ struct UUID { static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); } // namespace Common + +namespace std { + +template <> +struct hash<Common::UUID> { + size_t operator()(const Common::UUID& uuid) const noexcept { + return uuid.uuid[1] ^ uuid.uuid[0]; + } +}; + +} // namespace std diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 5c99c00f5..f5cf5c16a 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -636,6 +636,8 @@ add_library(core STATIC memory.h network/network.cpp network/network.h + network/network_interface.cpp + network/network_interface.h network/sockets.h perf_stats.cpp perf_stats.h diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index e742db48f..0a53c0c81 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -11,6 +11,7 @@ #include "core/hle/service/nifm/nifm.h" #include "core/hle/service/service.h" #include "core/network/network.h" +#include "core/network/network_interface.h" namespace Service::NIFM { @@ -179,10 +180,10 @@ private: IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - if (Settings::values.bcat_backend.GetValue() == "none") { - rb.PushEnum(RequestState::NotSubmitted); - } else { + if (Network::GetHostIPv4Address().has_value()) { rb.PushEnum(RequestState::Connected); + } else { + rb.PushEnum(RequestState::NotSubmitted); } } @@ -322,12 +323,15 @@ private: void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NIFM, "(STUBBED) called"); - const auto [ipv4, error] = Network::GetHostIPv4Address(); - UNIMPLEMENTED_IF(error != Network::Errno::SUCCESS); + auto ipv4 = Network::GetHostIPv4Address(); + if (!ipv4) { + LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0"); + ipv4.emplace(Network::IPv4Address{0, 0, 0, 0}); + } IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.PushRaw(ipv4); + rb.PushRaw(*ipv4); } void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NIFM, "called"); @@ -354,10 +358,10 @@ private: static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting), "IpConfigInfo has incorrect size."); - const IpConfigInfo ip_config_info{ + IpConfigInfo ip_config_info{ .ip_address_setting{ .is_automatic{true}, - .current_address{192, 168, 1, 100}, + .current_address{0, 0, 0, 0}, .subnet_mask{255, 255, 255, 0}, .gateway{192, 168, 1, 1}, }, @@ -368,6 +372,19 @@ private: }, }; + const auto iface = Network::GetSelectedNetworkInterface(); + if (iface) { + ip_config_info.ip_address_setting = + IpAddressSetting{.is_automatic{true}, + .current_address{Network::TranslateIPv4(iface->ip_address)}, + .subnet_mask{Network::TranslateIPv4(iface->subnet_mask)}, + .gateway{Network::TranslateIPv4(iface->gateway)}}; + + } else { + LOG_ERROR(Service_NIFM, + "Couldn't get host network configuration info, using default values"); + } + IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)}; rb.Push(ResultSuccess); rb.PushRaw<IpConfigInfo>(ip_config_info); @@ -384,10 +401,10 @@ private: IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - if (Settings::values.bcat_backend.GetValue() == "none") { - rb.Push<u8>(0); - } else { + if (Network::GetHostIPv4Address().has_value()) { rb.Push<u8>(1); + } else { + rb.Push<u8>(0); } } void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { @@ -395,10 +412,10 @@ private: IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - if (Settings::values.bcat_backend.GetValue() == "none") { - rb.Push<u8>(0); - } else { + if (Network::GetHostIPv4Address().has_value()) { rb.Push<u8>(1); + } else { + rb.Push<u8>(0); } } }; diff --git a/src/core/memory.cpp b/src/core/memory.cpp index f285c6f63..51c4dea26 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -4,8 +4,6 @@ #include <algorithm> #include <cstring> -#include <optional> -#include <utility> #include "common/assert.h" #include "common/atomic_ops.h" @@ -14,12 +12,10 @@ #include "common/page_table.h" #include "common/settings.h" #include "common/swap.h" -#include "core/arm/arm_interface.h" #include "core/core.h" #include "core/device_memory.h" #include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_process.h" -#include "core/hle/kernel/physical_memory.h" #include "core/memory.h" #include "video_core/gpu.h" @@ -62,17 +58,7 @@ struct Memory::Impl { } } - bool IsValidVirtualAddress(const Kernel::KProcess& process, const VAddr vaddr) const { - const auto& page_table = process.PageTable().PageTableImpl(); - const auto [pointer, type] = page_table.pointers[vaddr >> PAGE_BITS].PointerType(); - return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory; - } - - bool IsValidVirtualAddress(VAddr vaddr) const { - return IsValidVirtualAddress(*system.CurrentProcess(), vaddr); - } - - u8* GetPointerFromRasterizerCachedMemory(VAddr vaddr) const { + [[nodiscard]] u8* GetPointerFromRasterizerCachedMemory(VAddr vaddr) const { const PAddr paddr{current_page_table->backing_addr[vaddr >> PAGE_BITS]}; if (!paddr) { @@ -82,18 +68,6 @@ struct Memory::Impl { return system.DeviceMemory().GetPointer(paddr) + vaddr; } - u8* GetPointer(const VAddr vaddr) const { - const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw(); - if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { - return pointer + vaddr; - } - const auto type = Common::PageTable::PageInfo::ExtractType(raw_pointer); - if (type == Common::PageType::RasterizerCachedMemory) { - return GetPointerFromRasterizerCachedMemory(vaddr); - } - return nullptr; - } - u8 Read8(const VAddr addr) { return Read<u8>(addr); } @@ -179,7 +153,7 @@ struct Memory::Impl { std::string string; string.reserve(max_length); for (std::size_t i = 0; i < max_length; ++i) { - const char c = Read8(vaddr); + const char c = Read<s8>(vaddr); if (c == '\0') { break; } @@ -190,15 +164,14 @@ struct Memory::Impl { return string; } - void ReadBlock(const Kernel::KProcess& process, const VAddr src_addr, void* dest_buffer, - const std::size_t size) { + void WalkBlock(const Kernel::KProcess& process, const VAddr addr, const std::size_t size, + auto on_unmapped, auto on_memory, auto on_rasterizer, auto increment) { const auto& page_table = process.PageTable().PageTableImpl(); - std::size_t remaining_size = size; - std::size_t page_index = src_addr >> PAGE_BITS; - std::size_t page_offset = src_addr & PAGE_MASK; + std::size_t page_index = addr >> PAGE_BITS; + std::size_t page_offset = addr & PAGE_MASK; - while (remaining_size > 0) { + while (remaining_size) { const std::size_t copy_amount = std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); @@ -206,22 +179,18 @@ struct Memory::Impl { const auto [pointer, type] = page_table.pointers[page_index].PointerType(); switch (type) { case Common::PageType::Unmapped: { - LOG_ERROR(HW_Memory, - "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", - current_vaddr, src_addr, size); - std::memset(dest_buffer, 0, copy_amount); + on_unmapped(copy_amount, current_vaddr); break; } case Common::PageType::Memory: { DEBUG_ASSERT(pointer); - const u8* const src_ptr = pointer + page_offset + (page_index << PAGE_BITS); - std::memcpy(dest_buffer, src_ptr, copy_amount); + u8* mem_ptr = pointer + page_offset + (page_index << PAGE_BITS); + on_memory(copy_amount, mem_ptr); break; } case Common::PageType::RasterizerCachedMemory: { - const u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)}; - system.GPU().FlushRegion(current_vaddr, copy_amount); - std::memcpy(dest_buffer, host_ptr, copy_amount); + u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)}; + on_rasterizer(current_vaddr, copy_amount, host_ptr); break; } default: @@ -230,248 +199,122 @@ struct Memory::Impl { page_index++; page_offset = 0; - dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount; + increment(copy_amount); remaining_size -= copy_amount; } } - void ReadBlockUnsafe(const Kernel::KProcess& process, const VAddr src_addr, void* dest_buffer, - const std::size_t size) { - const auto& page_table = process.PageTable().PageTableImpl(); - - std::size_t remaining_size = size; - std::size_t page_index = src_addr >> PAGE_BITS; - std::size_t page_offset = src_addr & PAGE_MASK; - - while (remaining_size > 0) { - const std::size_t copy_amount = - std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); - const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); - - const auto [pointer, type] = page_table.pointers[page_index].PointerType(); - switch (type) { - case Common::PageType::Unmapped: { + template <bool UNSAFE> + void ReadBlockImpl(const Kernel::KProcess& process, const VAddr src_addr, void* dest_buffer, + const std::size_t size) { + WalkBlock( + process, src_addr, size, + [src_addr, size, &dest_buffer](const std::size_t copy_amount, + const VAddr current_vaddr) { LOG_ERROR(HW_Memory, "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", current_vaddr, src_addr, size); std::memset(dest_buffer, 0, copy_amount); - break; - } - case Common::PageType::Memory: { - DEBUG_ASSERT(pointer); - const u8* const src_ptr = pointer + page_offset + (page_index << PAGE_BITS); + }, + [&dest_buffer](const std::size_t copy_amount, const u8* const src_ptr) { std::memcpy(dest_buffer, src_ptr, copy_amount); - break; - } - case Common::PageType::RasterizerCachedMemory: { - const u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)}; + }, + [&system = system, &dest_buffer](const VAddr current_vaddr, + const std::size_t copy_amount, + const u8* const host_ptr) { + if constexpr (!UNSAFE) { + system.GPU().FlushRegion(current_vaddr, copy_amount); + } std::memcpy(dest_buffer, host_ptr, copy_amount); - break; - } - default: - UNREACHABLE(); - } - - page_index++; - page_offset = 0; - dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount; - remaining_size -= copy_amount; - } + }, + [&dest_buffer](const std::size_t copy_amount) { + dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount; + }); } void ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) { - ReadBlock(*system.CurrentProcess(), src_addr, dest_buffer, size); + ReadBlockImpl<false>(*system.CurrentProcess(), src_addr, dest_buffer, size); } void ReadBlockUnsafe(const VAddr src_addr, void* dest_buffer, const std::size_t size) { - ReadBlockUnsafe(*system.CurrentProcess(), src_addr, dest_buffer, size); + ReadBlockImpl<true>(*system.CurrentProcess(), src_addr, dest_buffer, size); } - void WriteBlock(const Kernel::KProcess& process, const VAddr dest_addr, const void* src_buffer, - const std::size_t size) { - const auto& page_table = process.PageTable().PageTableImpl(); - std::size_t remaining_size = size; - std::size_t page_index = dest_addr >> PAGE_BITS; - std::size_t page_offset = dest_addr & PAGE_MASK; - - while (remaining_size > 0) { - const std::size_t copy_amount = - std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); - const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); - - const auto [pointer, type] = page_table.pointers[page_index].PointerType(); - switch (type) { - case Common::PageType::Unmapped: { + template <bool UNSAFE> + void WriteBlockImpl(const Kernel::KProcess& process, const VAddr dest_addr, + const void* src_buffer, const std::size_t size) { + WalkBlock( + process, dest_addr, size, + [dest_addr, size](const std::size_t copy_amount, const VAddr current_vaddr) { LOG_ERROR(HW_Memory, "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", current_vaddr, dest_addr, size); - break; - } - case Common::PageType::Memory: { - DEBUG_ASSERT(pointer); - u8* const dest_ptr = pointer + page_offset + (page_index << PAGE_BITS); + }, + [&src_buffer](const std::size_t copy_amount, u8* const dest_ptr) { std::memcpy(dest_ptr, src_buffer, copy_amount); - break; - } - case Common::PageType::RasterizerCachedMemory: { - u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)}; - system.GPU().InvalidateRegion(current_vaddr, copy_amount); - std::memcpy(host_ptr, src_buffer, copy_amount); - break; - } - default: - UNREACHABLE(); - } - - page_index++; - page_offset = 0; - src_buffer = static_cast<const u8*>(src_buffer) + copy_amount; - remaining_size -= copy_amount; - } - } - - void WriteBlockUnsafe(const Kernel::KProcess& process, const VAddr dest_addr, - const void* src_buffer, const std::size_t size) { - const auto& page_table = process.PageTable().PageTableImpl(); - std::size_t remaining_size = size; - std::size_t page_index = dest_addr >> PAGE_BITS; - std::size_t page_offset = dest_addr & PAGE_MASK; - - while (remaining_size > 0) { - const std::size_t copy_amount = - std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); - const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); - - const auto [pointer, type] = page_table.pointers[page_index].PointerType(); - switch (type) { - case Common::PageType::Unmapped: { - LOG_ERROR(HW_Memory, - "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", - current_vaddr, dest_addr, size); - break; - } - case Common::PageType::Memory: { - DEBUG_ASSERT(pointer); - u8* const dest_ptr = pointer + page_offset + (page_index << PAGE_BITS); - std::memcpy(dest_ptr, src_buffer, copy_amount); - break; - } - case Common::PageType::RasterizerCachedMemory: { - u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)}; + }, + [&system = system, &src_buffer](const VAddr current_vaddr, + const std::size_t copy_amount, u8* const host_ptr) { + if constexpr (!UNSAFE) { + system.GPU().InvalidateRegion(current_vaddr, copy_amount); + } std::memcpy(host_ptr, src_buffer, copy_amount); - break; - } - default: - UNREACHABLE(); - } - - page_index++; - page_offset = 0; - src_buffer = static_cast<const u8*>(src_buffer) + copy_amount; - remaining_size -= copy_amount; - } + }, + [&src_buffer](const std::size_t copy_amount) { + src_buffer = static_cast<const u8*>(src_buffer) + copy_amount; + }); } void WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) { - WriteBlock(*system.CurrentProcess(), dest_addr, src_buffer, size); + WriteBlockImpl<false>(*system.CurrentProcess(), dest_addr, src_buffer, size); } void WriteBlockUnsafe(const VAddr dest_addr, const void* src_buffer, const std::size_t size) { - WriteBlockUnsafe(*system.CurrentProcess(), dest_addr, src_buffer, size); + WriteBlockImpl<true>(*system.CurrentProcess(), dest_addr, src_buffer, size); } void ZeroBlock(const Kernel::KProcess& process, const VAddr dest_addr, const std::size_t size) { - const auto& page_table = process.PageTable().PageTableImpl(); - std::size_t remaining_size = size; - std::size_t page_index = dest_addr >> PAGE_BITS; - std::size_t page_offset = dest_addr & PAGE_MASK; - - while (remaining_size > 0) { - const std::size_t copy_amount = - std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); - const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); - - const auto [pointer, type] = page_table.pointers[page_index].PointerType(); - switch (type) { - case Common::PageType::Unmapped: { + WalkBlock( + process, dest_addr, size, + [dest_addr, size](const std::size_t copy_amount, const VAddr current_vaddr) { LOG_ERROR(HW_Memory, "Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", current_vaddr, dest_addr, size); - break; - } - case Common::PageType::Memory: { - DEBUG_ASSERT(pointer); - u8* const dest_ptr = pointer + page_offset + (page_index << PAGE_BITS); + }, + [](const std::size_t copy_amount, u8* const dest_ptr) { std::memset(dest_ptr, 0, copy_amount); - break; - } - case Common::PageType::RasterizerCachedMemory: { - u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)}; + }, + [&system = system](const VAddr current_vaddr, const std::size_t copy_amount, + u8* const host_ptr) { system.GPU().InvalidateRegion(current_vaddr, copy_amount); std::memset(host_ptr, 0, copy_amount); - break; - } - default: - UNREACHABLE(); - } - - page_index++; - page_offset = 0; - remaining_size -= copy_amount; - } - } - - void ZeroBlock(const VAddr dest_addr, const std::size_t size) { - ZeroBlock(*system.CurrentProcess(), dest_addr, size); + }, + [](const std::size_t copy_amount) {}); } void CopyBlock(const Kernel::KProcess& process, VAddr dest_addr, VAddr src_addr, const std::size_t size) { - const auto& page_table = process.PageTable().PageTableImpl(); - std::size_t remaining_size = size; - std::size_t page_index = src_addr >> PAGE_BITS; - std::size_t page_offset = src_addr & PAGE_MASK; - - while (remaining_size > 0) { - const std::size_t copy_amount = - std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); - const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); - - const auto [pointer, type] = page_table.pointers[page_index].PointerType(); - switch (type) { - case Common::PageType::Unmapped: { + WalkBlock( + process, dest_addr, size, + [this, &process, &dest_addr, &src_addr, size](const std::size_t copy_amount, + const VAddr current_vaddr) { LOG_ERROR(HW_Memory, "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", current_vaddr, src_addr, size); ZeroBlock(process, dest_addr, copy_amount); - break; - } - case Common::PageType::Memory: { - DEBUG_ASSERT(pointer); - const u8* src_ptr = pointer + page_offset + (page_index << PAGE_BITS); - WriteBlock(process, dest_addr, src_ptr, copy_amount); - break; - } - case Common::PageType::RasterizerCachedMemory: { - const u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)}; + }, + [this, &process, &dest_addr](const std::size_t copy_amount, const u8* const src_ptr) { + WriteBlockImpl<false>(process, dest_addr, src_ptr, copy_amount); + }, + [this, &system = system, &process, &dest_addr]( + const VAddr current_vaddr, const std::size_t copy_amount, u8* const host_ptr) { system.GPU().FlushRegion(current_vaddr, copy_amount); - WriteBlock(process, dest_addr, host_ptr, copy_amount); - break; - } - default: - UNREACHABLE(); - } - - page_index++; - page_offset = 0; - dest_addr += static_cast<VAddr>(copy_amount); - src_addr += static_cast<VAddr>(copy_amount); - remaining_size -= copy_amount; - } - } - - void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size) { - return CopyBlock(*system.CurrentProcess(), dest_addr, src_addr, size); + WriteBlockImpl<false>(process, dest_addr, host_ptr, copy_amount); + }, + [&dest_addr, &src_addr](const std::size_t copy_amount) { + dest_addr += static_cast<VAddr>(copy_amount); + src_addr += static_cast<VAddr>(copy_amount); + }); } void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) { @@ -514,7 +357,7 @@ struct Memory::Impl { } else { // Switch page type to uncached if now uncached switch (page_type) { - case Common::PageType::Unmapped: + case Common::PageType::Unmapped: // NOLINT(bugprone-branch-clone) // It is not necessary for a process to have this region mapped into its address // space, for example, a system module need not have a VRAM mapping. break; @@ -597,52 +440,68 @@ struct Memory::Impl { } } - /** - * Reads a particular data type out of memory at the given virtual address. - * - * @param vaddr The virtual address to read the data type from. - * - * @tparam T The data type to read out of memory. This type *must* be - * trivially copyable, otherwise the behavior of this function - * is undefined. - * - * @returns The instance of T read from the specified virtual address. - */ - template <typename T> - T Read(VAddr vaddr) { + [[nodiscard]] u8* GetPointerImpl(VAddr vaddr, auto on_unmapped, auto on_rasterizer) const { // AARCH64 masks the upper 16 bit of all memory accesses vaddr &= 0xffffffffffffLL; if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) { - LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr); - return 0; + on_unmapped(); + return nullptr; } // Avoid adding any extra logic to this fast-path block const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw(); - if (const u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { - T value; - std::memcpy(&value, &pointer[vaddr], sizeof(T)); - return value; + if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { + return &pointer[vaddr]; } switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) { case Common::PageType::Unmapped: - LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr); - return 0; + on_unmapped(); + return nullptr; case Common::PageType::Memory: - ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); - break; + ASSERT_MSG(false, "Mapped memory page without a pointer @ 0x{:016X}", vaddr); + return nullptr; case Common::PageType::RasterizerCachedMemory: { - const u8* const host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; - system.GPU().FlushRegion(vaddr, sizeof(T)); - T value; - std::memcpy(&value, host_ptr, sizeof(T)); - return value; + u8* const host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; + on_rasterizer(); + return host_ptr; } default: UNREACHABLE(); } - return {}; + return nullptr; + } + + [[nodiscard]] u8* GetPointer(const VAddr vaddr) const { + return GetPointerImpl( + vaddr, [vaddr]() { LOG_ERROR(HW_Memory, "Unmapped GetPointer @ 0x{:016X}", vaddr); }, + []() {}); + } + + /** + * Reads a particular data type out of memory at the given virtual address. + * + * @param vaddr The virtual address to read the data type from. + * + * @tparam T The data type to read out of memory. This type *must* be + * trivially copyable, otherwise the behavior of this function + * is undefined. + * + * @returns The instance of T read from the specified virtual address. + */ + template <typename T> + T Read(VAddr vaddr) { + T result = 0; + const u8* const ptr = GetPointerImpl( + vaddr, + [vaddr]() { + LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:016X}", sizeof(T) * 8, vaddr); + }, + [&system = system, vaddr]() { system.GPU().FlushRegion(vaddr, sizeof(T)); }); + if (ptr) { + std::memcpy(&result, ptr, sizeof(T)); + } + return result; } /** @@ -656,110 +515,46 @@ struct Memory::Impl { */ template <typename T> void Write(VAddr vaddr, const T data) { - // AARCH64 masks the upper 16 bit of all memory accesses - vaddr &= 0xffffffffffffLL; - - if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) { - LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, - static_cast<u32>(data), vaddr); - return; - } - - // Avoid adding any extra logic to this fast-path block - const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw(); - if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { - std::memcpy(&pointer[vaddr], &data, sizeof(T)); - return; - } - switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) { - case Common::PageType::Unmapped: - LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, - static_cast<u32>(data), vaddr); - return; - case Common::PageType::Memory: - ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); - break; - case Common::PageType::RasterizerCachedMemory: { - u8* const host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; - system.GPU().InvalidateRegion(vaddr, sizeof(T)); - std::memcpy(host_ptr, &data, sizeof(T)); - break; - } - default: - UNREACHABLE(); + u8* const ptr = GetPointerImpl( + vaddr, + [vaddr, data]() { + LOG_ERROR(HW_Memory, "Unmapped Write{} @ 0x{:016X} = 0x{:016X}", sizeof(T) * 8, + vaddr, static_cast<u64>(data)); + }, + [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); }); + if (ptr) { + std::memcpy(ptr, &data, sizeof(T)); } } template <typename T> bool WriteExclusive(VAddr vaddr, const T data, const T expected) { - // AARCH64 masks the upper 16 bit of all memory accesses - vaddr &= 0xffffffffffffLL; - - if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) { - LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, - static_cast<u32>(data), vaddr); - return true; - } - - const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw(); - if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { - // NOTE: Avoid adding any extra logic to this fast-path block - const auto volatile_pointer = reinterpret_cast<volatile T*>(&pointer[vaddr]); + u8* const ptr = GetPointerImpl( + vaddr, + [vaddr, data]() { + LOG_ERROR(HW_Memory, "Unmapped WriteExclusive{} @ 0x{:016X} = 0x{:016X}", + sizeof(T) * 8, vaddr, static_cast<u64>(data)); + }, + [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); }); + if (ptr) { + const auto volatile_pointer = reinterpret_cast<volatile T*>(ptr); return Common::AtomicCompareAndSwap(volatile_pointer, data, expected); } - switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) { - case Common::PageType::Unmapped: - LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, - static_cast<u32>(data), vaddr); - return true; - case Common::PageType::Memory: - ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); - break; - case Common::PageType::RasterizerCachedMemory: { - u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; - system.GPU().InvalidateRegion(vaddr, sizeof(T)); - auto* pointer = reinterpret_cast<volatile T*>(&host_ptr); - return Common::AtomicCompareAndSwap(pointer, data, expected); - } - default: - UNREACHABLE(); - } return true; } bool WriteExclusive128(VAddr vaddr, const u128 data, const u128 expected) { - // AARCH64 masks the upper 16 bit of all memory accesses - vaddr &= 0xffffffffffffLL; - - if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) { - LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, - static_cast<u32>(data[0]), vaddr); - return true; - } - - const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw(); - if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { - // NOTE: Avoid adding any extra logic to this fast-path block - const auto volatile_pointer = reinterpret_cast<volatile u64*>(&pointer[vaddr]); + u8* const ptr = GetPointerImpl( + vaddr, + [vaddr, data]() { + LOG_ERROR(HW_Memory, "Unmapped WriteExclusive128 @ 0x{:016X} = 0x{:016X}{:016X}", + vaddr, static_cast<u64>(data[1]), static_cast<u64>(data[0])); + }, + [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(u128)); }); + if (ptr) { + const auto volatile_pointer = reinterpret_cast<volatile u64*>(ptr); return Common::AtomicCompareAndSwap(volatile_pointer, data, expected); } - switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) { - case Common::PageType::Unmapped: - LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}{:016X}", sizeof(data) * 8, - static_cast<u64>(data[1]), static_cast<u64>(data[0]), vaddr); - return true; - case Common::PageType::Memory: - ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); - break; - case Common::PageType::RasterizerCachedMemory: { - u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; - system.GPU().InvalidateRegion(vaddr, sizeof(u128)); - auto* pointer = reinterpret_cast<volatile u64*>(&host_ptr); - return Common::AtomicCompareAndSwap(pointer, data, expected); - } - default: - UNREACHABLE(); - } return true; } @@ -789,12 +584,11 @@ void Memory::UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) { impl->UnmapRegion(page_table, base, size); } -bool Memory::IsValidVirtualAddress(const Kernel::KProcess& process, const VAddr vaddr) const { - return impl->IsValidVirtualAddress(process, vaddr); -} - bool Memory::IsValidVirtualAddress(const VAddr vaddr) const { - return impl->IsValidVirtualAddress(vaddr); + const Kernel::KProcess& process = *system.CurrentProcess(); + const auto& page_table = process.PageTable().PageTableImpl(); + const auto [pointer, type] = page_table.pointers[vaddr >> PAGE_BITS].PointerType(); + return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory; } u8* Memory::GetPointer(VAddr vaddr) { @@ -863,64 +657,38 @@ std::string Memory::ReadCString(VAddr vaddr, std::size_t max_length) { void Memory::ReadBlock(const Kernel::KProcess& process, const VAddr src_addr, void* dest_buffer, const std::size_t size) { - impl->ReadBlock(process, src_addr, dest_buffer, size); + impl->ReadBlockImpl<false>(process, src_addr, dest_buffer, size); } void Memory::ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) { impl->ReadBlock(src_addr, dest_buffer, size); } -void Memory::ReadBlockUnsafe(const Kernel::KProcess& process, const VAddr src_addr, - void* dest_buffer, const std::size_t size) { - impl->ReadBlockUnsafe(process, src_addr, dest_buffer, size); -} - void Memory::ReadBlockUnsafe(const VAddr src_addr, void* dest_buffer, const std::size_t size) { impl->ReadBlockUnsafe(src_addr, dest_buffer, size); } void Memory::WriteBlock(const Kernel::KProcess& process, VAddr dest_addr, const void* src_buffer, std::size_t size) { - impl->WriteBlock(process, dest_addr, src_buffer, size); + impl->WriteBlockImpl<false>(process, dest_addr, src_buffer, size); } void Memory::WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) { impl->WriteBlock(dest_addr, src_buffer, size); } -void Memory::WriteBlockUnsafe(const Kernel::KProcess& process, VAddr dest_addr, - const void* src_buffer, std::size_t size) { - impl->WriteBlockUnsafe(process, dest_addr, src_buffer, size); -} - void Memory::WriteBlockUnsafe(const VAddr dest_addr, const void* src_buffer, const std::size_t size) { impl->WriteBlockUnsafe(dest_addr, src_buffer, size); } -void Memory::ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size) { - impl->ZeroBlock(process, dest_addr, size); -} - -void Memory::ZeroBlock(VAddr dest_addr, std::size_t size) { - impl->ZeroBlock(dest_addr, size); -} - void Memory::CopyBlock(const Kernel::KProcess& process, VAddr dest_addr, VAddr src_addr, const std::size_t size) { impl->CopyBlock(process, dest_addr, src_addr, size); } -void Memory::CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size) { - impl->CopyBlock(dest_addr, src_addr, size); -} - void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) { impl->RasterizerMarkRegionCached(vaddr, size, cached); } -bool IsKernelVirtualAddress(const VAddr vaddr) { - return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END; -} - } // namespace Core::Memory diff --git a/src/core/memory.h b/src/core/memory.h index c91eeced9..b5721b740 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -39,11 +39,6 @@ enum : VAddr { /// Application stack DEFAULT_STACK_SIZE = 0x100000, - - /// Kernel Virtual Address Range - KERNEL_REGION_VADDR = 0xFFFFFF8000000000, - KERNEL_REGION_SIZE = 0x7FFFE00000, - KERNEL_REGION_END = KERNEL_REGION_VADDR + KERNEL_REGION_SIZE, }; /// Central class that handles all memory operations and state. @@ -56,7 +51,7 @@ public: Memory& operator=(const Memory&) = delete; Memory(Memory&&) = default; - Memory& operator=(Memory&&) = default; + Memory& operator=(Memory&&) = delete; /** * Resets the state of the Memory system. @@ -92,24 +87,13 @@ public: /** * Checks whether or not the supplied address is a valid virtual - * address for the given process. - * - * @param process The emulated process to check the address against. - * @param vaddr The virtual address to check the validity of. - * - * @returns True if the given virtual address is valid, false otherwise. - */ - bool IsValidVirtualAddress(const Kernel::KProcess& process, VAddr vaddr) const; - - /** - * Checks whether or not the supplied address is a valid virtual * address for the current process. * * @param vaddr The virtual address to check the validity of. * * @returns True if the given virtual address is valid, false otherwise. */ - bool IsValidVirtualAddress(VAddr vaddr) const; + [[nodiscard]] bool IsValidVirtualAddress(VAddr vaddr) const; /** * Gets a pointer to the given address. @@ -134,7 +118,7 @@ public: * @returns The pointer to the given address, if the address is valid. * If the address is not valid, nullptr will be returned. */ - const u8* GetPointer(VAddr vaddr) const; + [[nodiscard]] const u8* GetPointer(VAddr vaddr) const; template <typename T> const T* GetPointer(VAddr vaddr) const { @@ -328,27 +312,6 @@ public: std::size_t size); /** - * Reads a contiguous block of bytes from a specified process' address space. - * This unsafe version does not trigger GPU flushing. - * - * @param process The process to read the data from. - * @param src_addr The virtual address to begin reading from. - * @param dest_buffer The buffer to place the read bytes into. - * @param size The amount of data to read, in bytes. - * - * @note If a size of 0 is specified, then this function reads nothing and - * no attempts to access memory are made at all. - * - * @pre dest_buffer must be at least size bytes in length, otherwise a - * buffer overrun will occur. - * - * @post The range [dest_buffer, size) contains the read bytes from the - * process' address space. - */ - void ReadBlockUnsafe(const Kernel::KProcess& process, VAddr src_addr, void* dest_buffer, - std::size_t size); - - /** * Reads a contiguous block of bytes from the current process' address space. * * @param src_addr The virtual address to begin reading from. @@ -409,26 +372,6 @@ public: std::size_t size); /** - * Writes a range of bytes into a given process' address space at the specified - * virtual address. - * This unsafe version does not invalidate GPU Memory. - * - * @param process The process to write data into the address space of. - * @param dest_addr The destination virtual address to begin writing the data at. - * @param src_buffer The data to write into the process' address space. - * @param size The size of the data to write, in bytes. - * - * @post The address range [dest_addr, size) in the process' address space - * contains the data that was within src_buffer. - * - * @post If an attempt is made to write into an unmapped region of memory, the writes - * will be ignored and an error will be logged. - * - */ - void WriteBlockUnsafe(const Kernel::KProcess& process, VAddr dest_addr, const void* src_buffer, - std::size_t size); - - /** * Writes a range of bytes into the current process' address space at the specified * virtual address. * @@ -468,29 +411,6 @@ public: void WriteBlockUnsafe(VAddr dest_addr, const void* src_buffer, std::size_t size); /** - * Fills the specified address range within a process' address space with zeroes. - * - * @param process The process that will have a portion of its memory zeroed out. - * @param dest_addr The starting virtual address of the range to zero out. - * @param size The size of the address range to zero out, in bytes. - * - * @post The range [dest_addr, size) within the process' address space is - * filled with zeroes. - */ - void ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size); - - /** - * Fills the specified address range within the current process' address space with zeroes. - * - * @param dest_addr The starting virtual address of the range to zero out. - * @param size The size of the address range to zero out, in bytes. - * - * @post The range [dest_addr, size) within the current process' address space is - * filled with zeroes. - */ - void ZeroBlock(VAddr dest_addr, std::size_t size); - - /** * Copies data within a process' address space to another location within the * same address space. * @@ -506,19 +426,6 @@ public: std::size_t size); /** - * Copies data within the current process' address space to another location within the - * same address space. - * - * @param dest_addr The destination virtual address to begin copying the data into. - * @param src_addr The source virtual address to begin copying the data from. - * @param size The size of the data to copy, in bytes. - * - * @post The range [dest_addr, size) within the current process' address space - * contains the same data within the range [src_addr, size). - */ - void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size); - - /** * Marks each page within the specified address range as cached or uncached. * * @param vaddr The virtual address indicating the start of the address range. @@ -535,7 +442,4 @@ private: std::unique_ptr<Impl> impl; }; -/// Determines if the given VAddr is a kernel address -bool IsKernelVirtualAddress(VAddr vaddr); - } // namespace Core::Memory diff --git a/src/core/network/network.cpp b/src/core/network/network.cpp index 375bc79ec..4732d4485 100644 --- a/src/core/network/network.cpp +++ b/src/core/network/network.cpp @@ -10,9 +10,10 @@ #include "common/common_funcs.h" #ifdef _WIN32 -#define _WINSOCK_DEPRECATED_NO_WARNINGS // gethostname #include <winsock2.h> +#include <ws2tcpip.h> #elif YUZU_UNIX +#include <arpa/inet.h> #include <errno.h> #include <fcntl.h> #include <netdb.h> @@ -27,7 +28,9 @@ #include "common/assert.h" #include "common/common_types.h" #include "common/logging/log.h" +#include "common/settings.h" #include "core/network/network.h" +#include "core/network/network_interface.h" #include "core/network/sockets.h" namespace Network { @@ -47,11 +50,6 @@ void Finalize() { WSACleanup(); } -constexpr IPv4Address TranslateIPv4(in_addr addr) { - auto& bytes = addr.S_un.S_un_b; - return IPv4Address{bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4}; -} - sockaddr TranslateFromSockAddrIn(SockAddrIn input) { sockaddr_in result; @@ -138,12 +136,6 @@ void Initialize() {} void Finalize() {} -constexpr IPv4Address TranslateIPv4(in_addr addr) { - const u32 bytes = addr.s_addr; - return IPv4Address{static_cast<u8>(bytes), static_cast<u8>(bytes >> 8), - static_cast<u8>(bytes >> 16), static_cast<u8>(bytes >> 24)}; -} - sockaddr TranslateFromSockAddrIn(SockAddrIn input) { sockaddr_in result; @@ -182,7 +174,7 @@ linger MakeLinger(bool enable, u32 linger_value) { } bool EnableNonBlock(int fd, bool enable) { - int flags = fcntl(fd, F_GETFD); + int flags = fcntl(fd, F_GETFL); if (flags == -1) { return false; } @@ -191,7 +183,7 @@ bool EnableNonBlock(int fd, bool enable) { } else { flags &= ~O_NONBLOCK; } - return fcntl(fd, F_SETFD, flags) == 0; + return fcntl(fd, F_SETFL, flags) == 0; } Errno TranslateNativeError(int e) { @@ -227,8 +219,12 @@ Errno GetAndLogLastError() { #else int e = errno; #endif + const Errno err = TranslateNativeError(e); + if (err == Errno::AGAIN) { + return err; + } LOG_ERROR(Network, "Socket operation error: {}", NativeErrorToString(e)); - return TranslateNativeError(e); + return err; } int TranslateDomain(Domain domain) { @@ -353,27 +349,29 @@ NetworkInstance::~NetworkInstance() { Finalize(); } -std::pair<IPv4Address, Errno> GetHostIPv4Address() { - std::array<char, 256> name{}; - if (gethostname(name.data(), static_cast<int>(name.size()) - 1) == SOCKET_ERROR) { - return {IPv4Address{}, GetAndLogLastError()}; +std::optional<IPv4Address> GetHostIPv4Address() { + const std::string& selected_network_interface = Settings::values.network_interface.GetValue(); + const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); + if (network_interfaces.size() == 0) { + LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces"); + return {}; } - hostent* const ent = gethostbyname(name.data()); - if (!ent) { - return {IPv4Address{}, GetAndLogLastError()}; - } - if (ent->h_addr_list == nullptr) { - UNIMPLEMENTED_MSG("No addr provided in hostent->h_addr_list"); - return {IPv4Address{}, Errno::SUCCESS}; - } - if (ent->h_length != sizeof(in_addr)) { - UNIMPLEMENTED_MSG("Unexpected size={} in hostent->h_length", ent->h_length); - } + const auto res = + std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) { + return iface.name == selected_network_interface; + }); + + 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); - in_addr addr; - std::memcpy(&addr, ent->h_addr_list[0], sizeof(addr)); - return {TranslateIPv4(addr), Errno::SUCCESS}; + return TranslateIPv4(res->ip_address); + } else { + LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface); + return {}; + } } std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) { diff --git a/src/core/network/network.h b/src/core/network/network.h index bd30f1899..fdd3e4655 100644 --- a/src/core/network/network.h +++ b/src/core/network/network.h @@ -5,11 +5,18 @@ #pragma once #include <array> +#include <optional> #include <utility> #include "common/common_funcs.h" #include "common/common_types.h" +#ifdef _WIN32 +#include <winsock2.h> +#elif YUZU_UNIX +#include <netinet/in.h> +#endif + namespace Network { class Socket; @@ -92,8 +99,21 @@ public: ~NetworkInstance(); }; +#ifdef _WIN32 +constexpr IPv4Address TranslateIPv4(in_addr addr) { + auto& bytes = addr.S_un.S_un_b; + return IPv4Address{bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4}; +} +#elif YUZU_UNIX +constexpr IPv4Address TranslateIPv4(in_addr addr) { + const u32 bytes = addr.s_addr; + return IPv4Address{static_cast<u8>(bytes), static_cast<u8>(bytes >> 8), + static_cast<u8>(bytes >> 16), static_cast<u8>(bytes >> 24)}; +} +#endif + /// @brief Returns host's IPv4 address -/// @return Pair of an array of human ordered IPv4 address (e.g. 192.168.0.1) and an error code -std::pair<IPv4Address, Errno> GetHostIPv4Address(); +/// @return human ordered IPv4 address (e.g. 192.168.0.1) as an array +std::optional<IPv4Address> GetHostIPv4Address(); } // namespace Network diff --git a/src/core/network/network_interface.cpp b/src/core/network/network_interface.cpp new file mode 100644 index 000000000..cecc9aa11 --- /dev/null +++ b/src/core/network/network_interface.cpp @@ -0,0 +1,203 @@ +// Copyright 2021 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <fstream> +#include <sstream> +#include <vector> + +#include "common/bit_cast.h" +#include "common/common_types.h" +#include "common/logging/log.h" +#include "common/settings.h" +#include "common/string_util.h" +#include "core/network/network_interface.h" + +#ifdef _WIN32 +#include <iphlpapi.h> +#else +#include <cerrno> +#include <ifaddrs.h> +#include <net/if.h> +#endif + +namespace Network { + +#ifdef _WIN32 + +std::vector<NetworkInterface> GetAvailableNetworkInterfaces() { + std::vector<IP_ADAPTER_ADDRESSES> adapter_addresses; + DWORD ret = ERROR_BUFFER_OVERFLOW; + DWORD buf_size = 0; + + // retry up to 5 times + for (int i = 0; i < 5 && ret == ERROR_BUFFER_OVERFLOW; i++) { + ret = GetAdaptersAddresses( + AF_INET, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_GATEWAYS, + nullptr, adapter_addresses.data(), &buf_size); + + if (ret == ERROR_BUFFER_OVERFLOW) { + adapter_addresses.resize((buf_size / sizeof(IP_ADAPTER_ADDRESSES)) + 1); + } else { + break; + } + } + + if (ret == NO_ERROR) { + std::vector<NetworkInterface> result; + + for (auto current_address = adapter_addresses.data(); current_address != nullptr; + current_address = current_address->Next) { + if (current_address->FirstUnicastAddress == nullptr || + current_address->FirstUnicastAddress->Address.lpSockaddr == nullptr) { + continue; + } + + if (current_address->OperStatus != IfOperStatusUp) { + continue; + } + + const auto ip_addr = Common::BitCast<struct sockaddr_in>( + *current_address->FirstUnicastAddress->Address.lpSockaddr) + .sin_addr; + + ULONG mask = 0; + if (ConvertLengthToIpv4Mask(current_address->FirstUnicastAddress->OnLinkPrefixLength, + &mask) != NO_ERROR) { + LOG_ERROR(Network, "Failed to convert IPv4 prefix length to subnet mask"); + continue; + } + + struct in_addr gateway = {.S_un{.S_addr{0}}}; + if (current_address->FirstGatewayAddress != nullptr && + current_address->FirstGatewayAddress->Address.lpSockaddr != nullptr) { + gateway = Common::BitCast<struct sockaddr_in>( + *current_address->FirstGatewayAddress->Address.lpSockaddr) + .sin_addr; + } + + result.push_back(NetworkInterface{ + .name{Common::UTF16ToUTF8(std::wstring{current_address->FriendlyName})}, + .ip_address{ip_addr}, + .subnet_mask = in_addr{.S_un{.S_addr{mask}}}, + .gateway = gateway}); + } + + return result; + } else { + LOG_ERROR(Network, "Failed to get network interfaces with GetAdaptersAddresses"); + return {}; + } +} + +#else + +std::vector<NetworkInterface> GetAvailableNetworkInterfaces() { + std::vector<NetworkInterface> result; + + struct ifaddrs* ifaddr = nullptr; + + if (getifaddrs(&ifaddr) != 0) { + LOG_ERROR(Network, "Failed to get network interfaces with getifaddrs: {}", + std::strerror(errno)); + return result; + } + + for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr) { + continue; + } + + if (ifa->ifa_addr->sa_family != AF_INET) { + continue; + } + + if ((ifa->ifa_flags & IFF_UP) == 0 || (ifa->ifa_flags & IFF_LOOPBACK) != 0) { + continue; + } + + std::uint32_t gateway{0}; + std::ifstream file{"/proc/net/route"}; + if (file.is_open()) { + + // ignore header + file.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); + + bool gateway_found = false; + + for (std::string line; std::getline(file, line);) { + std::istringstream iss{line}; + + std::string iface_name{}; + iss >> iface_name; + if (iface_name != ifa->ifa_name) { + continue; + } + + iss >> std::hex; + + std::uint32_t dest{0}; + iss >> dest; + if (dest != 0) { + // not the default route + continue; + } + + iss >> gateway; + + std::uint16_t flags{0}; + iss >> flags; + + // flag RTF_GATEWAY (defined in <linux/route.h>) + if ((flags & 0x2) == 0) { + continue; + } + + gateway_found = true; + break; + } + + if (!gateway_found) { + gateway = 0; + } + } else { + LOG_ERROR(Network, "Failed to open \"/proc/net/route\""); + } + + result.push_back(NetworkInterface{ + .name{ifa->ifa_name}, + .ip_address{Common::BitCast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr}, + .subnet_mask{Common::BitCast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr}, + .gateway{in_addr{.s_addr = gateway}}}); + } + + freeifaddrs(ifaddr); + + return result; +} + +#endif + +std::optional<NetworkInterface> GetSelectedNetworkInterface() { + const std::string& selected_network_interface = Settings::values.network_interface.GetValue(); + const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); + if (network_interfaces.size() == 0) { + LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces"); + return {}; + } + + const auto res = + std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) { + return iface.name == selected_network_interface; + }); + + if (res != network_interfaces.end()) { + return *res; + } else { + LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface); + return {}; + } +} + +} // namespace Network diff --git a/src/core/network/network_interface.h b/src/core/network/network_interface.h new file mode 100644 index 000000000..980edb2f5 --- /dev/null +++ b/src/core/network/network_interface.h @@ -0,0 +1,29 @@ +// Copyright 2021 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <optional> +#include <string> +#include <vector> + +#ifdef _WIN32 +#include <winsock2.h> +#else +#include <netinet/in.h> +#endif + +namespace Network { + +struct NetworkInterface { + std::string name; + struct in_addr ip_address; + struct in_addr subnet_mask; + struct in_addr gateway; +}; + +std::vector<NetworkInterface> GetAvailableNetworkInterfaces(); +std::optional<NetworkInterface> GetSelectedNetworkInterface(); + +} // namespace Network diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index 8de3d4520..ff23230f0 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp @@ -304,10 +304,10 @@ std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers([ } std::string GenerateKeyboardParam(int key_code) { - Common::ParamPackage param{ - {"engine", "keyboard"}, - {"code", std::to_string(key_code)}, - }; + Common::ParamPackage param; + param.Set("engine", "keyboard"); + param.Set("code", key_code); + param.Set("toggle", false); return param.Serialize(); } diff --git a/src/input_common/mouse/mouse_poller.cpp b/src/input_common/mouse/mouse_poller.cpp index efcdd85d2..090b26972 100644 --- a/src/input_common/mouse/mouse_poller.cpp +++ b/src/input_common/mouse/mouse_poller.cpp @@ -57,6 +57,7 @@ Common::ParamPackage MouseButtonFactory::GetNextInput() const { if (pad.button != MouseInput::MouseButton::Undefined) { params.Set("engine", "mouse"); params.Set("button", static_cast<u16>(pad.button)); + params.Set("toggle", false); return params; } } diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index 70a0ba09c..f102410d1 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -82,6 +82,12 @@ public: state.buttons.insert_or_assign(button, value); } + void PreSetButton(int button) { + if (!state.buttons.contains(button)) { + SetButton(button, false); + } + } + void SetMotion(SDL_ControllerSensorEvent event) { constexpr float gravity_constant = 9.80665f; std::lock_guard lock{mutex}; @@ -155,9 +161,16 @@ public: state.axes.insert_or_assign(axis, value); } - float GetAxis(int axis, float range) const { + void PreSetAxis(int axis) { + if (!state.axes.contains(axis)) { + SetAxis(axis, 0); + } + } + + float GetAxis(int axis, float range, float offset) const { std::lock_guard lock{mutex}; - return static_cast<float>(state.axes.at(axis)) / (32767.0f * range); + const float value = static_cast<float>(state.axes.at(axis)) / 32767.0f; + return (value + offset) / range; } bool RumblePlay(u16 amp_low, u16 amp_high) { @@ -174,9 +187,10 @@ public: return false; } - std::tuple<float, float> GetAnalog(int axis_x, int axis_y, float range) const { - float x = GetAxis(axis_x, range); - float y = GetAxis(axis_y, range); + std::tuple<float, float> GetAnalog(int axis_x, int axis_y, float range, float offset_x, + float offset_y) const { + float x = GetAxis(axis_x, range, offset_x); + float y = GetAxis(axis_y, range, offset_y); y = -y; // 3DS uses an y-axis inverse from SDL // Make sure the coordinates are in the unit circle, @@ -483,7 +497,7 @@ public: trigger_if_greater(trigger_if_greater_) {} bool GetStatus() const override { - const float axis_value = joystick->GetAxis(axis, 1.0f); + const float axis_value = joystick->GetAxis(axis, 1.0f, 0.0f); if (trigger_if_greater) { return axis_value > threshold; } @@ -500,12 +514,14 @@ private: class SDLAnalog final : public Input::AnalogDevice { public: explicit SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_, - bool invert_x_, bool invert_y_, float deadzone_, float range_) + bool invert_x_, bool invert_y_, float deadzone_, float range_, + float offset_x_, float offset_y_) : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), invert_x(invert_x_), - invert_y(invert_y_), deadzone(deadzone_), range(range_) {} + invert_y(invert_y_), deadzone(deadzone_), range(range_), offset_x(offset_x_), + offset_y(offset_y_) {} std::tuple<float, float> GetStatus() const override { - auto [x, y] = joystick->GetAnalog(axis_x, axis_y, range); + auto [x, y] = joystick->GetAnalog(axis_x, axis_y, range, offset_x, offset_y); const float r = std::sqrt((x * x) + (y * y)); if (invert_x) { x = -x; @@ -522,8 +538,8 @@ public: } std::tuple<float, float> GetRawStatus() const override { - const float x = joystick->GetAxis(axis_x, range); - const float y = joystick->GetAxis(axis_y, range); + const float x = joystick->GetAxis(axis_x, range, offset_x); + const float y = joystick->GetAxis(axis_y, range, offset_y); return {x, -y}; } @@ -555,6 +571,8 @@ private: const bool invert_y; const float deadzone; const float range; + const float offset_x; + const float offset_y; }; class SDLVibration final : public Input::VibrationDevice { @@ -621,7 +639,7 @@ public: trigger_if_greater(trigger_if_greater_) {} Input::MotionStatus GetStatus() const override { - const float axis_value = joystick->GetAxis(axis, 1.0f); + const float axis_value = joystick->GetAxis(axis, 1.0f, 0.0f); bool trigger = axis_value < threshold; if (trigger_if_greater) { trigger = axis_value > threshold; @@ -720,13 +738,13 @@ public: LOG_ERROR(Input, "Unknown direction {}", direction_name); } // This is necessary so accessing GetAxis with axis won't crash - joystick->SetAxis(axis, 0); + joystick->PreSetAxis(axis); return std::make_unique<SDLAxisButton>(joystick, axis, threshold, trigger_if_greater); } const int button = params.Get("button", 0); // This is necessary so accessing GetButton with button won't crash - joystick->SetButton(button, false); + joystick->PreSetButton(button); return std::make_unique<SDLButton>(joystick, button, toggle); } @@ -757,13 +775,15 @@ public: const std::string invert_y_value = params.Get("invert_y", "+"); const bool invert_x = invert_x_value == "-"; const bool invert_y = invert_y_value == "-"; + const float offset_x = params.Get("offset_x", 0.0f); + const float offset_y = params.Get("offset_y", 0.0f); auto joystick = state.GetSDLJoystickByGUID(guid, port); // This is necessary so accessing GetAxis with axis_x and axis_y won't crash - joystick->SetAxis(axis_x, 0); - joystick->SetAxis(axis_y, 0); + joystick->PreSetAxis(axis_x); + joystick->PreSetAxis(axis_y); return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, invert_x, invert_y, deadzone, - range); + range, offset_x, offset_y); } private: @@ -844,13 +864,13 @@ public: LOG_ERROR(Input, "Unknown direction {}", direction_name); } // This is necessary so accessing GetAxis with axis won't crash - joystick->SetAxis(axis, 0); + joystick->PreSetAxis(axis); return std::make_unique<SDLAxisMotion>(joystick, axis, threshold, trigger_if_greater); } const int button = params.Get("button", 0); // This is necessary so accessing GetButton with button won't crash - joystick->SetButton(button, false); + joystick->PreSetButton(button); return std::make_unique<SDLButtonMotion>(joystick, button); } @@ -869,6 +889,9 @@ SDLState::SDLState() { RegisterFactory<VibrationDevice>("sdl", vibration_factory); RegisterFactory<MotionDevice>("sdl", motion_factory); + // Disable raw input. When enabled this setting causes SDL to die when a web applet opens + SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, "0"); + // Enable HIDAPI rumble. This prevents SDL from disabling motion on PS4 and PS5 controllers SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1"); @@ -995,6 +1018,7 @@ Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid params.Set("port", port); params.Set("guid", std::move(guid)); params.Set("button", button); + params.Set("toggle", false); return params; } @@ -1134,13 +1158,15 @@ Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& gu } Common::ParamPackage BuildParamPackageForAnalog(int port, const std::string& guid, int axis_x, - int axis_y) { + int axis_y, float offset_x, float offset_y) { Common::ParamPackage params; params.Set("engine", "sdl"); params.Set("port", port); params.Set("guid", guid); params.Set("axis_x", axis_x); params.Set("axis_y", axis_y); + params.Set("offset_x", offset_x); + params.Set("offset_y", offset_y); params.Set("invert_x", "+"); params.Set("invert_y", "+"); return params; @@ -1342,24 +1368,39 @@ AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& pa const auto& binding_left_y = SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY); if (params.Has("guid2")) { + joystick2->PreSetAxis(binding_left_x.value.axis); + joystick2->PreSetAxis(binding_left_y.value.axis); + const auto left_offset_x = -joystick2->GetAxis(binding_left_x.value.axis, 1.0f, 0); + const auto left_offset_y = -joystick2->GetAxis(binding_left_y.value.axis, 1.0f, 0); mapping.insert_or_assign( Settings::NativeAnalog::LStick, BuildParamPackageForAnalog(joystick2->GetPort(), joystick2->GetGUID(), - binding_left_x.value.axis, binding_left_y.value.axis)); + binding_left_x.value.axis, binding_left_y.value.axis, + left_offset_x, left_offset_y)); } else { + joystick->PreSetAxis(binding_left_x.value.axis); + joystick->PreSetAxis(binding_left_y.value.axis); + const auto left_offset_x = -joystick->GetAxis(binding_left_x.value.axis, 1.0f, 0); + const auto left_offset_y = -joystick->GetAxis(binding_left_y.value.axis, 1.0f, 0); mapping.insert_or_assign( Settings::NativeAnalog::LStick, BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), - binding_left_x.value.axis, binding_left_y.value.axis)); + binding_left_x.value.axis, binding_left_y.value.axis, + left_offset_x, left_offset_y)); } const auto& binding_right_x = SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX); const auto& binding_right_y = SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY); + joystick->PreSetAxis(binding_right_x.value.axis); + joystick->PreSetAxis(binding_right_y.value.axis); + const auto right_offset_x = -joystick->GetAxis(binding_right_x.value.axis, 1.0f, 0); + const auto right_offset_y = -joystick->GetAxis(binding_right_y.value.axis, 1.0f, 0); mapping.insert_or_assign(Settings::NativeAnalog::RStick, BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), binding_right_x.value.axis, - binding_right_y.value.axis)); + binding_right_y.value.axis, right_offset_x, + right_offset_y)); return mapping; } @@ -1563,8 +1604,9 @@ public: } if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) { + // Set offset to zero since the joystick is not on center auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), - first_axis, axis); + first_axis, axis, 0, 0); first_axis = -1; return params; } diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 1eb67c051..2f6cdd216 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -97,6 +97,7 @@ add_library(video_core STATIC renderer_opengl/gl_stream_buffer.h renderer_opengl/gl_texture_cache.cpp renderer_opengl/gl_texture_cache.h + renderer_opengl/gl_texture_cache_base.cpp renderer_opengl/gl_query_cache.cpp renderer_opengl/gl_query_cache.h renderer_opengl/maxwell_to_gl.h @@ -155,6 +156,7 @@ add_library(video_core STATIC renderer_vulkan/vk_swapchain.h renderer_vulkan/vk_texture_cache.cpp renderer_vulkan/vk_texture_cache.h + renderer_vulkan/vk_texture_cache_base.cpp renderer_vulkan/vk_update_descriptor.cpp renderer_vulkan/vk_update_descriptor.h shader_cache.cpp @@ -186,6 +188,7 @@ add_library(video_core STATIC texture_cache/samples_helper.h texture_cache/slot_vector.h texture_cache/texture_cache.h + texture_cache/texture_cache_base.h texture_cache/types.h texture_cache/util.cpp texture_cache/util.h diff --git a/src/video_core/command_classes/codecs/vp9.cpp b/src/video_core/command_classes/codecs/vp9.cpp index 7eecb3991..70030066a 100644 --- a/src/video_core/command_classes/codecs/vp9.cpp +++ b/src/video_core/command_classes/codecs/vp9.cpp @@ -397,14 +397,14 @@ Vp9FrameContainer VP9::GetCurrentFrame(const NvdecCommon::NvdecRegisters& state) next_frame = std::move(temp); } else { next_frame.info = current_frame.info; - next_frame.bit_stream = std::move(current_frame.bit_stream); + next_frame.bit_stream = current_frame.bit_stream; } return current_frame; } std::vector<u8> VP9::ComposeCompressedHeader() { VpxRangeEncoder writer{}; - const bool update_probs = current_frame_info.show_frame && !current_frame_info.is_key_frame; + const bool update_probs = !current_frame_info.is_key_frame && current_frame_info.show_frame; if (!current_frame_info.lossless) { if (static_cast<u32>(current_frame_info.transform_mode) >= 3) { writer.Write(3, 2); diff --git a/src/video_core/command_classes/codecs/vp9_types.h b/src/video_core/command_classes/codecs/vp9_types.h index 6820afa26..87eafdb03 100644 --- a/src/video_core/command_classes/codecs/vp9_types.h +++ b/src/video_core/command_classes/codecs/vp9_types.h @@ -176,7 +176,7 @@ struct PictureInfo { .frame_size_changed = (vp9_flags & FrameFlags::FrameSizeChanged) != 0, .error_resilient_mode = (vp9_flags & FrameFlags::ErrorResilientMode) != 0, .last_frame_shown = (vp9_flags & FrameFlags::LastShowFrame) != 0, - .show_frame = false, + .show_frame = true, .ref_frame_sign_bias = ref_frame_sign_bias, .base_q_index = base_q_index, .y_dc_delta_q = y_dc_delta_q, diff --git a/src/video_core/command_classes/nvdec.cpp b/src/video_core/command_classes/nvdec.cpp index b5e3b70fc..b5c55f14a 100644 --- a/src/video_core/command_classes/nvdec.cpp +++ b/src/video_core/command_classes/nvdec.cpp @@ -39,7 +39,7 @@ void Nvdec::Execute() { codec->Decode(); break; default: - UNIMPLEMENTED_MSG("Unknown codec {}", static_cast<u32>(codec->GetCurrentCodec())); + UNIMPLEMENTED_MSG("Codec {}", codec->GetCurrentCodecName()); break; } } diff --git a/src/video_core/command_classes/vic.cpp b/src/video_core/command_classes/vic.cpp index d5e77941c..0ee07f398 100644 --- a/src/video_core/command_classes/vic.cpp +++ b/src/video_core/command_classes/vic.cpp @@ -96,12 +96,11 @@ void Vic::Execute() { if (!converted_frame_buffer) { converted_frame_buffer = AVMallocPtr{static_cast<u8*>(av_malloc(linear_size)), av_free}; } - - const int converted_stride{frame->width * 4}; + 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); + &converted_frame_buf_addr, converted_stride.data()); const u32 blk_kind = static_cast<u32>(config.block_linear_kind); if (blk_kind != 0) { diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp index fac0034fb..bccb37a58 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp @@ -15,7 +15,7 @@ #include "video_core/renderer_opengl/gl_shader_util.h" #include "video_core/renderer_opengl/gl_state_tracker.h" #include "video_core/shader_notify.h" -#include "video_core/texture_cache/texture_cache.h" +#include "video_core/texture_cache/texture_cache_base.h" #if defined(_MSC_VER) && defined(NDEBUG) #define LAMBDA_FORCEINLINE [[msvc::forceinline]] diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 41d2b73f4..b909c387e 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -32,7 +32,7 @@ #include "video_core/renderer_opengl/maxwell_to_gl.h" #include "video_core/renderer_opengl/renderer_opengl.h" #include "video_core/shader_cache.h" -#include "video_core/texture_cache/texture_cache.h" +#include "video_core/texture_cache/texture_cache_base.h" namespace OpenGL { diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index c373c9cb4..b0aee6cc1 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp @@ -18,10 +18,8 @@ #include "video_core/renderer_opengl/maxwell_to_gl.h" #include "video_core/renderer_opengl/util_shaders.h" #include "video_core/surface.h" -#include "video_core/texture_cache/format_lookup_table.h" +#include "video_core/texture_cache/formatter.h" #include "video_core/texture_cache/samples_helper.h" -#include "video_core/texture_cache/texture_cache.h" -#include "video_core/textures/decoders.h" namespace OpenGL { namespace { diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h index 921072ebe..4a4f6301c 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.h +++ b/src/video_core/renderer_opengl/gl_texture_cache.h @@ -12,7 +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/texture_cache.h" +#include "video_core/texture_cache/texture_cache_base.h" namespace OpenGL { diff --git a/src/video_core/renderer_opengl/gl_texture_cache_base.cpp b/src/video_core/renderer_opengl/gl_texture_cache_base.cpp new file mode 100644 index 000000000..385358fea --- /dev/null +++ b/src/video_core/renderer_opengl/gl_texture_cache_base.cpp @@ -0,0 +1,10 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "video_core/renderer_opengl/gl_texture_cache.h" +#include "video_core/texture_cache/texture_cache.h" + +namespace VideoCommon { +template class VideoCommon::TextureCache<OpenGL::TextureCacheParams>; +} diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 23cef2996..3ac18ea54 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -32,7 +32,7 @@ #include "video_core/renderer_vulkan/vk_texture_cache.h" #include "video_core/renderer_vulkan/vk_update_descriptor.h" #include "video_core/shader_cache.h" -#include "video_core/texture_cache/texture_cache.h" +#include "video_core/texture_cache/texture_cache_base.h" #include "video_core/vulkan_common/vulkan_device.h" #include "video_core/vulkan_common/vulkan_wrapper.h" diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 8e029bcb3..8f4df7122 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -19,6 +19,8 @@ #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" #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/vulkan_common/vulkan_device.h" #include "video_core/vulkan_common/vulkan_memory_allocator.h" #include "video_core/vulkan_common/vulkan_wrapper.h" diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 0b73d55f8..5fe6b7ba3 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -9,7 +9,7 @@ #include "shader_recompiler/shader_info.h" #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" -#include "video_core/texture_cache/texture_cache.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/renderer_vulkan/vk_texture_cache_base.cpp b/src/video_core/renderer_vulkan/vk_texture_cache_base.cpp new file mode 100644 index 000000000..44e688342 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_texture_cache_base.cpp @@ -0,0 +1,10 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "video_core/renderer_vulkan/vk_texture_cache.h" +#include "video_core/texture_cache/texture_cache.h" + +namespace VideoCommon { +template class VideoCommon::TextureCache<Vulkan::TextureCacheParams>; +} diff --git a/src/video_core/texture_cache/image_view_info.cpp b/src/video_core/texture_cache/image_view_info.cpp index faf5b151f..6527e14c8 100644 --- a/src/video_core/texture_cache/image_view_info.cpp +++ b/src/video_core/texture_cache/image_view_info.cpp @@ -6,7 +6,7 @@ #include "common/assert.h" #include "video_core/texture_cache/image_view_info.h" -#include "video_core/texture_cache/texture_cache.h" +#include "video_core/texture_cache/texture_cache_base.h" #include "video_core/texture_cache/types.h" #include "video_core/textures/texture.h" @@ -14,6 +14,8 @@ namespace VideoCommon { namespace { +using Tegra::Texture::TextureType; + constexpr u8 RENDER_TARGET_SWIZZLE = std::numeric_limits<u8>::max(); [[nodiscard]] u8 CastSwizzle(SwizzleSource source) { diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index f34c9d9ca..a087498ff 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -4,48 +4,11 @@ #pragma once -#include <algorithm> -#include <array> -#include <bit> -#include <memory> -#include <mutex> -#include <optional> -#include <span> -#include <type_traits> -#include <unordered_map> -#include <unordered_set> -#include <utility> -#include <vector> - -#include <boost/container/small_vector.hpp> - #include "common/alignment.h" -#include "common/common_types.h" -#include "common/literals.h" -#include "common/logging/log.h" #include "common/settings.h" -#include "video_core/compatible_formats.h" -#include "video_core/delayed_destruction_ring.h" #include "video_core/dirty_flags.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/format_lookup_table.h" -#include "video_core/texture_cache/formatter.h" -#include "video_core/texture_cache/image_base.h" -#include "video_core/texture_cache/image_info.h" -#include "video_core/texture_cache/image_view_base.h" -#include "video_core/texture_cache/image_view_info.h" -#include "video_core/texture_cache/render_targets.h" #include "video_core/texture_cache/samples_helper.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" +#include "video_core/texture_cache/texture_cache_base.h" namespace VideoCommon { @@ -62,352 +25,6 @@ using VideoCore::Surface::SurfaceType; using namespace Common::Literals; template <class P> -class TextureCache { - /// Address shift for caching images into a hash table - static constexpr u64 PAGE_BITS = 20; - - /// Enables debugging features to the texture cache - static constexpr bool ENABLE_VALIDATION = P::ENABLE_VALIDATION; - /// Implement blits as copies between framebuffers - static constexpr bool FRAMEBUFFER_BLITS = P::FRAMEBUFFER_BLITS; - /// True when some copies have to be emulated - static constexpr bool HAS_EMULATED_COPIES = P::HAS_EMULATED_COPIES; - /// True when the API can provide info about the memory of the device. - static constexpr bool HAS_DEVICE_MEMORY_INFO = P::HAS_DEVICE_MEMORY_INFO; - - /// Image view ID for null descriptors - static constexpr ImageViewId NULL_IMAGE_VIEW_ID{0}; - /// Sampler ID for bugged sampler ids - static constexpr SamplerId NULL_SAMPLER_ID{0}; - - static constexpr u64 DEFAULT_EXPECTED_MEMORY = 1_GiB; - static constexpr u64 DEFAULT_CRITICAL_MEMORY = 2_GiB; - - using Runtime = typename P::Runtime; - using Image = typename P::Image; - using ImageAlloc = typename P::ImageAlloc; - using ImageView = typename P::ImageView; - using Sampler = typename P::Sampler; - using Framebuffer = typename P::Framebuffer; - - struct BlitImages { - ImageId dst_id; - ImageId src_id; - PixelFormat dst_format; - PixelFormat src_format; - }; - - template <typename T> - struct IdentityHash { - [[nodiscard]] size_t operator()(T value) const noexcept { - return static_cast<size_t>(value); - } - }; - -public: - explicit TextureCache(Runtime&, VideoCore::RasterizerInterface&, Tegra::Engines::Maxwell3D&, - Tegra::Engines::KeplerCompute&, Tegra::MemoryManager&); - - /// Notify the cache that a new frame has been queued - void TickFrame(); - - /// Return a constant reference to the given image view id - [[nodiscard]] const ImageView& GetImageView(ImageViewId id) const noexcept; - - /// Return a reference to the given image view id - [[nodiscard]] ImageView& GetImageView(ImageViewId id) noexcept; - - /// Mark an image as modified from the GPU - void MarkModification(ImageId id) noexcept; - - /// Fill image_view_ids with the graphics images in indices - void FillGraphicsImageViews(std::span<const u32> indices, - std::span<ImageViewId> image_view_ids); - - /// Fill image_view_ids with the compute images in indices - void FillComputeImageViews(std::span<const u32> indices, std::span<ImageViewId> image_view_ids); - - /// Get the sampler from the graphics descriptor table in the specified index - Sampler* GetGraphicsSampler(u32 index); - - /// Get the sampler from the compute descriptor table in the specified index - Sampler* GetComputeSampler(u32 index); - - /// Refresh the state for graphics image view and sampler descriptors - void SynchronizeGraphicsDescriptors(); - - /// Refresh the state for compute image view and sampler descriptors - void SynchronizeComputeDescriptors(); - - /// Update bound render targets and upload memory if necessary - /// @param is_clear True when the render targets are being used for clears - void UpdateRenderTargets(bool is_clear); - - /// Find a framebuffer with the currently bound render targets - /// UpdateRenderTargets should be called before this - Framebuffer* GetFramebuffer(); - - /// Mark images in a range as modified from the CPU - void WriteMemory(VAddr cpu_addr, size_t size); - - /// Download contents of host images to guest memory in a region - void DownloadMemory(VAddr cpu_addr, size_t size); - - /// Remove images in a region - void UnmapMemory(VAddr cpu_addr, size_t size); - - /// Remove images in a region - void UnmapGPUMemory(GPUVAddr gpu_addr, size_t size); - - /// Blit an image with the given parameters - void BlitImage(const Tegra::Engines::Fermi2D::Surface& dst, - const Tegra::Engines::Fermi2D::Surface& src, - const Tegra::Engines::Fermi2D::Config& copy); - - /// Invalidate the contents of the color buffer index - /// These contents become unspecified, the cache can assume aggressive optimizations. - void InvalidateColorBuffer(size_t index); - - /// Invalidate the contents of the depth buffer - /// These contents become unspecified, the cache can assume aggressive optimizations. - void InvalidateDepthBuffer(); - - /// Try to find a cached image view in the given CPU address - [[nodiscard]] ImageView* TryFindFramebufferImageView(VAddr cpu_addr); - - /// Return true when there are uncommitted images to be downloaded - [[nodiscard]] bool HasUncommittedFlushes() const noexcept; - - /// Return true when the caller should wait for async downloads - [[nodiscard]] bool ShouldWaitAsyncFlushes() const noexcept; - - /// Commit asynchronous downloads - void CommitAsyncFlushes(); - - /// Pop asynchronous downloads - void PopAsyncFlushes(); - - /// Return true when a CPU region is modified from the GPU - [[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size); - - std::mutex mutex; - -private: - /// Iterate over all page indices in a range - template <typename Func> - static void ForEachCPUPage(VAddr addr, size_t size, Func&& func) { - static constexpr bool RETURNS_BOOL = std::is_same_v<std::invoke_result<Func, u64>, bool>; - const u64 page_end = (addr + size - 1) >> PAGE_BITS; - for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) { - if constexpr (RETURNS_BOOL) { - if (func(page)) { - break; - } - } else { - func(page); - } - } - } - - template <typename Func> - static void ForEachGPUPage(GPUVAddr addr, size_t size, Func&& func) { - static constexpr bool RETURNS_BOOL = std::is_same_v<std::invoke_result<Func, u64>, bool>; - const u64 page_end = (addr + size - 1) >> PAGE_BITS; - for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) { - if constexpr (RETURNS_BOOL) { - if (func(page)) { - break; - } - } else { - func(page); - } - } - } - - /// Runs the Garbage Collector. - void RunGarbageCollector(); - - /// Fills image_view_ids in the image views in indices - void FillImageViews(DescriptorTable<TICEntry>& table, - std::span<ImageViewId> cached_image_view_ids, std::span<const u32> indices, - std::span<ImageViewId> image_view_ids); - - /// Find or create an image view in the guest descriptor table - ImageViewId VisitImageView(DescriptorTable<TICEntry>& table, - std::span<ImageViewId> cached_image_view_ids, u32 index); - - /// Find or create a framebuffer with the given render target parameters - FramebufferId GetFramebufferId(const RenderTargets& key); - - /// Refresh the contents (pixel data) of an image - void RefreshContents(Image& image, ImageId image_id); - - /// Upload data from guest to an image - template <typename StagingBuffer> - void UploadImageContents(Image& image, StagingBuffer& staging_buffer); - - /// Find or create an image view from a guest descriptor - [[nodiscard]] ImageViewId FindImageView(const TICEntry& config); - - /// Create a new image view from a guest descriptor - [[nodiscard]] ImageViewId CreateImageView(const TICEntry& config); - - /// Find or create an image from the given parameters - [[nodiscard]] ImageId FindOrInsertImage(const ImageInfo& info, GPUVAddr gpu_addr, - RelaxedOptions options = RelaxedOptions{}); - - /// Find an image from the given parameters - [[nodiscard]] ImageId FindImage(const ImageInfo& info, GPUVAddr gpu_addr, - RelaxedOptions options); - - /// Create an image from the given parameters - [[nodiscard]] ImageId InsertImage(const ImageInfo& info, GPUVAddr gpu_addr, - RelaxedOptions options); - - /// Create a new image and join perfectly matching existing images - /// Remove joined images from the cache - [[nodiscard]] ImageId JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VAddr cpu_addr); - - /// Return a blit image pair from the given guest blit parameters - [[nodiscard]] BlitImages GetBlitImages(const Tegra::Engines::Fermi2D::Surface& dst, - const Tegra::Engines::Fermi2D::Surface& src); - - /// Find or create a sampler from a guest descriptor sampler - [[nodiscard]] SamplerId FindSampler(const TSCEntry& config); - - /// Find or create an image view for the given color buffer index - [[nodiscard]] ImageViewId FindColorBuffer(size_t index, bool is_clear); - - /// Find or create an image view for the depth buffer - [[nodiscard]] ImageViewId FindDepthBuffer(bool is_clear); - - /// Find or create a view for a render target with the given image parameters - [[nodiscard]] ImageViewId FindRenderTargetView(const ImageInfo& info, GPUVAddr gpu_addr, - bool is_clear); - - /// Iterates over all the images in a region calling func - template <typename Func> - void ForEachImageInRegion(VAddr cpu_addr, size_t size, Func&& func); - - template <typename Func> - void ForEachImageInRegionGPU(GPUVAddr gpu_addr, size_t size, Func&& func); - - template <typename Func> - void ForEachSparseImageInRegion(GPUVAddr gpu_addr, size_t size, Func&& func); - - /// Iterates over all the images in a region calling func - template <typename Func> - void ForEachSparseSegment(ImageBase& image, Func&& func); - - /// Find or create an image view in the given image with the passed parameters - [[nodiscard]] ImageViewId FindOrEmplaceImageView(ImageId image_id, const ImageViewInfo& info); - - /// Register image in the page table - void RegisterImage(ImageId image); - - /// Unregister image from the page table - void UnregisterImage(ImageId image); - - /// Track CPU reads and writes for image - void TrackImage(ImageBase& image, ImageId image_id); - - /// Stop tracking CPU reads and writes for image - void UntrackImage(ImageBase& image, ImageId image_id); - - /// Delete image from the cache - void DeleteImage(ImageId image); - - /// Remove image views references from the cache - void RemoveImageViewReferences(std::span<const ImageViewId> removed_views); - - /// Remove framebuffers using the given image views from the cache - void RemoveFramebuffers(std::span<const ImageViewId> removed_views); - - /// Mark an image as modified from the GPU - void MarkModification(ImageBase& image) noexcept; - - /// Synchronize image aliases, copying data if needed - void SynchronizeAliases(ImageId image_id); - - /// Prepare an image to be used - void PrepareImage(ImageId image_id, bool is_modification, bool invalidate); - - /// Prepare an image view to be used - void PrepareImageView(ImageViewId image_view_id, bool is_modification, bool invalidate); - - /// Execute copies from one image to the other, even if they are incompatible - void CopyImage(ImageId dst_id, ImageId src_id, std::span<const ImageCopy> copies); - - /// Bind an image view as render target, downloading resources preemtively if needed - void BindRenderTarget(ImageViewId* old_id, ImageViewId new_id); - - /// Create a render target from a given image and image view parameters - [[nodiscard]] std::pair<FramebufferId, ImageViewId> RenderTargetFromImage( - ImageId, const ImageViewInfo& view_info); - - /// Returns true if the current clear parameters clear the whole image of a given image view - [[nodiscard]] bool IsFullClear(ImageViewId id); - - Runtime& runtime; - VideoCore::RasterizerInterface& rasterizer; - Tegra::Engines::Maxwell3D& maxwell3d; - Tegra::Engines::KeplerCompute& kepler_compute; - Tegra::MemoryManager& gpu_memory; - - DescriptorTable<TICEntry> graphics_image_table{gpu_memory}; - DescriptorTable<TSCEntry> graphics_sampler_table{gpu_memory}; - std::vector<SamplerId> graphics_sampler_ids; - std::vector<ImageViewId> graphics_image_view_ids; - - DescriptorTable<TICEntry> compute_image_table{gpu_memory}; - DescriptorTable<TSCEntry> compute_sampler_table{gpu_memory}; - std::vector<SamplerId> compute_sampler_ids; - std::vector<ImageViewId> compute_image_view_ids; - - RenderTargets render_targets; - - std::unordered_map<TICEntry, ImageViewId> image_views; - std::unordered_map<TSCEntry, SamplerId> samplers; - std::unordered_map<RenderTargets, FramebufferId> framebuffers; - - std::unordered_map<u64, std::vector<ImageMapId>, IdentityHash<u64>> page_table; - std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>> gpu_page_table; - std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>> sparse_page_table; - - std::unordered_map<ImageId, std::vector<ImageViewId>> sparse_views; - - VAddr virtual_invalid_space{}; - - bool has_deleted_images = false; - u64 total_used_memory = 0; - u64 minimum_memory; - u64 expected_memory; - u64 critical_memory; - - SlotVector<Image> slot_images; - SlotVector<ImageMapView> slot_map_views; - SlotVector<ImageView> slot_image_views; - SlotVector<ImageAlloc> slot_image_allocs; - SlotVector<Sampler> slot_samplers; - SlotVector<Framebuffer> slot_framebuffers; - - // TODO: This data structure is not optimal and it should be reworked - std::vector<ImageId> uncommitted_downloads; - std::queue<std::vector<ImageId>> committed_downloads; - - static constexpr size_t TICKS_TO_DESTROY = 6; - DelayedDestructionRing<Image, TICKS_TO_DESTROY> sentenced_images; - DelayedDestructionRing<ImageView, TICKS_TO_DESTROY> sentenced_image_view; - DelayedDestructionRing<Framebuffer, TICKS_TO_DESTROY> sentenced_framebuffers; - - std::unordered_map<GPUVAddr, ImageAllocId> image_allocs_table; - - u64 modification_tick = 0; - u64 frame_tick = 0; - typename SlotVector<Image>::Iterator deletion_iterator; -}; - -template <class P> TextureCache<P>::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface& rasterizer_, Tegra::Engines::Maxwell3D& maxwell3d_, Tegra::Engines::KeplerCompute& kepler_compute_, @@ -821,40 +438,6 @@ void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst, } template <class P> -void TextureCache<P>::InvalidateColorBuffer(size_t index) { - ImageViewId& color_buffer_id = render_targets.color_buffer_ids[index]; - color_buffer_id = FindColorBuffer(index, false); - if (!color_buffer_id) { - LOG_ERROR(HW_GPU, "Invalidating invalid color buffer in index={}", index); - return; - } - // When invalidating a color buffer, the old contents are no longer relevant - ImageView& color_buffer = slot_image_views[color_buffer_id]; - Image& image = slot_images[color_buffer.image_id]; - image.flags &= ~ImageFlagBits::CpuModified; - image.flags &= ~ImageFlagBits::GpuModified; - - runtime.InvalidateColorBuffer(color_buffer, index); -} - -template <class P> -void TextureCache<P>::InvalidateDepthBuffer() { - ImageViewId& depth_buffer_id = render_targets.depth_buffer_id; - depth_buffer_id = FindDepthBuffer(false); - if (!depth_buffer_id) { - LOG_ERROR(HW_GPU, "Invalidating invalid depth buffer"); - return; - } - // When invalidating the depth buffer, the old contents are no longer relevant - ImageBase& image = slot_images[slot_image_views[depth_buffer_id].image_id]; - image.flags &= ~ImageFlagBits::CpuModified; - image.flags &= ~ImageFlagBits::GpuModified; - - ImageView& depth_buffer = slot_image_views[depth_buffer_id]; - runtime.InvalidateDepthBuffer(depth_buffer); -} - -template <class P> typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView(VAddr cpu_addr) { // TODO: Properly implement this const auto it = page_table.find(cpu_addr >> PAGE_BITS); diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h new file mode 100644 index 000000000..e4ae351cb --- /dev/null +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -0,0 +1,385 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <mutex> +#include <span> +#include <type_traits> +#include <unordered_map> +#include <unordered_set> +#include <vector> + +#include "common/common_types.h" +#include "common/literals.h" +#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" +#include "video_core/texture_cache/image_info.h" +#include "video_core/texture_cache/image_view_info.h" +#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 { + +using Tegra::Texture::SwizzleSource; +using Tegra::Texture::TICEntry; +using Tegra::Texture::TSCEntry; +using VideoCore::Surface::GetFormatType; +using VideoCore::Surface::IsCopyCompatible; +using VideoCore::Surface::PixelFormat; +using VideoCore::Surface::PixelFormatFromDepthFormat; +using VideoCore::Surface::PixelFormatFromRenderTargetFormat; +using namespace Common::Literals; + +template <class P> +class TextureCache { + /// Address shift for caching images into a hash table + static constexpr u64 PAGE_BITS = 20; + + /// Enables debugging features to the texture cache + static constexpr bool ENABLE_VALIDATION = P::ENABLE_VALIDATION; + /// Implement blits as copies between framebuffers + static constexpr bool FRAMEBUFFER_BLITS = P::FRAMEBUFFER_BLITS; + /// True when some copies have to be emulated + static constexpr bool HAS_EMULATED_COPIES = P::HAS_EMULATED_COPIES; + /// True when the API can provide info about the memory of the device. + static constexpr bool HAS_DEVICE_MEMORY_INFO = P::HAS_DEVICE_MEMORY_INFO; + + /// Image view ID for null descriptors + static constexpr ImageViewId NULL_IMAGE_VIEW_ID{0}; + /// Sampler ID for bugged sampler ids + static constexpr SamplerId NULL_SAMPLER_ID{0}; + + static constexpr u64 DEFAULT_EXPECTED_MEMORY = 1_GiB; + static constexpr u64 DEFAULT_CRITICAL_MEMORY = 2_GiB; + + using Runtime = typename P::Runtime; + using Image = typename P::Image; + using ImageAlloc = typename P::ImageAlloc; + using ImageView = typename P::ImageView; + using Sampler = typename P::Sampler; + using Framebuffer = typename P::Framebuffer; + + struct BlitImages { + ImageId dst_id; + ImageId src_id; + PixelFormat dst_format; + PixelFormat src_format; + }; + + template <typename T> + struct IdentityHash { + [[nodiscard]] size_t operator()(T value) const noexcept { + return static_cast<size_t>(value); + } + }; + +public: + explicit TextureCache(Runtime&, VideoCore::RasterizerInterface&, Tegra::Engines::Maxwell3D&, + Tegra::Engines::KeplerCompute&, Tegra::MemoryManager&); + + /// Notify the cache that a new frame has been queued + void TickFrame(); + + /// Return a constant reference to the given image view id + [[nodiscard]] const ImageView& GetImageView(ImageViewId id) const noexcept; + + /// Return a reference to the given image view id + [[nodiscard]] ImageView& GetImageView(ImageViewId id) noexcept; + + /// Mark an image as modified from the GPU + void MarkModification(ImageId id) noexcept; + + /// Fill image_view_ids with the graphics images in indices + void FillGraphicsImageViews(std::span<const u32> indices, + std::span<ImageViewId> image_view_ids); + + /// Fill image_view_ids with the compute images in indices + void FillComputeImageViews(std::span<const u32> indices, std::span<ImageViewId> image_view_ids); + + /// Get the sampler from the graphics descriptor table in the specified index + Sampler* GetGraphicsSampler(u32 index); + + /// Get the sampler from the compute descriptor table in the specified index + Sampler* GetComputeSampler(u32 index); + + /// Refresh the state for graphics image view and sampler descriptors + void SynchronizeGraphicsDescriptors(); + + /// Refresh the state for compute image view and sampler descriptors + void SynchronizeComputeDescriptors(); + + /// Update bound render targets and upload memory if necessary + /// @param is_clear True when the render targets are being used for clears + void UpdateRenderTargets(bool is_clear); + + /// Find a framebuffer with the currently bound render targets + /// UpdateRenderTargets should be called before this + Framebuffer* GetFramebuffer(); + + /// Mark images in a range as modified from the CPU + void WriteMemory(VAddr cpu_addr, size_t size); + + /// Download contents of host images to guest memory in a region + void DownloadMemory(VAddr cpu_addr, size_t size); + + /// Remove images in a region + void UnmapMemory(VAddr cpu_addr, size_t size); + + /// Remove images in a region + void UnmapGPUMemory(GPUVAddr gpu_addr, size_t size); + + /// Blit an image with the given parameters + void BlitImage(const Tegra::Engines::Fermi2D::Surface& dst, + const Tegra::Engines::Fermi2D::Surface& src, + const Tegra::Engines::Fermi2D::Config& copy); + + /// Try to find a cached image view in the given CPU address + [[nodiscard]] ImageView* TryFindFramebufferImageView(VAddr cpu_addr); + + /// Return true when there are uncommitted images to be downloaded + [[nodiscard]] bool HasUncommittedFlushes() const noexcept; + + /// Return true when the caller should wait for async downloads + [[nodiscard]] bool ShouldWaitAsyncFlushes() const noexcept; + + /// Commit asynchronous downloads + void CommitAsyncFlushes(); + + /// Pop asynchronous downloads + void PopAsyncFlushes(); + + /// Return true when a CPU region is modified from the GPU + [[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size); + + std::mutex mutex; + +private: + /// Iterate over all page indices in a range + template <typename Func> + static void ForEachCPUPage(VAddr addr, size_t size, Func&& func) { + static constexpr bool RETURNS_BOOL = std::is_same_v<std::invoke_result<Func, u64>, bool>; + const u64 page_end = (addr + size - 1) >> PAGE_BITS; + for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) { + if constexpr (RETURNS_BOOL) { + if (func(page)) { + break; + } + } else { + func(page); + } + } + } + + template <typename Func> + static void ForEachGPUPage(GPUVAddr addr, size_t size, Func&& func) { + static constexpr bool RETURNS_BOOL = std::is_same_v<std::invoke_result<Func, u64>, bool>; + const u64 page_end = (addr + size - 1) >> PAGE_BITS; + for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) { + if constexpr (RETURNS_BOOL) { + if (func(page)) { + break; + } + } else { + func(page); + } + } + } + + /// Runs the Garbage Collector. + void RunGarbageCollector(); + + /// Fills image_view_ids in the image views in indices + void FillImageViews(DescriptorTable<TICEntry>& table, + std::span<ImageViewId> cached_image_view_ids, std::span<const u32> indices, + std::span<ImageViewId> image_view_ids); + + /// Find or create an image view in the guest descriptor table + ImageViewId VisitImageView(DescriptorTable<TICEntry>& table, + std::span<ImageViewId> cached_image_view_ids, u32 index); + + /// Find or create a framebuffer with the given render target parameters + FramebufferId GetFramebufferId(const RenderTargets& key); + + /// Refresh the contents (pixel data) of an image + void RefreshContents(Image& image, ImageId image_id); + + /// Upload data from guest to an image + template <typename StagingBuffer> + void UploadImageContents(Image& image, StagingBuffer& staging_buffer); + + /// Find or create an image view from a guest descriptor + [[nodiscard]] ImageViewId FindImageView(const TICEntry& config); + + /// Create a new image view from a guest descriptor + [[nodiscard]] ImageViewId CreateImageView(const TICEntry& config); + + /// Find or create an image from the given parameters + [[nodiscard]] ImageId FindOrInsertImage(const ImageInfo& info, GPUVAddr gpu_addr, + RelaxedOptions options = RelaxedOptions{}); + + /// Find an image from the given parameters + [[nodiscard]] ImageId FindImage(const ImageInfo& info, GPUVAddr gpu_addr, + RelaxedOptions options); + + /// Create an image from the given parameters + [[nodiscard]] ImageId InsertImage(const ImageInfo& info, GPUVAddr gpu_addr, + RelaxedOptions options); + + /// Create a new image and join perfectly matching existing images + /// Remove joined images from the cache + [[nodiscard]] ImageId JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VAddr cpu_addr); + + /// Return a blit image pair from the given guest blit parameters + [[nodiscard]] BlitImages GetBlitImages(const Tegra::Engines::Fermi2D::Surface& dst, + const Tegra::Engines::Fermi2D::Surface& src); + + /// Find or create a sampler from a guest descriptor sampler + [[nodiscard]] SamplerId FindSampler(const TSCEntry& config); + + /// Find or create an image view for the given color buffer index + [[nodiscard]] ImageViewId FindColorBuffer(size_t index, bool is_clear); + + /// Find or create an image view for the depth buffer + [[nodiscard]] ImageViewId FindDepthBuffer(bool is_clear); + + /// Find or create a view for a render target with the given image parameters + [[nodiscard]] ImageViewId FindRenderTargetView(const ImageInfo& info, GPUVAddr gpu_addr, + bool is_clear); + + /// Iterates over all the images in a region calling func + template <typename Func> + void ForEachImageInRegion(VAddr cpu_addr, size_t size, Func&& func); + + template <typename Func> + void ForEachImageInRegionGPU(GPUVAddr gpu_addr, size_t size, Func&& func); + + template <typename Func> + void ForEachSparseImageInRegion(GPUVAddr gpu_addr, size_t size, Func&& func); + + /// Iterates over all the images in a region calling func + template <typename Func> + void ForEachSparseSegment(ImageBase& image, Func&& func); + + /// Find or create an image view in the given image with the passed parameters + [[nodiscard]] ImageViewId FindOrEmplaceImageView(ImageId image_id, const ImageViewInfo& info); + + /// Register image in the page table + void RegisterImage(ImageId image); + + /// Unregister image from the page table + void UnregisterImage(ImageId image); + + /// Track CPU reads and writes for image + void TrackImage(ImageBase& image, ImageId image_id); + + /// Stop tracking CPU reads and writes for image + void UntrackImage(ImageBase& image, ImageId image_id); + + /// Delete image from the cache + void DeleteImage(ImageId image); + + /// Remove image views references from the cache + void RemoveImageViewReferences(std::span<const ImageViewId> removed_views); + + /// Remove framebuffers using the given image views from the cache + void RemoveFramebuffers(std::span<const ImageViewId> removed_views); + + /// Mark an image as modified from the GPU + void MarkModification(ImageBase& image) noexcept; + + /// Synchronize image aliases, copying data if needed + void SynchronizeAliases(ImageId image_id); + + /// Prepare an image to be used + void PrepareImage(ImageId image_id, bool is_modification, bool invalidate); + + /// Prepare an image view to be used + void PrepareImageView(ImageViewId image_view_id, bool is_modification, bool invalidate); + + /// Execute copies from one image to the other, even if they are incompatible + void CopyImage(ImageId dst_id, ImageId src_id, std::span<const ImageCopy> copies); + + /// Bind an image view as render target, downloading resources preemtively if needed + void BindRenderTarget(ImageViewId* old_id, ImageViewId new_id); + + /// Create a render target from a given image and image view parameters + [[nodiscard]] std::pair<FramebufferId, ImageViewId> RenderTargetFromImage( + ImageId, const ImageViewInfo& view_info); + + /// Returns true if the current clear parameters clear the whole image of a given image view + [[nodiscard]] bool IsFullClear(ImageViewId id); + + Runtime& runtime; + VideoCore::RasterizerInterface& rasterizer; + Tegra::Engines::Maxwell3D& maxwell3d; + Tegra::Engines::KeplerCompute& kepler_compute; + Tegra::MemoryManager& gpu_memory; + + DescriptorTable<TICEntry> graphics_image_table{gpu_memory}; + DescriptorTable<TSCEntry> graphics_sampler_table{gpu_memory}; + std::vector<SamplerId> graphics_sampler_ids; + std::vector<ImageViewId> graphics_image_view_ids; + + DescriptorTable<TICEntry> compute_image_table{gpu_memory}; + DescriptorTable<TSCEntry> compute_sampler_table{gpu_memory}; + std::vector<SamplerId> compute_sampler_ids; + std::vector<ImageViewId> compute_image_view_ids; + + RenderTargets render_targets; + + std::unordered_map<TICEntry, ImageViewId> image_views; + std::unordered_map<TSCEntry, SamplerId> samplers; + std::unordered_map<RenderTargets, FramebufferId> framebuffers; + + std::unordered_map<u64, std::vector<ImageMapId>, IdentityHash<u64>> page_table; + std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>> gpu_page_table; + std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>> sparse_page_table; + + std::unordered_map<ImageId, std::vector<ImageViewId>> sparse_views; + + VAddr virtual_invalid_space{}; + + bool has_deleted_images = false; + u64 total_used_memory = 0; + u64 minimum_memory; + u64 expected_memory; + u64 critical_memory; + + SlotVector<Image> slot_images; + SlotVector<ImageMapView> slot_map_views; + SlotVector<ImageView> slot_image_views; + SlotVector<ImageAlloc> slot_image_allocs; + SlotVector<Sampler> slot_samplers; + SlotVector<Framebuffer> slot_framebuffers; + + // TODO: This data structure is not optimal and it should be reworked + std::vector<ImageId> uncommitted_downloads; + std::queue<std::vector<ImageId>> committed_downloads; + + static constexpr size_t TICKS_TO_DESTROY = 6; + DelayedDestructionRing<Image, TICKS_TO_DESTROY> sentenced_images; + DelayedDestructionRing<ImageView, TICKS_TO_DESTROY> sentenced_image_view; + DelayedDestructionRing<Framebuffer, TICKS_TO_DESTROY> sentenced_framebuffers; + + std::unordered_map<GPUVAddr, ImageAllocId> image_allocs_table; + + u64 modification_tick = 0; + u64 frame_tick = 0; + typename SlotVector<Image>::Iterator deletion_iterator; +}; + +} // namespace VideoCommon diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index c32ae956a..c010b9353 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp @@ -84,56 +84,31 @@ template <bool TO_LINEAR> void Swizzle(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth, u32 stride_alignment) { switch (bytes_per_pixel) { - case 1: - return SwizzleImpl<TO_LINEAR, 1>(output, input, width, height, depth, block_height, +#define BPP_CASE(x) \ + case x: \ + return SwizzleImpl<TO_LINEAR, x>(output, input, width, height, depth, block_height, \ block_depth, stride_alignment); - case 2: - return SwizzleImpl<TO_LINEAR, 2>(output, input, width, height, depth, block_height, - block_depth, stride_alignment); - case 3: - return SwizzleImpl<TO_LINEAR, 3>(output, input, width, height, depth, block_height, - block_depth, stride_alignment); - case 4: - return SwizzleImpl<TO_LINEAR, 4>(output, input, width, height, depth, block_height, - block_depth, stride_alignment); - case 6: - return SwizzleImpl<TO_LINEAR, 6>(output, input, width, height, depth, block_height, - block_depth, stride_alignment); - case 8: - return SwizzleImpl<TO_LINEAR, 8>(output, input, width, height, depth, block_height, - block_depth, stride_alignment); - case 12: - return SwizzleImpl<TO_LINEAR, 12>(output, input, width, height, depth, block_height, - block_depth, stride_alignment); - case 16: - return SwizzleImpl<TO_LINEAR, 16>(output, input, width, height, depth, block_height, - block_depth, stride_alignment); + BPP_CASE(1) + BPP_CASE(2) + BPP_CASE(3) + BPP_CASE(4) + BPP_CASE(6) + BPP_CASE(8) + BPP_CASE(12) + BPP_CASE(16) +#undef BPP_CASE default: UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel); } } -} // Anonymous namespace - -void UnswizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, - u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth, - u32 stride_alignment) { - Swizzle<false>(output, input, bytes_per_pixel, width, height, depth, block_height, block_depth, - stride_alignment); -} - -void SwizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, u32 width, - u32 height, u32 depth, u32 block_height, u32 block_depth, - u32 stride_alignment) { - Swizzle<true>(output, input, bytes_per_pixel, width, height, depth, block_height, block_depth, - stride_alignment); -} +template <u32 BYTES_PER_PIXEL> void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width, - u32 bytes_per_pixel, u8* swizzled_data, const u8* unswizzled_data, - u32 block_height_bit, u32 offset_x, u32 offset_y) { + u8* swizzled_data, const u8* unswizzled_data, u32 block_height_bit, + u32 offset_x, u32 offset_y) { const u32 block_height = 1U << block_height_bit; const u32 image_width_in_gobs = - (swizzled_width * bytes_per_pixel + (GOB_SIZE_X - 1)) / GOB_SIZE_X; + (swizzled_width * BYTES_PER_PIXEL + (GOB_SIZE_X - 1)) / GOB_SIZE_X; for (u32 line = 0; line < subrect_height; ++line) { const u32 dst_y = line + offset_y; const u32 gob_address_y = @@ -143,20 +118,21 @@ void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 for (u32 x = 0; x < subrect_width; ++x) { const u32 dst_x = x + offset_x; const u32 gob_address = - gob_address_y + (dst_x * bytes_per_pixel / GOB_SIZE_X) * GOB_SIZE * block_height; - const u32 swizzled_offset = gob_address + table[(dst_x * bytes_per_pixel) % GOB_SIZE_X]; - const u32 unswizzled_offset = line * source_pitch + x * bytes_per_pixel; + gob_address_y + (dst_x * BYTES_PER_PIXEL / GOB_SIZE_X) * GOB_SIZE * block_height; + const u32 swizzled_offset = gob_address + table[(dst_x * BYTES_PER_PIXEL) % GOB_SIZE_X]; + const u32 unswizzled_offset = line * source_pitch + x * BYTES_PER_PIXEL; const u8* const source_line = unswizzled_data + unswizzled_offset; u8* const dest_addr = swizzled_data + swizzled_offset; - std::memcpy(dest_addr, source_line, bytes_per_pixel); + std::memcpy(dest_addr, source_line, BYTES_PER_PIXEL); } } } -void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 bytes_per_pixel, - u32 block_height, u32 origin_x, u32 origin_y, u8* output, const u8* input) { - const u32 stride = width * bytes_per_pixel; +template <u32 BYTES_PER_PIXEL> +void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 block_height, + u32 origin_x, u32 origin_y, u8* output, const u8* input) { + const u32 stride = width * BYTES_PER_PIXEL; const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) / GOB_SIZE_X; const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height); @@ -171,24 +147,25 @@ void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, const u32 src_offset_y = (block_y >> block_height) * block_size + ((block_y & block_height_mask) << GOB_SIZE_SHIFT); for (u32 column = 0; column < line_length_in; ++column) { - const u32 src_x = (column + origin_x) * bytes_per_pixel; + const u32 src_x = (column + origin_x) * BYTES_PER_PIXEL; const u32 src_offset_x = (src_x >> GOB_SIZE_X_SHIFT) << x_shift; const u32 swizzled_offset = src_offset_y + src_offset_x + table[src_x % GOB_SIZE_X]; - const u32 unswizzled_offset = line * pitch + column * bytes_per_pixel; + const u32 unswizzled_offset = line * pitch + column * BYTES_PER_PIXEL; - std::memcpy(output + unswizzled_offset, input + swizzled_offset, bytes_per_pixel); + std::memcpy(output + unswizzled_offset, input + swizzled_offset, BYTES_PER_PIXEL); } } } +template <u32 BYTES_PER_PIXEL> void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 height, - u32 bytes_per_pixel, u32 block_height, u32 block_depth, u32 origin_x, - u32 origin_y, u8* output, const u8* input) { + u32 block_height, u32 block_depth, u32 origin_x, u32 origin_y, u8* output, + const u8* input) { UNIMPLEMENTED_IF(origin_x > 0); UNIMPLEMENTED_IF(origin_y > 0); - const u32 stride = width * bytes_per_pixel; + const u32 stride = width * BYTES_PER_PIXEL; const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) / GOB_SIZE_X; const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth); @@ -203,11 +180,93 @@ void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 widt for (u32 x = 0; x < line_length_in; ++x) { const u32 dst_offset = ((x / GOB_SIZE_X) << x_shift) + dst_offset_y + table[x % GOB_SIZE_X]; - const u32 src_offset = x * bytes_per_pixel + line * pitch; - std::memcpy(output + dst_offset, input + src_offset, bytes_per_pixel); + const u32 src_offset = x * BYTES_PER_PIXEL + line * pitch; + std::memcpy(output + dst_offset, input + src_offset, BYTES_PER_PIXEL); } } } +} // Anonymous namespace + +void UnswizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, + u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth, + u32 stride_alignment) { + Swizzle<false>(output, input, bytes_per_pixel, width, height, depth, block_height, block_depth, + stride_alignment); +} + +void SwizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, u32 width, + u32 height, u32 depth, u32 block_height, u32 block_depth, + u32 stride_alignment) { + Swizzle<true>(output, input, bytes_per_pixel, width, height, depth, block_height, block_depth, + stride_alignment); +} + +void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width, + u32 bytes_per_pixel, u8* swizzled_data, const u8* unswizzled_data, + u32 block_height_bit, u32 offset_x, u32 offset_y) { + switch (bytes_per_pixel) { +#define BPP_CASE(x) \ + case x: \ + return SwizzleSubrect<x>(subrect_width, subrect_height, source_pitch, swizzled_width, \ + swizzled_data, unswizzled_data, block_height_bit, offset_x, \ + offset_y); + BPP_CASE(1) + BPP_CASE(2) + BPP_CASE(3) + BPP_CASE(4) + BPP_CASE(6) + BPP_CASE(8) + BPP_CASE(12) + BPP_CASE(16) +#undef BPP_CASE + default: + UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel); + } +} + +void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 bytes_per_pixel, + u32 block_height, u32 origin_x, u32 origin_y, u8* output, const u8* input) { + switch (bytes_per_pixel) { +#define BPP_CASE(x) \ + case x: \ + return UnswizzleSubrect<x>(line_length_in, line_count, pitch, width, block_height, \ + origin_x, origin_y, output, input); + BPP_CASE(1) + BPP_CASE(2) + BPP_CASE(3) + BPP_CASE(4) + BPP_CASE(6) + BPP_CASE(8) + BPP_CASE(12) + BPP_CASE(16) +#undef BPP_CASE + default: + UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel); + } +} + +void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 height, + u32 bytes_per_pixel, u32 block_height, u32 block_depth, u32 origin_x, + u32 origin_y, u8* output, const u8* input) { + switch (bytes_per_pixel) { +#define BPP_CASE(x) \ + case x: \ + return SwizzleSliceToVoxel<x>(line_length_in, line_count, pitch, width, height, \ + block_height, block_depth, origin_x, origin_y, output, \ + input); + BPP_CASE(1) + BPP_CASE(2) + BPP_CASE(3) + BPP_CASE(4) + BPP_CASE(6) + BPP_CASE(8) + BPP_CASE(12) + BPP_CASE(16) +#undef BPP_CASE + default: + UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel); + } +} void SwizzleKepler(const u32 width, const u32 height, const u32 dst_x, const u32 dst_y, const u32 block_height_bit, const std::size_t copy_size, const u8* source_data, @@ -228,7 +287,7 @@ void SwizzleKepler(const u32 width, const u32 height, const u32 dst_x, const u32 u8* dest_addr = swizzle_data + swizzled_offset; count++; - std::memcpy(dest_addr, source_line, 1); + *dest_addr = *source_line; } } } diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp index aa173d19e..300a61205 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp @@ -228,7 +228,9 @@ void MemoryCommit::Release() { MemoryAllocator::MemoryAllocator(const Device& device_, bool export_allocations_) : device{device_}, properties{device_.GetPhysical().GetMemoryProperties()}, - export_allocations{export_allocations_} {} + export_allocations{export_allocations_}, + buffer_image_granularity{ + device_.GetPhysical().GetProperties().limits.bufferImageGranularity} {} MemoryAllocator::~MemoryAllocator() = default; @@ -258,7 +260,9 @@ MemoryCommit MemoryAllocator::Commit(const vk::Buffer& buffer, MemoryUsage usage } MemoryCommit MemoryAllocator::Commit(const vk::Image& image, MemoryUsage usage) { - auto commit = Commit(device.GetLogical().GetImageMemoryRequirements(*image), usage); + VkMemoryRequirements requirements = device.GetLogical().GetImageMemoryRequirements(*image); + requirements.size = Common::AlignUp(requirements.size, buffer_image_granularity); + auto commit = Commit(requirements, usage); image.BindMemory(commit.Memory(), commit.Offset()); return commit; } diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.h b/src/video_core/vulkan_common/vulkan_memory_allocator.h index b61e931e0..86e8ed119 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.h +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.h @@ -123,6 +123,8 @@ private: const VkPhysicalDeviceMemoryProperties properties; ///< Physical device properties. const bool export_allocations; ///< True when memory allocations have to be exported. std::vector<std::unique_ptr<MemoryAllocation>> allocations; ///< Current allocations. + VkDeviceSize buffer_image_granularity; // The granularity for adjacent offsets between buffers + // and optimal images }; /// Returns true when a memory usage is guaranteed to be host visible. diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index cb4bdcc7e..cf68a95b5 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -102,9 +102,9 @@ add_executable(yuzu configuration/configure_profile_manager.cpp configuration/configure_profile_manager.h configuration/configure_profile_manager.ui - configuration/configure_service.cpp - configuration/configure_service.h - configuration/configure_service.ui + configuration/configure_network.cpp + configuration/configure_network.h + configuration/configure_network.ui configuration/configure_system.cpp configuration/configure_system.h configuration/configure_system.ui diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 380379eb4..377795326 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -692,6 +692,7 @@ 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(); } @@ -1144,7 +1145,7 @@ void Config::SaveValues() { SaveDataStorageValues(); SaveDebuggingValues(); SaveDisabledAddOnValues(); - SaveServiceValues(); + SaveNetworkValues(); SaveUIValues(); SaveWebServiceValues(); SaveMiscellaneousValues(); @@ -1238,11 +1239,12 @@ void Config::SaveDebuggingValues() { qt_config->endGroup(); } -void Config::SaveServiceValues() { +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/config.h b/src/yuzu/configuration/config.h index c1d7feb9f..9555f4498 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -88,7 +88,7 @@ private: void SaveCoreValues(); void SaveDataStorageValues(); void SaveDebuggingValues(); - void SaveServiceValues(); + void SaveNetworkValues(); void SaveDisabledAddOnValues(); void SaveMiscellaneousValues(); void SavePathValues(); diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui index fca9aed5f..6258dcf20 100644 --- a/src/yuzu/configuration/configure.ui +++ b/src/yuzu/configuration/configure.ui @@ -147,12 +147,12 @@ <string>Web</string> </attribute> </widget> - <widget class="ConfigureService" name="serviceTab"> + <widget class="ConfigureNetwork" name="networkTab"> <property name="accessibleName"> - <string>Services</string> + <string>Network</string> </property> <attribute name="title"> - <string>Services</string> + <string>Network</string> </attribute> </widget> </widget> @@ -242,9 +242,9 @@ <container>1</container> </customwidget> <customwidget> - <class>ConfigureService</class> + <class>ConfigureNetwork</class> <extends>QWidget</extends> - <header>configuration/configure_service.h</header> + <header>configuration/configure_network.h</header> <container>1</container> </customwidget> <customwidget> diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index bc009b6b3..fe4186157 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp @@ -67,7 +67,7 @@ void ConfigureDialog::ApplyConfiguration() { ui->audioTab->ApplyConfiguration(); ui->debugTab->ApplyConfiguration(); ui->webTab->ApplyConfiguration(); - ui->serviceTab->ApplyConfiguration(); + ui->networkTab->ApplyConfiguration(); Core::System::GetInstance().ApplySettings(); Settings::LogSettings(); } @@ -103,7 +103,7 @@ Q_DECLARE_METATYPE(QList<QWidget*>); void ConfigureDialog::PopulateSelectionList() { const std::array<std::pair<QString, QList<QWidget*>>, 6> items{ {{tr("General"), {ui->generalTab, ui->hotkeysTab, ui->uiTab, ui->webTab, ui->debugTab}}, - {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab}}, + {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->networkTab, ui->filesystemTab}}, {tr("CPU"), {ui->cpuTab}}, {tr("Graphics"), {ui->graphicsTab, ui->graphicsAdvancedTab}}, {tr("Audio"), {ui->audioTab}}, diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui index 8ce97edec..69b6c2d66 100644 --- a/src/yuzu/configuration/configure_general.ui +++ b/src/yuzu/configuration/configure_general.ui @@ -25,6 +25,36 @@ <item> <layout class="QVBoxLayout" name="GeneralVerticalLayout"> <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QLabel" name="fps_cap_label"> + <property name="text"> + <string>Framerate Cap</string> + </property> + <property name="toolTip"> + <string>Requires the use of the FPS Limiter Toggle hotkey to take effect.</string> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="fps_cap"> + <property name="suffix"> + <string>x</string> + </property> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>1000</number> + </property> + <property name="value"> + <number>500</number> + </property> + </widget> + </item> + </layout> + </item> + <item> <layout class="QHBoxLayout" name="horizontalLayout_2"> <item> <widget class="QCheckBox" name="toggle_speed_limit"> @@ -52,36 +82,6 @@ </layout> </item> <item> - <layout class="QHBoxLayout" name="horizontalLayout_2"> - <item> - <widget class="QLabel" name="fps_cap_label"> - <property name="text"> - <string>Framerate Cap</string> - </property> - <property name="toolTip"> - <string>Requires the use of the FPS Limiter Toggle hotkey to take effect.</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="fps_cap"> - <property name="suffix"> - <string>x</string> - </property> - <property name="minimum"> - <number>1</number> - </property> - <property name="maximum"> - <number>1000</number> - </property> - <property name="value"> - <number>500</number> - </property> - </widget> - </item> - </layout> - </item> - <item> <widget class="QCheckBox" name="use_multi_core"> <property name="text"> <string>Multicore CPU Emulation</string> diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui index 379dc5d2e..4fe6b86ae 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.ui +++ b/src/yuzu/configuration/configure_graphics_advanced.ui @@ -82,14 +82,17 @@ <string>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</string> </property> <property name="text"> - <string>Use asynchronous shader building</string> + <string>Use asynchronous shader building (hack)</string> </property> </widget> </item> <item> <widget class="QCheckBox" name="use_fast_gpu_time"> + <property name="toolTip"> + <string>Enables Fast GPU Time. This option will force most games to run at their highest native resolution.</string> + </property> <property name="text"> - <string>Use Fast GPU Time</string> + <string>Use Fast GPU Time (hack)</string> </property> </widget> </item> diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 6b9bd05f1..7527c068b 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -309,11 +309,14 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i buttons_param[button_id].Clear(); button_map[button_id]->setText(tr("[not set]")); }); - context_menu.addAction(tr("Toggle button"), [&] { - const bool toggle_value = !buttons_param[button_id].Get("toggle", false); - buttons_param[button_id].Set("toggle", toggle_value); - button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); - }); + if (buttons_param[button_id].Has("toggle")) { + context_menu.addAction(tr("Toggle button"), [&] { + const bool toggle_value = + !buttons_param[button_id].Get("toggle", false); + buttons_param[button_id].Set("toggle", toggle_value); + button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); + }); + } if (buttons_param[button_id].Has("threshold")) { context_menu.addAction(tr("Set threshold"), [&] { const int button_threshold = static_cast<int>( diff --git a/src/yuzu/configuration/configure_service.cpp b/src/yuzu/configuration/configure_network.cpp index 4aa424803..ae22f1018 100644 --- a/src/yuzu/configuration/configure_service.cpp +++ b/src/yuzu/configuration/configure_network.cpp @@ -5,9 +5,11 @@ #include <QGraphicsItem> #include <QtConcurrent/QtConcurrent> #include "common/settings.h" +#include "core/core.h" #include "core/hle/service/bcat/backend/boxcat.h" -#include "ui_configure_service.h" -#include "yuzu/configuration/configure_service.h" +#include "core/network/network_interface.h" +#include "ui_configure_network.h" +#include "yuzu/configuration/configure_network.h" #ifdef YUZU_ENABLE_BOXCAT namespace { @@ -35,8 +37,8 @@ QString FormatEventStatusString(const Service::BCAT::EventStatus& status) { } // Anonymous namespace #endif -ConfigureService::ConfigureService(QWidget* parent) - : QWidget(parent), ui(std::make_unique<Ui::ConfigureService>()) { +ConfigureNetwork::ConfigureNetwork(QWidget* parent) + : QWidget(parent), ui(std::make_unique<Ui::ConfigureNetwork>()) { ui->setupUi(this); ui->bcat_source->addItem(QStringLiteral("None")); @@ -47,29 +49,42 @@ ConfigureService::ConfigureService(QWidget* parent) 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, - &ConfigureService::OnBCATImplChanged); + &ConfigureNetwork::OnBCATImplChanged); this->SetConfiguration(); } -ConfigureService::~ConfigureService() = default; +ConfigureNetwork::~ConfigureNetwork() = default; -void ConfigureService::ApplyConfiguration() { +void ConfigureNetwork::ApplyConfiguration() { Settings::values.bcat_backend = ui->bcat_source->currentText().toLower().toStdString(); + Settings::values.network_interface = ui->network_interface->currentText().toStdString(); } -void ConfigureService::RetranslateUi() { +void ConfigureNetwork::RetranslateUi() { ui->retranslateUi(this); } -void ConfigureService::SetConfiguration() { +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> ConfigureService::BCATDownloadEvents() { +std::pair<QString, QString> ConfigureNetwork::BCATDownloadEvents() { #ifdef YUZU_ENABLE_BOXCAT std::optional<std::string> global; std::map<std::string, Service::BCAT::EventStatus> map; @@ -114,7 +129,7 @@ std::pair<QString, QString> ConfigureService::BCATDownloadEvents() { #endif } -void ConfigureService::OnBCATImplChanged() { +void ConfigureNetwork::OnBCATImplChanged() { #ifdef YUZU_ENABLE_BOXCAT const auto boxcat = ui->bcat_source->currentText() == QStringLiteral("Boxcat"); ui->bcat_empty_header->setHidden(!boxcat); @@ -133,7 +148,7 @@ void ConfigureService::OnBCATImplChanged() { #endif } -void ConfigureService::OnUpdateBCATEmptyLabel(std::pair<QString, QString> string) { +void ConfigureNetwork::OnUpdateBCATEmptyLabel(std::pair<QString, QString> string) { #ifdef YUZU_ENABLE_BOXCAT const auto boxcat = ui->bcat_source->currentText() == QStringLiteral("Boxcat"); if (boxcat) { diff --git a/src/yuzu/configuration/configure_service.h b/src/yuzu/configuration/configure_network.h index f5c1b703a..442b68e6b 100644 --- a/src/yuzu/configuration/configure_service.h +++ b/src/yuzu/configuration/configure_network.h @@ -9,15 +9,15 @@ #include <QWidget> namespace Ui { -class ConfigureService; +class ConfigureNetwork; } -class ConfigureService : public QWidget { +class ConfigureNetwork : public QWidget { Q_OBJECT public: - explicit ConfigureService(QWidget* parent = nullptr); - ~ConfigureService() override; + explicit ConfigureNetwork(QWidget* parent = nullptr); + ~ConfigureNetwork() override; void ApplyConfiguration(); void RetranslateUi(); @@ -29,6 +29,6 @@ private: void OnBCATImplChanged(); void OnUpdateBCATEmptyLabel(std::pair<QString, QString> string); - std::unique_ptr<Ui::ConfigureService> ui; + std::unique_ptr<Ui::ConfigureNetwork> ui; QFutureWatcher<std::pair<QString, QString>> watcher{this}; }; diff --git a/src/yuzu/configuration/configure_service.ui b/src/yuzu/configuration/configure_network.ui index 9668dd557..5f9b7e97b 100644 --- a/src/yuzu/configuration/configure_service.ui +++ b/src/yuzu/configuration/configure_network.ui @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> - <class>ConfigureService</class> - <widget class="QWidget" name="ConfigureService"> + <class>ConfigureNetwork</class> + <widget class="QWidget" name="ConfigureNetwork"> <property name="geometry"> <rect> <x>0</x> @@ -17,21 +17,37 @@ <item> <layout class="QVBoxLayout" name="verticalLayout_3"> <item> + <widget class="QGroupBox" name="groupBox_2"> + <property name="title"> + <string>General</string> + </property> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="1" column="1"> + <widget class="QComboBox" name="network_interface"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Network Interface</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> <widget class="QGroupBox" name="groupBox"> <property name="title"> <string>BCAT</string> </property> <layout class="QGridLayout" name="gridLayout"> - <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> + <item row="3" column="0"> + <widget class="QLabel" name="bcat_empty_header"> <property name="text"> - <string>BCAT is Nintendo's way of sending data to games to engage its community and unlock additional content.</string> + <string/> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> </property> <property name="wordWrap"> <bool>true</bool> @@ -51,11 +67,8 @@ </property> </widget> </item> - <item row="3" column="1" colspan="2"> - <widget class="QLabel" name="bcat_empty_label"> - <property name="enabled"> - <bool>true</bool> - </property> + <item row="1" column="1" colspan="2"> + <widget class="QLabel" name="label_2"> <property name="maximumSize"> <size> <width>260</width> @@ -63,10 +76,7 @@ </size> </property> <property name="text"> - <string/> - </property> - <property name="alignment"> - <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + <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> @@ -86,8 +96,17 @@ <item row="0" column="1" colspan="2"> <widget class="QComboBox" name="bcat_source"/> </item> - <item row="3" column="0"> - <widget class="QLabel" name="bcat_empty_header"> + <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> diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 9544f0fb0..5940e0cfd 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2814,8 +2814,6 @@ void GMainWindow::OnToggleFilterBar() { } void GMainWindow::OnCaptureScreenshot() { - OnPauseGame(); - const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); const auto screenshot_path = QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::ScreenshotsDir)); @@ -2827,23 +2825,22 @@ void GMainWindow::OnCaptureScreenshot() { .arg(date); if (!Common::FS::CreateDir(screenshot_path.toStdString())) { - OnStartGame(); return; } #ifdef _WIN32 if (UISettings::values.enable_screenshot_save_as) { + OnPauseGame(); filename = QFileDialog::getSaveFileName(this, tr("Capture Screenshot"), filename, tr("PNG Image (*.png)")); + OnStartGame(); if (filename.isEmpty()) { - OnStartGame(); return; } } #endif render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor.GetValue(), filename); - OnStartGame(); } // TODO: Written 2020-10-01: Remove per-game config migration code when it is irrelevant diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt index e55a19649..74fc24972 100644 --- a/src/yuzu_cmd/CMakeLists.txt +++ b/src/yuzu_cmd/CMakeLists.txt @@ -1,5 +1,6 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules) +# Credits to Samantas5855 and others for this function. function(create_resource file output filename) # Read hex data from file file(READ ${file} filedata HEX) diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index c80f7791c..87fce0c23 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp @@ -232,6 +232,7 @@ void EmuWindow_SDL2::WaitEvent() { } } +// Credits to Samantas5855 and others for this function. void EmuWindow_SDL2::SetWindowIcon() { SDL_RWops* const yuzu_icon_stream = SDL_RWFromConstMem((void*)yuzu_icon, yuzu_icon_size); if (yuzu_icon_stream == nullptr) { |