diff options
Diffstat (limited to 'src')
132 files changed, 3623 insertions, 557 deletions
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 9cf71680c..598f4e8bf 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp | |||
| @@ -218,7 +218,6 @@ public: | |||
| 218 | return; | 218 | return; |
| 219 | } | 219 | } |
| 220 | m_window->OnSurfaceChanged(m_native_window); | 220 | m_window->OnSurfaceChanged(m_native_window); |
| 221 | m_system.Renderer().NotifySurfaceChanged(); | ||
| 222 | } | 221 | } |
| 223 | 222 | ||
| 224 | void ConfigureFilesystemProvider(const std::string& filepath) { | 223 | void ConfigureFilesystemProvider(const std::string& filepath) { |
diff --git a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp index 972d5e45b..ef301d8b4 100644 --- a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp +++ b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp | |||
| @@ -77,6 +77,7 @@ void AudioRenderer::Wait() { | |||
| 77 | "{}, got {}", | 77 | "{}, got {}", |
| 78 | Message::RenderResponse, msg); | 78 | Message::RenderResponse, msg); |
| 79 | } | 79 | } |
| 80 | PostDSPClearCommandBuffer(); | ||
| 80 | } | 81 | } |
| 81 | 82 | ||
| 82 | void AudioRenderer::Send(Direction dir, u32 message) { | 83 | void AudioRenderer::Send(Direction dir, u32 message) { |
| @@ -96,6 +97,14 @@ void AudioRenderer::SetCommandBuffer(s32 session_id, CpuAddr buffer, u64 size, u | |||
| 96 | command_buffers[session_id].reset_buffer = reset; | 97 | command_buffers[session_id].reset_buffer = reset; |
| 97 | } | 98 | } |
| 98 | 99 | ||
| 100 | void AudioRenderer::PostDSPClearCommandBuffer() noexcept { | ||
| 101 | for (auto& buffer : command_buffers) { | ||
| 102 | buffer.buffer = 0; | ||
| 103 | buffer.size = 0; | ||
| 104 | buffer.reset_buffer = false; | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 99 | u32 AudioRenderer::GetRemainCommandCount(s32 session_id) const noexcept { | 108 | u32 AudioRenderer::GetRemainCommandCount(s32 session_id) const noexcept { |
| 100 | return command_buffers[session_id].remaining_command_count; | 109 | return command_buffers[session_id].remaining_command_count; |
| 101 | } | 110 | } |
diff --git a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h index 85874d88a..57b89d9fe 100644 --- a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h +++ b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h | |||
| @@ -85,6 +85,8 @@ private: | |||
| 85 | */ | 85 | */ |
| 86 | void CreateSinkStreams(); | 86 | void CreateSinkStreams(); |
| 87 | 87 | ||
| 88 | void PostDSPClearCommandBuffer() noexcept; | ||
| 89 | |||
| 88 | /// Core system | 90 | /// Core system |
| 89 | Core::System& system; | 91 | Core::System& system; |
| 90 | /// The output sink the AudioRenderer will send samples to | 92 | /// The output sink the AudioRenderer will send samples to |
diff --git a/src/audio_core/renderer/command/mix/depop_prepare.h b/src/audio_core/renderer/command/mix/depop_prepare.h index 161a94461..a0cc228f7 100644 --- a/src/audio_core/renderer/command/mix/depop_prepare.h +++ b/src/audio_core/renderer/command/mix/depop_prepare.h | |||
| @@ -16,7 +16,7 @@ namespace AudioCore::Renderer { | |||
| 16 | 16 | ||
| 17 | /** | 17 | /** |
| 18 | * AudioRenderer command for preparing depop. | 18 | * AudioRenderer command for preparing depop. |
| 19 | * Adds the previusly output last samples to the depop buffer. | 19 | * Adds the previously output last samples to the depop buffer. |
| 20 | */ | 20 | */ |
| 21 | struct DepopPrepareCommand : ICommand { | 21 | struct DepopPrepareCommand : ICommand { |
| 22 | /** | 22 | /** |
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 416203c59..8a1861051 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -189,6 +189,14 @@ if(ARCHITECTURE_x86_64) | |||
| 189 | target_link_libraries(common PRIVATE xbyak::xbyak) | 189 | target_link_libraries(common PRIVATE xbyak::xbyak) |
| 190 | endif() | 190 | endif() |
| 191 | 191 | ||
| 192 | if (ARCHITECTURE_arm64 AND (ANDROID OR LINUX)) | ||
| 193 | target_sources(common | ||
| 194 | PRIVATE | ||
| 195 | arm64/native_clock.cpp | ||
| 196 | arm64/native_clock.h | ||
| 197 | ) | ||
| 198 | endif() | ||
| 199 | |||
| 192 | if (MSVC) | 200 | if (MSVC) |
| 193 | target_compile_definitions(common PRIVATE | 201 | target_compile_definitions(common PRIVATE |
| 194 | # The standard library doesn't provide any replacement for codecvt yet | 202 | # The standard library doesn't provide any replacement for codecvt yet |
diff --git a/src/common/arm64/native_clock.cpp b/src/common/arm64/native_clock.cpp new file mode 100644 index 000000000..88fdba527 --- /dev/null +++ b/src/common/arm64/native_clock.cpp | |||
| @@ -0,0 +1,72 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/arm64/native_clock.h" | ||
| 5 | |||
| 6 | namespace Common::Arm64 { | ||
| 7 | |||
| 8 | namespace { | ||
| 9 | |||
| 10 | NativeClock::FactorType GetFixedPointFactor(u64 num, u64 den) { | ||
| 11 | return (static_cast<NativeClock::FactorType>(num) << 64) / den; | ||
| 12 | } | ||
| 13 | |||
| 14 | u64 MultiplyHigh(u64 m, NativeClock::FactorType factor) { | ||
| 15 | return static_cast<u64>((m * factor) >> 64); | ||
| 16 | } | ||
| 17 | |||
| 18 | } // namespace | ||
| 19 | |||
| 20 | NativeClock::NativeClock() { | ||
| 21 | const u64 host_cntfrq = GetHostCNTFRQ(); | ||
| 22 | ns_cntfrq_factor = GetFixedPointFactor(NsRatio::den, host_cntfrq); | ||
| 23 | us_cntfrq_factor = GetFixedPointFactor(UsRatio::den, host_cntfrq); | ||
| 24 | ms_cntfrq_factor = GetFixedPointFactor(MsRatio::den, host_cntfrq); | ||
| 25 | guest_cntfrq_factor = GetFixedPointFactor(CNTFRQ, host_cntfrq); | ||
| 26 | gputick_cntfrq_factor = GetFixedPointFactor(GPUTickFreq, host_cntfrq); | ||
| 27 | } | ||
| 28 | |||
| 29 | std::chrono::nanoseconds NativeClock::GetTimeNS() const { | ||
| 30 | return std::chrono::nanoseconds{MultiplyHigh(GetHostTicksElapsed(), ns_cntfrq_factor)}; | ||
| 31 | } | ||
| 32 | |||
| 33 | std::chrono::microseconds NativeClock::GetTimeUS() const { | ||
| 34 | return std::chrono::microseconds{MultiplyHigh(GetHostTicksElapsed(), us_cntfrq_factor)}; | ||
| 35 | } | ||
| 36 | |||
| 37 | std::chrono::milliseconds NativeClock::GetTimeMS() const { | ||
| 38 | return std::chrono::milliseconds{MultiplyHigh(GetHostTicksElapsed(), ms_cntfrq_factor)}; | ||
| 39 | } | ||
| 40 | |||
| 41 | u64 NativeClock::GetCNTPCT() const { | ||
| 42 | return MultiplyHigh(GetHostTicksElapsed(), guest_cntfrq_factor); | ||
| 43 | } | ||
| 44 | |||
| 45 | u64 NativeClock::GetGPUTick() const { | ||
| 46 | return MultiplyHigh(GetHostTicksElapsed(), gputick_cntfrq_factor); | ||
| 47 | } | ||
| 48 | |||
| 49 | u64 NativeClock::GetHostTicksNow() const { | ||
| 50 | u64 cntvct_el0 = 0; | ||
| 51 | asm volatile("dsb ish\n\t" | ||
| 52 | "mrs %[cntvct_el0], cntvct_el0\n\t" | ||
| 53 | "dsb ish\n\t" | ||
| 54 | : [cntvct_el0] "=r"(cntvct_el0)); | ||
| 55 | return cntvct_el0; | ||
| 56 | } | ||
| 57 | |||
| 58 | u64 NativeClock::GetHostTicksElapsed() const { | ||
| 59 | return GetHostTicksNow(); | ||
| 60 | } | ||
| 61 | |||
| 62 | bool NativeClock::IsNative() const { | ||
| 63 | return true; | ||
| 64 | } | ||
| 65 | |||
| 66 | u64 NativeClock::GetHostCNTFRQ() { | ||
| 67 | u64 cntfrq_el0 = 0; | ||
| 68 | asm("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0)); | ||
| 69 | return cntfrq_el0; | ||
| 70 | } | ||
| 71 | |||
| 72 | } // namespace Common::Arm64 | ||
diff --git a/src/common/arm64/native_clock.h b/src/common/arm64/native_clock.h new file mode 100644 index 000000000..a28b419f2 --- /dev/null +++ b/src/common/arm64/native_clock.h | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/wall_clock.h" | ||
| 7 | |||
| 8 | namespace Common::Arm64 { | ||
| 9 | |||
| 10 | class NativeClock final : public WallClock { | ||
| 11 | public: | ||
| 12 | explicit NativeClock(); | ||
| 13 | |||
| 14 | std::chrono::nanoseconds GetTimeNS() const override; | ||
| 15 | |||
| 16 | std::chrono::microseconds GetTimeUS() const override; | ||
| 17 | |||
| 18 | std::chrono::milliseconds GetTimeMS() const override; | ||
| 19 | |||
| 20 | u64 GetCNTPCT() const override; | ||
| 21 | |||
| 22 | u64 GetGPUTick() const override; | ||
| 23 | |||
| 24 | u64 GetHostTicksNow() const override; | ||
| 25 | |||
| 26 | u64 GetHostTicksElapsed() const override; | ||
| 27 | |||
| 28 | bool IsNative() const override; | ||
| 29 | |||
| 30 | static u64 GetHostCNTFRQ(); | ||
| 31 | |||
| 32 | public: | ||
| 33 | using FactorType = unsigned __int128; | ||
| 34 | |||
| 35 | FactorType GetGuestCNTFRQFactor() const { | ||
| 36 | return guest_cntfrq_factor; | ||
| 37 | } | ||
| 38 | |||
| 39 | private: | ||
| 40 | FactorType ns_cntfrq_factor; | ||
| 41 | FactorType us_cntfrq_factor; | ||
| 42 | FactorType ms_cntfrq_factor; | ||
| 43 | FactorType guest_cntfrq_factor; | ||
| 44 | FactorType gputick_cntfrq_factor; | ||
| 45 | }; | ||
| 46 | |||
| 47 | } // namespace Common::Arm64 | ||
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index 0dad9338a..47d028d48 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h | |||
| @@ -39,8 +39,12 @@ | |||
| 39 | #define Crash() exit(1) | 39 | #define Crash() exit(1) |
| 40 | #endif | 40 | #endif |
| 41 | 41 | ||
| 42 | #define LTO_NOINLINE __attribute__((noinline)) | ||
| 43 | |||
| 42 | #else // _MSC_VER | 44 | #else // _MSC_VER |
| 43 | 45 | ||
| 46 | #define LTO_NOINLINE | ||
| 47 | |||
| 44 | // Locale Cross-Compatibility | 48 | // Locale Cross-Compatibility |
| 45 | #define locale_t _locale_t | 49 | #define locale_t _locale_t |
| 46 | 50 | ||
diff --git a/src/common/elf.h b/src/common/elf.h index 14a5e9597..0b728dc54 100644 --- a/src/common/elf.h +++ b/src/common/elf.h | |||
| @@ -211,6 +211,11 @@ struct Elf64_Rela { | |||
| 211 | Elf64_Sxword r_addend; /* Addend */ | 211 | Elf64_Sxword r_addend; /* Addend */ |
| 212 | }; | 212 | }; |
| 213 | 213 | ||
| 214 | /* RELR relocation table entry */ | ||
| 215 | |||
| 216 | using Elf32_Relr = Elf32_Word; | ||
| 217 | using Elf64_Relr = Elf64_Xword; | ||
| 218 | |||
| 214 | /* How to extract and insert information held in the r_info field. */ | 219 | /* How to extract and insert information held in the r_info field. */ |
| 215 | 220 | ||
| 216 | static inline u32 Elf32RelSymIndex(Elf32_Word r_info) { | 221 | static inline u32 Elf32RelSymIndex(Elf32_Word r_info) { |
| @@ -328,6 +333,9 @@ constexpr u32 ElfDtFiniArray = 26; /* Array with addresses of fini fct */ | |||
| 328 | constexpr u32 ElfDtInitArraySz = 27; /* Size in bytes of DT_INIT_ARRAY */ | 333 | constexpr u32 ElfDtInitArraySz = 27; /* Size in bytes of DT_INIT_ARRAY */ |
| 329 | constexpr u32 ElfDtFiniArraySz = 28; /* Size in bytes of DT_FINI_ARRAY */ | 334 | constexpr u32 ElfDtFiniArraySz = 28; /* Size in bytes of DT_FINI_ARRAY */ |
| 330 | constexpr u32 ElfDtSymtabShndx = 34; /* Address of SYMTAB_SHNDX section */ | 335 | constexpr u32 ElfDtSymtabShndx = 34; /* Address of SYMTAB_SHNDX section */ |
| 336 | constexpr u32 ElfDtRelrsz = 35; /* Size of RELR relative relocations */ | ||
| 337 | constexpr u32 ElfDtRelr = 36; /* Address of RELR relative relocations */ | ||
| 338 | constexpr u32 ElfDtRelrent = 37; /* Size of one RELR relative relocation */ | ||
| 331 | 339 | ||
| 332 | } // namespace ELF | 340 | } // namespace ELF |
| 333 | } // namespace Common | 341 | } // namespace Common |
diff --git a/src/common/fs/fs_paths.h b/src/common/fs/fs_paths.h index 61bac9eba..441c8af97 100644 --- a/src/common/fs/fs_paths.h +++ b/src/common/fs/fs_paths.h | |||
| @@ -18,10 +18,12 @@ | |||
| 18 | #define LOAD_DIR "load" | 18 | #define LOAD_DIR "load" |
| 19 | #define LOG_DIR "log" | 19 | #define LOG_DIR "log" |
| 20 | #define NAND_DIR "nand" | 20 | #define NAND_DIR "nand" |
| 21 | #define PLAY_TIME_DIR "play_time" | ||
| 21 | #define SCREENSHOTS_DIR "screenshots" | 22 | #define SCREENSHOTS_DIR "screenshots" |
| 22 | #define SDMC_DIR "sdmc" | 23 | #define SDMC_DIR "sdmc" |
| 23 | #define SHADER_DIR "shader" | 24 | #define SHADER_DIR "shader" |
| 24 | #define TAS_DIR "tas" | 25 | #define TAS_DIR "tas" |
| 26 | #define ICONS_DIR "icons" | ||
| 25 | 27 | ||
| 26 | // yuzu-specific files | 28 | // yuzu-specific files |
| 27 | 29 | ||
diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp index dce219fcf..0abd81a45 100644 --- a/src/common/fs/path_util.cpp +++ b/src/common/fs/path_util.cpp | |||
| @@ -124,10 +124,12 @@ public: | |||
| 124 | GenerateYuzuPath(YuzuPath::LoadDir, yuzu_path / LOAD_DIR); | 124 | GenerateYuzuPath(YuzuPath::LoadDir, yuzu_path / LOAD_DIR); |
| 125 | GenerateYuzuPath(YuzuPath::LogDir, yuzu_path / LOG_DIR); | 125 | GenerateYuzuPath(YuzuPath::LogDir, yuzu_path / LOG_DIR); |
| 126 | GenerateYuzuPath(YuzuPath::NANDDir, yuzu_path / NAND_DIR); | 126 | GenerateYuzuPath(YuzuPath::NANDDir, yuzu_path / NAND_DIR); |
| 127 | GenerateYuzuPath(YuzuPath::PlayTimeDir, yuzu_path / PLAY_TIME_DIR); | ||
| 127 | GenerateYuzuPath(YuzuPath::ScreenshotsDir, yuzu_path / SCREENSHOTS_DIR); | 128 | GenerateYuzuPath(YuzuPath::ScreenshotsDir, yuzu_path / SCREENSHOTS_DIR); |
| 128 | GenerateYuzuPath(YuzuPath::SDMCDir, yuzu_path / SDMC_DIR); | 129 | GenerateYuzuPath(YuzuPath::SDMCDir, yuzu_path / SDMC_DIR); |
| 129 | GenerateYuzuPath(YuzuPath::ShaderDir, yuzu_path / SHADER_DIR); | 130 | GenerateYuzuPath(YuzuPath::ShaderDir, yuzu_path / SHADER_DIR); |
| 130 | GenerateYuzuPath(YuzuPath::TASDir, yuzu_path / TAS_DIR); | 131 | GenerateYuzuPath(YuzuPath::TASDir, yuzu_path / TAS_DIR); |
| 132 | GenerateYuzuPath(YuzuPath::IconsDir, yuzu_path / ICONS_DIR); | ||
| 131 | } | 133 | } |
| 132 | 134 | ||
| 133 | private: | 135 | private: |
diff --git a/src/common/fs/path_util.h b/src/common/fs/path_util.h index ba28964d0..63801c924 100644 --- a/src/common/fs/path_util.h +++ b/src/common/fs/path_util.h | |||
| @@ -20,10 +20,12 @@ enum class YuzuPath { | |||
| 20 | LoadDir, // Where cheat/mod files are stored. | 20 | LoadDir, // Where cheat/mod files are stored. |
| 21 | LogDir, // Where log files are stored. | 21 | LogDir, // Where log files are stored. |
| 22 | NANDDir, // Where the emulated NAND is stored. | 22 | NANDDir, // Where the emulated NAND is stored. |
| 23 | PlayTimeDir, // Where play time data is stored. | ||
| 23 | ScreenshotsDir, // Where yuzu screenshots are stored. | 24 | ScreenshotsDir, // Where yuzu screenshots are stored. |
| 24 | SDMCDir, // Where the emulated SDMC is stored. | 25 | SDMCDir, // Where the emulated SDMC is stored. |
| 25 | ShaderDir, // Where shaders are stored. | 26 | ShaderDir, // Where shaders are stored. |
| 26 | TASDir, // Where TAS scripts are stored. | 27 | TASDir, // Where TAS scripts are stored. |
| 28 | IconsDir, // Where Icons for Windows shortcuts are stored. | ||
| 27 | }; | 29 | }; |
| 28 | 30 | ||
| 29 | /** | 31 | /** |
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index feab1653d..4c7aba3f5 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp | |||
| @@ -135,6 +135,11 @@ std::u16string UTF8ToUTF16(std::string_view input) { | |||
| 135 | return convert.from_bytes(input.data(), input.data() + input.size()); | 135 | return convert.from_bytes(input.data(), input.data() + input.size()); |
| 136 | } | 136 | } |
| 137 | 137 | ||
| 138 | std::u32string UTF8ToUTF32(std::string_view input) { | ||
| 139 | std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> convert; | ||
| 140 | return convert.from_bytes(input.data(), input.data() + input.size()); | ||
| 141 | } | ||
| 142 | |||
| 138 | #ifdef _WIN32 | 143 | #ifdef _WIN32 |
| 139 | static std::wstring CPToUTF16(u32 code_page, std::string_view input) { | 144 | static std::wstring CPToUTF16(u32 code_page, std::string_view input) { |
| 140 | const auto size = | 145 | const auto size = |
diff --git a/src/common/string_util.h b/src/common/string_util.h index c351f1a0c..9da1ca4e9 100644 --- a/src/common/string_util.h +++ b/src/common/string_util.h | |||
| @@ -38,6 +38,7 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _ | |||
| 38 | 38 | ||
| 39 | [[nodiscard]] std::string UTF16ToUTF8(std::u16string_view input); | 39 | [[nodiscard]] std::string UTF16ToUTF8(std::u16string_view input); |
| 40 | [[nodiscard]] std::u16string UTF8ToUTF16(std::string_view input); | 40 | [[nodiscard]] std::u16string UTF8ToUTF16(std::string_view input); |
| 41 | [[nodiscard]] std::u32string UTF8ToUTF32(std::string_view input); | ||
| 41 | 42 | ||
| 42 | #ifdef _WIN32 | 43 | #ifdef _WIN32 |
| 43 | [[nodiscard]] std::string UTF16ToUTF8(std::wstring_view input); | 44 | [[nodiscard]] std::string UTF16ToUTF8(std::wstring_view input); |
diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp index 71e15ab4c..caca9a123 100644 --- a/src/common/wall_clock.cpp +++ b/src/common/wall_clock.cpp | |||
| @@ -10,6 +10,10 @@ | |||
| 10 | #include "common/x64/rdtsc.h" | 10 | #include "common/x64/rdtsc.h" |
| 11 | #endif | 11 | #endif |
| 12 | 12 | ||
| 13 | #if defined(ARCHITECTURE_arm64) && defined(__linux__) | ||
| 14 | #include "common/arm64/native_clock.h" | ||
| 15 | #endif | ||
| 16 | |||
| 13 | namespace Common { | 17 | namespace Common { |
| 14 | 18 | ||
| 15 | class StandardWallClock final : public WallClock { | 19 | class StandardWallClock final : public WallClock { |
| @@ -53,7 +57,7 @@ private: | |||
| 53 | }; | 57 | }; |
| 54 | 58 | ||
| 55 | std::unique_ptr<WallClock> CreateOptimalClock() { | 59 | std::unique_ptr<WallClock> CreateOptimalClock() { |
| 56 | #ifdef ARCHITECTURE_x86_64 | 60 | #if defined(ARCHITECTURE_x86_64) |
| 57 | const auto& caps = GetCPUCaps(); | 61 | const auto& caps = GetCPUCaps(); |
| 58 | 62 | ||
| 59 | if (caps.invariant_tsc && caps.tsc_frequency >= std::nano::den) { | 63 | if (caps.invariant_tsc && caps.tsc_frequency >= std::nano::den) { |
| @@ -64,6 +68,8 @@ std::unique_ptr<WallClock> CreateOptimalClock() { | |||
| 64 | // - Is not more precise than 1 GHz (1ns resolution) | 68 | // - Is not more precise than 1 GHz (1ns resolution) |
| 65 | return std::make_unique<StandardWallClock>(); | 69 | return std::make_unique<StandardWallClock>(); |
| 66 | } | 70 | } |
| 71 | #elif defined(ARCHITECTURE_arm64) && defined(__linux__) | ||
| 72 | return std::make_unique<Arm64::NativeClock>(); | ||
| 67 | #else | 73 | #else |
| 68 | return std::make_unique<StandardWallClock>(); | 74 | return std::make_unique<StandardWallClock>(); |
| 69 | #endif | 75 | #endif |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index d0f76e57e..e4f499135 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -466,14 +466,18 @@ add_library(core STATIC | |||
| 466 | hle/service/caps/caps_a.h | 466 | hle/service/caps/caps_a.h |
| 467 | hle/service/caps/caps_c.cpp | 467 | hle/service/caps/caps_c.cpp |
| 468 | hle/service/caps/caps_c.h | 468 | hle/service/caps/caps_c.h |
| 469 | hle/service/caps/caps_u.cpp | 469 | hle/service/caps/caps_manager.cpp |
| 470 | hle/service/caps/caps_u.h | 470 | hle/service/caps/caps_manager.h |
| 471 | hle/service/caps/caps_result.h | ||
| 471 | hle/service/caps/caps_sc.cpp | 472 | hle/service/caps/caps_sc.cpp |
| 472 | hle/service/caps/caps_sc.h | 473 | hle/service/caps/caps_sc.h |
| 473 | hle/service/caps/caps_ss.cpp | 474 | hle/service/caps/caps_ss.cpp |
| 474 | hle/service/caps/caps_ss.h | 475 | hle/service/caps/caps_ss.h |
| 475 | hle/service/caps/caps_su.cpp | 476 | hle/service/caps/caps_su.cpp |
| 476 | hle/service/caps/caps_su.h | 477 | hle/service/caps/caps_su.h |
| 478 | hle/service/caps/caps_types.h | ||
| 479 | hle/service/caps/caps_u.cpp | ||
| 480 | hle/service/caps/caps_u.h | ||
| 477 | hle/service/erpt/erpt.cpp | 481 | hle/service/erpt/erpt.cpp |
| 478 | hle/service/erpt/erpt.h | 482 | hle/service/erpt/erpt.h |
| 479 | hle/service/es/es.cpp | 483 | hle/service/es/es.cpp |
| @@ -698,6 +702,8 @@ add_library(core STATIC | |||
| 698 | hle/service/nvnflinger/consumer_base.cpp | 702 | hle/service/nvnflinger/consumer_base.cpp |
| 699 | hle/service/nvnflinger/consumer_base.h | 703 | hle/service/nvnflinger/consumer_base.h |
| 700 | hle/service/nvnflinger/consumer_listener.h | 704 | hle/service/nvnflinger/consumer_listener.h |
| 705 | hle/service/nvnflinger/fb_share_buffer_manager.cpp | ||
| 706 | hle/service/nvnflinger/fb_share_buffer_manager.h | ||
| 701 | hle/service/nvnflinger/graphic_buffer_producer.cpp | 707 | hle/service/nvnflinger/graphic_buffer_producer.cpp |
| 702 | hle/service/nvnflinger/graphic_buffer_producer.h | 708 | hle/service/nvnflinger/graphic_buffer_producer.h |
| 703 | hle/service/nvnflinger/hos_binder_driver_server.cpp | 709 | hle/service/nvnflinger/hos_binder_driver_server.cpp |
diff --git a/src/core/core.cpp b/src/core/core.cpp index 08cbb8978..0ab2e3b76 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -1078,6 +1078,10 @@ void System::ApplySettings() { | |||
| 1078 | impl->RefreshTime(); | 1078 | impl->RefreshTime(); |
| 1079 | 1079 | ||
| 1080 | if (IsPoweredOn()) { | 1080 | if (IsPoweredOn()) { |
| 1081 | if (Settings::values.custom_rtc_enabled) { | ||
| 1082 | const s64 posix_time{Settings::values.custom_rtc.GetValue()}; | ||
| 1083 | GetTimeManager().UpdateLocalSystemClockTime(posix_time); | ||
| 1084 | } | ||
| 1081 | Renderer().RefreshBaseSettings(); | 1085 | Renderer().RefreshBaseSettings(); |
| 1082 | } | 1086 | } |
| 1083 | } | 1087 | } |
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp index e55831f27..82964f0a1 100644 --- a/src/core/debugger/gdbstub.cpp +++ b/src/core/debugger/gdbstub.cpp | |||
| @@ -2,6 +2,8 @@ | |||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <atomic> | 4 | #include <atomic> |
| 5 | #include <codecvt> | ||
| 6 | #include <locale> | ||
| 5 | #include <numeric> | 7 | #include <numeric> |
| 6 | #include <optional> | 8 | #include <optional> |
| 7 | #include <thread> | 9 | #include <thread> |
| @@ -12,6 +14,7 @@ | |||
| 12 | #include "common/logging/log.h" | 14 | #include "common/logging/log.h" |
| 13 | #include "common/scope_exit.h" | 15 | #include "common/scope_exit.h" |
| 14 | #include "common/settings.h" | 16 | #include "common/settings.h" |
| 17 | #include "common/string_util.h" | ||
| 15 | #include "core/arm/arm_interface.h" | 18 | #include "core/arm/arm_interface.h" |
| 16 | #include "core/core.h" | 19 | #include "core/core.h" |
| 17 | #include "core/debugger/gdbstub.h" | 20 | #include "core/debugger/gdbstub.h" |
| @@ -68,10 +71,16 @@ static std::string EscapeGDB(std::string_view data) { | |||
| 68 | } | 71 | } |
| 69 | 72 | ||
| 70 | static std::string EscapeXML(std::string_view data) { | 73 | static std::string EscapeXML(std::string_view data) { |
| 74 | std::u32string converted = U"[Encoding error]"; | ||
| 75 | try { | ||
| 76 | converted = Common::UTF8ToUTF32(data); | ||
| 77 | } catch (std::range_error&) { | ||
| 78 | } | ||
| 79 | |||
| 71 | std::string escaped; | 80 | std::string escaped; |
| 72 | escaped.reserve(data.size()); | 81 | escaped.reserve(data.size()); |
| 73 | 82 | ||
| 74 | for (char c : data) { | 83 | for (char32_t c : converted) { |
| 75 | switch (c) { | 84 | switch (c) { |
| 76 | case '&': | 85 | case '&': |
| 77 | escaped += "&"; | 86 | escaped += "&"; |
| @@ -86,7 +95,11 @@ static std::string EscapeXML(std::string_view data) { | |||
| 86 | escaped += ">"; | 95 | escaped += ">"; |
| 87 | break; | 96 | break; |
| 88 | default: | 97 | default: |
| 89 | escaped += c; | 98 | if (c > 0x7f) { |
| 99 | escaped += fmt::format("&#{};", static_cast<u32>(c)); | ||
| 100 | } else { | ||
| 101 | escaped += static_cast<char>(c); | ||
| 102 | } | ||
| 90 | break; | 103 | break; |
| 91 | } | 104 | } |
| 92 | } | 105 | } |
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index f00479bd3..8e291ff67 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include <vector> | 5 | #include <vector> |
| 6 | 6 | ||
| 7 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 8 | #include "common/scope_exit.h" | ||
| 8 | #include "core/file_sys/program_metadata.h" | 9 | #include "core/file_sys/program_metadata.h" |
| 9 | #include "core/file_sys/vfs.h" | 10 | #include "core/file_sys/vfs.h" |
| 10 | #include "core/loader/loader.h" | 11 | #include "core/loader/loader.h" |
| @@ -95,6 +96,13 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) { | |||
| 95 | return Loader::ResultStatus::Success; | 96 | return Loader::ResultStatus::Success; |
| 96 | } | 97 | } |
| 97 | 98 | ||
| 99 | Loader::ResultStatus ProgramMetadata::Reload(VirtualFile file) { | ||
| 100 | const u64 original_program_id = aci_header.title_id; | ||
| 101 | SCOPE_EXIT({ aci_header.title_id = original_program_id; }); | ||
| 102 | |||
| 103 | return this->Load(file); | ||
| 104 | } | ||
| 105 | |||
| 98 | /*static*/ ProgramMetadata ProgramMetadata::GetDefault() { | 106 | /*static*/ ProgramMetadata ProgramMetadata::GetDefault() { |
| 99 | // Allow use of cores 0~3 and thread priorities 1~63. | 107 | // Allow use of cores 0~3 and thread priorities 1~63. |
| 100 | constexpr u32 default_thread_info_capability = 0x30007F7; | 108 | constexpr u32 default_thread_info_capability = 0x30007F7; |
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h index 2e8960b07..9f8e74b13 100644 --- a/src/core/file_sys/program_metadata.h +++ b/src/core/file_sys/program_metadata.h | |||
| @@ -56,6 +56,7 @@ public: | |||
| 56 | static ProgramMetadata GetDefault(); | 56 | static ProgramMetadata GetDefault(); |
| 57 | 57 | ||
| 58 | Loader::ResultStatus Load(VirtualFile file); | 58 | Loader::ResultStatus Load(VirtualFile file); |
| 59 | Loader::ResultStatus Reload(VirtualFile file); | ||
| 59 | 60 | ||
| 60 | /// Load from parameters instead of NPDM file, used for KIP | 61 | /// Load from parameters instead of NPDM file, used for KIP |
| 61 | void LoadManual(bool is_64_bit, ProgramAddressSpaceType address_space, s32 main_thread_prio, | 62 | void LoadManual(bool is_64_bit, ProgramAddressSpaceType address_space, s32 main_thread_prio, |
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h index a93e21f67..a7cd1cae3 100644 --- a/src/core/file_sys/vfs.h +++ b/src/core/file_sys/vfs.h | |||
| @@ -175,7 +175,7 @@ public: | |||
| 175 | return Write(reinterpret_cast<const u8*>(&data), sizeof(T), offset); | 175 | return Write(reinterpret_cast<const u8*>(&data), sizeof(T), offset); |
| 176 | } | 176 | } |
| 177 | 177 | ||
| 178 | // Renames the file to name. Returns whether or not the operation was successsful. | 178 | // Renames the file to name. Returns whether or not the operation was successful. |
| 179 | virtual bool Rename(std::string_view name) = 0; | 179 | virtual bool Rename(std::string_view name) = 0; |
| 180 | 180 | ||
| 181 | // Returns the full path of this file as a string, recursively | 181 | // Returns the full path of this file as a string, recursively |
diff --git a/src/core/hle/kernel/k_memory_layout.cpp b/src/core/hle/kernel/k_memory_layout.cpp index af40092c0..bec714668 100644 --- a/src/core/hle/kernel/k_memory_layout.cpp +++ b/src/core/hle/kernel/k_memory_layout.cpp | |||
| @@ -61,7 +61,7 @@ bool KMemoryRegionTree::Insert(u64 address, size_t size, u32 type_id, u32 new_at | |||
| 61 | found->Reset(address, inserted_region_last, old_pair, new_attr, type_id); | 61 | found->Reset(address, inserted_region_last, old_pair, new_attr, type_id); |
| 62 | this->insert(*found); | 62 | this->insert(*found); |
| 63 | } else { | 63 | } else { |
| 64 | // If we can't re-use, adjust the old region. | 64 | // If we can't reuse, adjust the old region. |
| 65 | found->Reset(old_address, address - 1, old_pair, old_attr, old_type); | 65 | found->Reset(old_address, address - 1, old_pair, old_attr, old_type); |
| 66 | this->insert(*found); | 66 | this->insert(*found); |
| 67 | 67 | ||
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index 9bfc85b34..1fbfbf31f 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include "common/assert.h" | 5 | #include "common/assert.h" |
| 6 | #include "common/literals.h" | 6 | #include "common/literals.h" |
| 7 | #include "common/scope_exit.h" | 7 | #include "common/scope_exit.h" |
| 8 | #include "common/settings.h" | ||
| 8 | #include "core/core.h" | 9 | #include "core/core.h" |
| 9 | #include "core/hle/kernel/k_address_space_info.h" | 10 | #include "core/hle/kernel/k_address_space_info.h" |
| 10 | #include "core/hle/kernel/k_memory_block.h" | 11 | #include "core/hle/kernel/k_memory_block.h" |
| @@ -337,11 +338,14 @@ Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type | |||
| 337 | } | 338 | } |
| 338 | 339 | ||
| 339 | void KPageTable::Finalize() { | 340 | void KPageTable::Finalize() { |
| 341 | auto HostUnmapCallback = [&](KProcessAddress addr, u64 size) { | ||
| 342 | if (Settings::IsFastmemEnabled()) { | ||
| 343 | m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size); | ||
| 344 | } | ||
| 345 | }; | ||
| 346 | |||
| 340 | // Finalize memory blocks. | 347 | // Finalize memory blocks. |
| 341 | m_memory_block_manager.Finalize(m_memory_block_slab_manager, | 348 | m_memory_block_manager.Finalize(m_memory_block_slab_manager, std::move(HostUnmapCallback)); |
| 342 | [&](KProcessAddress addr, u64 size) { | ||
| 343 | m_memory->UnmapRegion(*m_page_table_impl, addr, size); | ||
| 344 | }); | ||
| 345 | 349 | ||
| 346 | // Release any insecure mapped memory. | 350 | // Release any insecure mapped memory. |
| 347 | if (m_mapped_insecure_memory) { | 351 | if (m_mapped_insecure_memory) { |
| @@ -2945,6 +2949,23 @@ Result KPageTable::UnlockForIpcUserBuffer(KProcessAddress address, size_t size) | |||
| 2945 | KMemoryAttribute::Locked, nullptr)); | 2949 | KMemoryAttribute::Locked, nullptr)); |
| 2946 | } | 2950 | } |
| 2947 | 2951 | ||
| 2952 | Result KPageTable::LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size, | ||
| 2953 | KMemoryPermission perm) { | ||
| 2954 | R_RETURN(this->LockMemoryAndOpen(out, nullptr, address, size, KMemoryState::FlagCanTransfer, | ||
| 2955 | KMemoryState::FlagCanTransfer, KMemoryPermission::All, | ||
| 2956 | KMemoryPermission::UserReadWrite, KMemoryAttribute::All, | ||
| 2957 | KMemoryAttribute::None, perm, KMemoryAttribute::Locked)); | ||
| 2958 | } | ||
| 2959 | |||
| 2960 | Result KPageTable::UnlockForTransferMemory(KProcessAddress address, size_t size, | ||
| 2961 | const KPageGroup& pg) { | ||
| 2962 | R_RETURN(this->UnlockMemory(address, size, KMemoryState::FlagCanTransfer, | ||
| 2963 | KMemoryState::FlagCanTransfer, KMemoryPermission::None, | ||
| 2964 | KMemoryPermission::None, KMemoryAttribute::All, | ||
| 2965 | KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, | ||
| 2966 | KMemoryAttribute::Locked, std::addressof(pg))); | ||
| 2967 | } | ||
| 2968 | |||
| 2948 | Result KPageTable::LockForCodeMemory(KPageGroup* out, KProcessAddress addr, size_t size) { | 2969 | Result KPageTable::LockForCodeMemory(KPageGroup* out, KProcessAddress addr, size_t size) { |
| 2949 | R_RETURN(this->LockMemoryAndOpen( | 2970 | R_RETURN(this->LockMemoryAndOpen( |
| 2950 | out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory, | 2971 | out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory, |
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index b9e8c6042..7da675f27 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h | |||
| @@ -104,6 +104,9 @@ public: | |||
| 104 | Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state); | 104 | Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state); |
| 105 | Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state); | 105 | Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state); |
| 106 | 106 | ||
| 107 | Result LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size, | ||
| 108 | KMemoryPermission perm); | ||
| 109 | Result UnlockForTransferMemory(KProcessAddress address, size_t size, const KPageGroup& pg); | ||
| 107 | Result LockForCodeMemory(KPageGroup* out, KProcessAddress addr, size_t size); | 110 | Result LockForCodeMemory(KPageGroup* out, KProcessAddress addr, size_t size); |
| 108 | Result UnlockForCodeMemory(KProcessAddress addr, size_t size, const KPageGroup& pg); | 111 | Result UnlockForCodeMemory(KProcessAddress addr, size_t size, const KPageGroup& pg); |
| 109 | Result MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages, | 112 | Result MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages, |
diff --git a/src/core/hle/kernel/k_transfer_memory.cpp b/src/core/hle/kernel/k_transfer_memory.cpp index 13d34125c..0e2e11743 100644 --- a/src/core/hle/kernel/k_transfer_memory.cpp +++ b/src/core/hle/kernel/k_transfer_memory.cpp | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "common/scope_exit.h" | ||
| 4 | #include "core/hle/kernel/k_process.h" | 5 | #include "core/hle/kernel/k_process.h" |
| 5 | #include "core/hle/kernel/k_resource_limit.h" | 6 | #include "core/hle/kernel/k_resource_limit.h" |
| 6 | #include "core/hle/kernel/k_transfer_memory.h" | 7 | #include "core/hle/kernel/k_transfer_memory.h" |
| @@ -9,28 +10,50 @@ | |||
| 9 | namespace Kernel { | 10 | namespace Kernel { |
| 10 | 11 | ||
| 11 | KTransferMemory::KTransferMemory(KernelCore& kernel) | 12 | KTransferMemory::KTransferMemory(KernelCore& kernel) |
| 12 | : KAutoObjectWithSlabHeapAndContainer{kernel} {} | 13 | : KAutoObjectWithSlabHeapAndContainer{kernel}, m_lock{kernel} {} |
| 13 | 14 | ||
| 14 | KTransferMemory::~KTransferMemory() = default; | 15 | KTransferMemory::~KTransferMemory() = default; |
| 15 | 16 | ||
| 16 | Result KTransferMemory::Initialize(KProcessAddress address, std::size_t size, | 17 | Result KTransferMemory::Initialize(KProcessAddress addr, std::size_t size, |
| 17 | Svc::MemoryPermission owner_perm) { | 18 | Svc::MemoryPermission own_perm) { |
| 18 | // Set members. | 19 | // Set members. |
| 19 | m_owner = GetCurrentProcessPointer(m_kernel); | 20 | m_owner = GetCurrentProcessPointer(m_kernel); |
| 20 | 21 | ||
| 21 | // TODO(bunnei): Lock for transfer memory | 22 | // Get the owner page table. |
| 23 | auto& page_table = m_owner->GetPageTable(); | ||
| 24 | |||
| 25 | // Construct the page group, guarding to make sure our state is valid on exit. | ||
| 26 | m_page_group.emplace(m_kernel, page_table.GetBlockInfoManager()); | ||
| 27 | auto pg_guard = SCOPE_GUARD({ m_page_group.reset(); }); | ||
| 28 | |||
| 29 | // Lock the memory. | ||
| 30 | R_TRY(page_table.LockForTransferMemory(std::addressof(*m_page_group), addr, size, | ||
| 31 | ConvertToKMemoryPermission(own_perm))); | ||
| 22 | 32 | ||
| 23 | // Set remaining tracking members. | 33 | // Set remaining tracking members. |
| 24 | m_owner->Open(); | 34 | m_owner->Open(); |
| 25 | m_owner_perm = owner_perm; | 35 | m_owner_perm = own_perm; |
| 26 | m_address = address; | 36 | m_address = addr; |
| 27 | m_size = size; | ||
| 28 | m_is_initialized = true; | 37 | m_is_initialized = true; |
| 38 | m_is_mapped = false; | ||
| 29 | 39 | ||
| 40 | // We succeeded. | ||
| 41 | pg_guard.Cancel(); | ||
| 30 | R_SUCCEED(); | 42 | R_SUCCEED(); |
| 31 | } | 43 | } |
| 32 | 44 | ||
| 33 | void KTransferMemory::Finalize() {} | 45 | void KTransferMemory::Finalize() { |
| 46 | // Unlock. | ||
| 47 | if (!m_is_mapped) { | ||
| 48 | const size_t size = m_page_group->GetNumPages() * PageSize; | ||
| 49 | ASSERT(R_SUCCEEDED( | ||
| 50 | m_owner->GetPageTable().UnlockForTransferMemory(m_address, size, *m_page_group))); | ||
| 51 | } | ||
| 52 | |||
| 53 | // Close the page group. | ||
| 54 | m_page_group->Close(); | ||
| 55 | m_page_group->Finalize(); | ||
| 56 | } | ||
| 34 | 57 | ||
| 35 | void KTransferMemory::PostDestroy(uintptr_t arg) { | 58 | void KTransferMemory::PostDestroy(uintptr_t arg) { |
| 36 | KProcess* owner = reinterpret_cast<KProcess*>(arg); | 59 | KProcess* owner = reinterpret_cast<KProcess*>(arg); |
| @@ -38,4 +61,54 @@ void KTransferMemory::PostDestroy(uintptr_t arg) { | |||
| 38 | owner->Close(); | 61 | owner->Close(); |
| 39 | } | 62 | } |
| 40 | 63 | ||
| 64 | Result KTransferMemory::Map(KProcessAddress address, size_t size, Svc::MemoryPermission map_perm) { | ||
| 65 | // Validate the size. | ||
| 66 | R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); | ||
| 67 | |||
| 68 | // Validate the permission. | ||
| 69 | R_UNLESS(m_owner_perm == map_perm, ResultInvalidState); | ||
| 70 | |||
| 71 | // Lock ourselves. | ||
| 72 | KScopedLightLock lk(m_lock); | ||
| 73 | |||
| 74 | // Ensure we're not already mapped. | ||
| 75 | R_UNLESS(!m_is_mapped, ResultInvalidState); | ||
| 76 | |||
| 77 | // Map the memory. | ||
| 78 | const KMemoryState state = (m_owner_perm == Svc::MemoryPermission::None) | ||
| 79 | ? KMemoryState::Transfered | ||
| 80 | : KMemoryState::SharedTransfered; | ||
| 81 | R_TRY(GetCurrentProcess(m_kernel).GetPageTable().MapPageGroup( | ||
| 82 | address, *m_page_group, state, KMemoryPermission::UserReadWrite)); | ||
| 83 | |||
| 84 | // Mark ourselves as mapped. | ||
| 85 | m_is_mapped = true; | ||
| 86 | |||
| 87 | R_SUCCEED(); | ||
| 88 | } | ||
| 89 | |||
| 90 | Result KTransferMemory::Unmap(KProcessAddress address, size_t size) { | ||
| 91 | // Validate the size. | ||
| 92 | R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); | ||
| 93 | |||
| 94 | // Lock ourselves. | ||
| 95 | KScopedLightLock lk(m_lock); | ||
| 96 | |||
| 97 | // Unmap the memory. | ||
| 98 | const KMemoryState state = (m_owner_perm == Svc::MemoryPermission::None) | ||
| 99 | ? KMemoryState::Transfered | ||
| 100 | : KMemoryState::SharedTransfered; | ||
| 101 | R_TRY(GetCurrentProcess(m_kernel).GetPageTable().UnmapPageGroup(address, *m_page_group, state)); | ||
| 102 | |||
| 103 | // Mark ourselves as unmapped. | ||
| 104 | ASSERT(m_is_mapped); | ||
| 105 | m_is_mapped = false; | ||
| 106 | |||
| 107 | R_SUCCEED(); | ||
| 108 | } | ||
| 109 | |||
| 110 | size_t KTransferMemory::GetSize() const { | ||
| 111 | return m_is_initialized ? m_page_group->GetNumPages() * PageSize : 0; | ||
| 112 | } | ||
| 113 | |||
| 41 | } // namespace Kernel | 114 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/k_transfer_memory.h b/src/core/hle/kernel/k_transfer_memory.h index 54f97ccb4..8a0b08761 100644 --- a/src/core/hle/kernel/k_transfer_memory.h +++ b/src/core/hle/kernel/k_transfer_memory.h | |||
| @@ -3,6 +3,9 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <optional> | ||
| 7 | |||
| 8 | #include "core/hle/kernel/k_page_group.h" | ||
| 6 | #include "core/hle/kernel/slab_helpers.h" | 9 | #include "core/hle/kernel/slab_helpers.h" |
| 7 | #include "core/hle/kernel/svc_types.h" | 10 | #include "core/hle/kernel/svc_types.h" |
| 8 | #include "core/hle/result.h" | 11 | #include "core/hle/result.h" |
| @@ -48,16 +51,19 @@ public: | |||
| 48 | return m_address; | 51 | return m_address; |
| 49 | } | 52 | } |
| 50 | 53 | ||
| 51 | size_t GetSize() const { | 54 | size_t GetSize() const; |
| 52 | return m_is_initialized ? m_size : 0; | 55 | |
| 53 | } | 56 | Result Map(KProcessAddress address, size_t size, Svc::MemoryPermission map_perm); |
| 57 | Result Unmap(KProcessAddress address, size_t size); | ||
| 54 | 58 | ||
| 55 | private: | 59 | private: |
| 60 | std::optional<KPageGroup> m_page_group{}; | ||
| 56 | KProcess* m_owner{}; | 61 | KProcess* m_owner{}; |
| 57 | KProcessAddress m_address{}; | 62 | KProcessAddress m_address{}; |
| 63 | KLightLock m_lock; | ||
| 58 | Svc::MemoryPermission m_owner_perm{}; | 64 | Svc::MemoryPermission m_owner_perm{}; |
| 59 | size_t m_size{}; | ||
| 60 | bool m_is_initialized{}; | 65 | bool m_is_initialized{}; |
| 66 | bool m_is_mapped{}; | ||
| 61 | }; | 67 | }; |
| 62 | 68 | ||
| 63 | } // namespace Kernel | 69 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index a1134b7e2..cb025c3d6 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -373,7 +373,7 @@ struct KernelCore::Impl { | |||
| 373 | static inline thread_local u8 host_thread_id = UINT8_MAX; | 373 | static inline thread_local u8 host_thread_id = UINT8_MAX; |
| 374 | 374 | ||
| 375 | /// Sets the host thread ID for the caller. | 375 | /// Sets the host thread ID for the caller. |
| 376 | u32 SetHostThreadId(std::size_t core_id) { | 376 | LTO_NOINLINE u32 SetHostThreadId(std::size_t core_id) { |
| 377 | // This should only be called during core init. | 377 | // This should only be called during core init. |
| 378 | ASSERT(host_thread_id == UINT8_MAX); | 378 | ASSERT(host_thread_id == UINT8_MAX); |
| 379 | 379 | ||
| @@ -384,13 +384,13 @@ struct KernelCore::Impl { | |||
| 384 | } | 384 | } |
| 385 | 385 | ||
| 386 | /// Gets the host thread ID for the caller | 386 | /// Gets the host thread ID for the caller |
| 387 | u32 GetHostThreadId() const { | 387 | LTO_NOINLINE u32 GetHostThreadId() const { |
| 388 | return host_thread_id; | 388 | return host_thread_id; |
| 389 | } | 389 | } |
| 390 | 390 | ||
| 391 | // Gets the dummy KThread for the caller, allocating a new one if this is the first time | 391 | // Gets the dummy KThread for the caller, allocating a new one if this is the first time |
| 392 | KThread* GetHostDummyThread(KThread* existing_thread) { | 392 | LTO_NOINLINE KThread* GetHostDummyThread(KThread* existing_thread) { |
| 393 | const auto initialize{[](KThread* thread) { | 393 | const auto initialize{[](KThread* thread) LTO_NOINLINE { |
| 394 | ASSERT(KThread::InitializeDummyThread(thread, nullptr).IsSuccess()); | 394 | ASSERT(KThread::InitializeDummyThread(thread, nullptr).IsSuccess()); |
| 395 | return thread; | 395 | return thread; |
| 396 | }}; | 396 | }}; |
| @@ -424,11 +424,11 @@ struct KernelCore::Impl { | |||
| 424 | 424 | ||
| 425 | static inline thread_local bool is_phantom_mode_for_singlecore{false}; | 425 | static inline thread_local bool is_phantom_mode_for_singlecore{false}; |
| 426 | 426 | ||
| 427 | bool IsPhantomModeForSingleCore() const { | 427 | LTO_NOINLINE bool IsPhantomModeForSingleCore() const { |
| 428 | return is_phantom_mode_for_singlecore; | 428 | return is_phantom_mode_for_singlecore; |
| 429 | } | 429 | } |
| 430 | 430 | ||
| 431 | void SetIsPhantomModeForSingleCore(bool value) { | 431 | LTO_NOINLINE void SetIsPhantomModeForSingleCore(bool value) { |
| 432 | ASSERT(!is_multicore); | 432 | ASSERT(!is_multicore); |
| 433 | is_phantom_mode_for_singlecore = value; | 433 | is_phantom_mode_for_singlecore = value; |
| 434 | } | 434 | } |
| @@ -439,14 +439,14 @@ struct KernelCore::Impl { | |||
| 439 | 439 | ||
| 440 | static inline thread_local KThread* current_thread{nullptr}; | 440 | static inline thread_local KThread* current_thread{nullptr}; |
| 441 | 441 | ||
| 442 | KThread* GetCurrentEmuThread() { | 442 | LTO_NOINLINE KThread* GetCurrentEmuThread() { |
| 443 | if (!current_thread) { | 443 | if (!current_thread) { |
| 444 | current_thread = GetHostDummyThread(nullptr); | 444 | current_thread = GetHostDummyThread(nullptr); |
| 445 | } | 445 | } |
| 446 | return current_thread; | 446 | return current_thread; |
| 447 | } | 447 | } |
| 448 | 448 | ||
| 449 | void SetCurrentEmuThread(KThread* thread) { | 449 | LTO_NOINLINE void SetCurrentEmuThread(KThread* thread) { |
| 450 | current_thread = thread; | 450 | current_thread = thread; |
| 451 | } | 451 | } |
| 452 | 452 | ||
diff --git a/src/core/hle/kernel/svc/svc_transfer_memory.cpp b/src/core/hle/kernel/svc/svc_transfer_memory.cpp index 7d94e7f09..1f97121b3 100644 --- a/src/core/hle/kernel/svc/svc_transfer_memory.cpp +++ b/src/core/hle/kernel/svc/svc_transfer_memory.cpp | |||
| @@ -71,15 +71,59 @@ Result CreateTransferMemory(Core::System& system, Handle* out, u64 address, u64 | |||
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | Result MapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t address, uint64_t size, | 73 | Result MapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t address, uint64_t size, |
| 74 | MemoryPermission owner_perm) { | 74 | MemoryPermission map_perm) { |
| 75 | UNIMPLEMENTED(); | 75 | // Validate the address/size. |
| 76 | R_THROW(ResultNotImplemented); | 76 | R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); |
| 77 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 78 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 79 | R_UNLESS((address < address + size), ResultInvalidCurrentMemory); | ||
| 80 | |||
| 81 | // Validate the permission. | ||
| 82 | R_UNLESS(IsValidTransferMemoryPermission(map_perm), ResultInvalidState); | ||
| 83 | |||
| 84 | // Get the transfer memory. | ||
| 85 | KScopedAutoObject trmem = GetCurrentProcess(system.Kernel()) | ||
| 86 | .GetHandleTable() | ||
| 87 | .GetObject<KTransferMemory>(trmem_handle); | ||
| 88 | R_UNLESS(trmem.IsNotNull(), ResultInvalidHandle); | ||
| 89 | |||
| 90 | // Verify that the mapping is in range. | ||
| 91 | R_UNLESS(GetCurrentProcess(system.Kernel()) | ||
| 92 | .GetPageTable() | ||
| 93 | .CanContain(address, size, KMemoryState::Transfered), | ||
| 94 | ResultInvalidMemoryRegion); | ||
| 95 | |||
| 96 | // Map the transfer memory. | ||
| 97 | R_TRY(trmem->Map(address, size, map_perm)); | ||
| 98 | |||
| 99 | // We succeeded. | ||
| 100 | R_SUCCEED(); | ||
| 77 | } | 101 | } |
| 78 | 102 | ||
| 79 | Result UnmapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t address, | 103 | Result UnmapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t address, |
| 80 | uint64_t size) { | 104 | uint64_t size) { |
| 81 | UNIMPLEMENTED(); | 105 | // Validate the address/size. |
| 82 | R_THROW(ResultNotImplemented); | 106 | R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); |
| 107 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 108 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 109 | R_UNLESS((address < address + size), ResultInvalidCurrentMemory); | ||
| 110 | |||
| 111 | // Get the transfer memory. | ||
| 112 | KScopedAutoObject trmem = GetCurrentProcess(system.Kernel()) | ||
| 113 | .GetHandleTable() | ||
| 114 | .GetObject<KTransferMemory>(trmem_handle); | ||
| 115 | R_UNLESS(trmem.IsNotNull(), ResultInvalidHandle); | ||
| 116 | |||
| 117 | // Verify that the mapping is in range. | ||
| 118 | R_UNLESS(GetCurrentProcess(system.Kernel()) | ||
| 119 | .GetPageTable() | ||
| 120 | .CanContain(address, size, KMemoryState::Transfered), | ||
| 121 | ResultInvalidMemoryRegion); | ||
| 122 | |||
| 123 | // Unmap the transfer memory. | ||
| 124 | R_TRY(trmem->Unmap(address, size)); | ||
| 125 | |||
| 126 | R_SUCCEED(); | ||
| 83 | } | 127 | } |
| 84 | 128 | ||
| 85 | Result MapTransferMemory64(Core::System& system, Handle trmem_handle, uint64_t address, | 129 | Result MapTransferMemory64(Core::System& system, Handle trmem_handle, uint64_t address, |
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index a83622f7c..ac376b55a 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include "common/settings.h" | 8 | #include "common/settings.h" |
| 9 | #include "common/settings_enums.h" | 9 | #include "common/settings_enums.h" |
| 10 | #include "core/core.h" | 10 | #include "core/core.h" |
| 11 | #include "core/core_timing.h" | ||
| 11 | #include "core/file_sys/control_metadata.h" | 12 | #include "core/file_sys/control_metadata.h" |
| 12 | #include "core/file_sys/patch_manager.h" | 13 | #include "core/file_sys/patch_manager.h" |
| 13 | #include "core/file_sys/registered_cache.h" | 14 | #include "core/file_sys/registered_cache.h" |
| @@ -19,6 +20,7 @@ | |||
| 19 | #include "core/hle/service/am/am.h" | 20 | #include "core/hle/service/am/am.h" |
| 20 | #include "core/hle/service/am/applet_ae.h" | 21 | #include "core/hle/service/am/applet_ae.h" |
| 21 | #include "core/hle/service/am/applet_oe.h" | 22 | #include "core/hle/service/am/applet_oe.h" |
| 23 | #include "core/hle/service/am/applets/applet_cabinet.h" | ||
| 22 | #include "core/hle/service/am/applets/applet_mii_edit_types.h" | 24 | #include "core/hle/service/am/applets/applet_mii_edit_types.h" |
| 23 | #include "core/hle/service/am/applets/applet_profile_select.h" | 25 | #include "core/hle/service/am/applets/applet_profile_select.h" |
| 24 | #include "core/hle/service/am/applets/applet_web_browser.h" | 26 | #include "core/hle/service/am/applets/applet_web_browser.h" |
| @@ -29,15 +31,17 @@ | |||
| 29 | #include "core/hle/service/apm/apm_controller.h" | 31 | #include "core/hle/service/apm/apm_controller.h" |
| 30 | #include "core/hle/service/apm/apm_interface.h" | 32 | #include "core/hle/service/apm/apm_interface.h" |
| 31 | #include "core/hle/service/bcat/backend/backend.h" | 33 | #include "core/hle/service/bcat/backend/backend.h" |
| 32 | #include "core/hle/service/caps/caps.h" | 34 | #include "core/hle/service/caps/caps_types.h" |
| 33 | #include "core/hle/service/filesystem/filesystem.h" | 35 | #include "core/hle/service/filesystem/filesystem.h" |
| 34 | #include "core/hle/service/ipc_helpers.h" | 36 | #include "core/hle/service/ipc_helpers.h" |
| 35 | #include "core/hle/service/ns/ns.h" | 37 | #include "core/hle/service/ns/ns.h" |
| 38 | #include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" | ||
| 36 | #include "core/hle/service/nvnflinger/nvnflinger.h" | 39 | #include "core/hle/service/nvnflinger/nvnflinger.h" |
| 37 | #include "core/hle/service/pm/pm.h" | 40 | #include "core/hle/service/pm/pm.h" |
| 38 | #include "core/hle/service/server_manager.h" | 41 | #include "core/hle/service/server_manager.h" |
| 39 | #include "core/hle/service/sm/sm.h" | 42 | #include "core/hle/service/sm/sm.h" |
| 40 | #include "core/hle/service/vi/vi.h" | 43 | #include "core/hle/service/vi/vi.h" |
| 44 | #include "core/hle/service/vi/vi_results.h" | ||
| 41 | #include "core/memory.h" | 45 | #include "core/memory.h" |
| 42 | 46 | ||
| 43 | namespace Service::AM { | 47 | namespace Service::AM { |
| @@ -190,7 +194,7 @@ IDisplayController::IDisplayController(Core::System& system_) | |||
| 190 | {4, nullptr, "UpdateCallerAppletCaptureImage"}, | 194 | {4, nullptr, "UpdateCallerAppletCaptureImage"}, |
| 191 | {5, nullptr, "GetLastForegroundCaptureImageEx"}, | 195 | {5, nullptr, "GetLastForegroundCaptureImageEx"}, |
| 192 | {6, nullptr, "GetLastApplicationCaptureImageEx"}, | 196 | {6, nullptr, "GetLastApplicationCaptureImageEx"}, |
| 193 | {7, nullptr, "GetCallerAppletCaptureImageEx"}, | 197 | {7, &IDisplayController::GetCallerAppletCaptureImageEx, "GetCallerAppletCaptureImageEx"}, |
| 194 | {8, &IDisplayController::TakeScreenShotOfOwnLayer, "TakeScreenShotOfOwnLayer"}, | 198 | {8, &IDisplayController::TakeScreenShotOfOwnLayer, "TakeScreenShotOfOwnLayer"}, |
| 195 | {9, nullptr, "CopyBetweenCaptureBuffers"}, | 199 | {9, nullptr, "CopyBetweenCaptureBuffers"}, |
| 196 | {10, nullptr, "AcquireLastApplicationCaptureBuffer"}, | 200 | {10, nullptr, "AcquireLastApplicationCaptureBuffer"}, |
| @@ -208,8 +212,8 @@ IDisplayController::IDisplayController(Core::System& system_) | |||
| 208 | {23, nullptr, "ReleaseLastApplicationCaptureSharedBuffer"}, | 212 | {23, nullptr, "ReleaseLastApplicationCaptureSharedBuffer"}, |
| 209 | {24, nullptr, "AcquireLastForegroundCaptureSharedBuffer"}, | 213 | {24, nullptr, "AcquireLastForegroundCaptureSharedBuffer"}, |
| 210 | {25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"}, | 214 | {25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"}, |
| 211 | {26, nullptr, "AcquireCallerAppletCaptureSharedBuffer"}, | 215 | {26, &IDisplayController::AcquireCallerAppletCaptureSharedBuffer, "AcquireCallerAppletCaptureSharedBuffer"}, |
| 212 | {27, nullptr, "ReleaseCallerAppletCaptureSharedBuffer"}, | 216 | {27, &IDisplayController::ReleaseCallerAppletCaptureSharedBuffer, "ReleaseCallerAppletCaptureSharedBuffer"}, |
| 213 | {28, nullptr, "TakeScreenShotOfOwnLayerEx"}, | 217 | {28, nullptr, "TakeScreenShotOfOwnLayerEx"}, |
| 214 | }; | 218 | }; |
| 215 | // clang-format on | 219 | // clang-format on |
| @@ -219,6 +223,15 @@ IDisplayController::IDisplayController(Core::System& system_) | |||
| 219 | 223 | ||
| 220 | IDisplayController::~IDisplayController() = default; | 224 | IDisplayController::~IDisplayController() = default; |
| 221 | 225 | ||
| 226 | void IDisplayController::GetCallerAppletCaptureImageEx(HLERequestContext& ctx) { | ||
| 227 | LOG_WARNING(Service_AM, "(STUBBED) called"); | ||
| 228 | |||
| 229 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 230 | rb.Push(ResultSuccess); | ||
| 231 | rb.Push(1u); | ||
| 232 | rb.Push(0); | ||
| 233 | } | ||
| 234 | |||
| 222 | void IDisplayController::TakeScreenShotOfOwnLayer(HLERequestContext& ctx) { | 235 | void IDisplayController::TakeScreenShotOfOwnLayer(HLERequestContext& ctx) { |
| 223 | LOG_WARNING(Service_AM, "(STUBBED) called"); | 236 | LOG_WARNING(Service_AM, "(STUBBED) called"); |
| 224 | 237 | ||
| @@ -226,6 +239,22 @@ void IDisplayController::TakeScreenShotOfOwnLayer(HLERequestContext& ctx) { | |||
| 226 | rb.Push(ResultSuccess); | 239 | rb.Push(ResultSuccess); |
| 227 | } | 240 | } |
| 228 | 241 | ||
| 242 | void IDisplayController::AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx) { | ||
| 243 | LOG_WARNING(Service_AM, "(STUBBED) called"); | ||
| 244 | |||
| 245 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 246 | rb.Push(ResultSuccess); | ||
| 247 | rb.Push(1U); | ||
| 248 | rb.Push(0); | ||
| 249 | } | ||
| 250 | |||
| 251 | void IDisplayController::ReleaseCallerAppletCaptureSharedBuffer(HLERequestContext& ctx) { | ||
| 252 | LOG_WARNING(Service_AM, "(STUBBED) called"); | ||
| 253 | |||
| 254 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 255 | rb.Push(ResultSuccess); | ||
| 256 | } | ||
| 257 | |||
| 229 | IDebugFunctions::IDebugFunctions(Core::System& system_) | 258 | IDebugFunctions::IDebugFunctions(Core::System& system_) |
| 230 | : ServiceFramework{system_, "IDebugFunctions"} { | 259 | : ServiceFramework{system_, "IDebugFunctions"} { |
| 231 | // clang-format off | 260 | // clang-format off |
| @@ -285,14 +314,14 @@ ISelfController::ISelfController(Core::System& system_, Nvnflinger::Nvnflinger& | |||
| 285 | {20, nullptr, "SetDesirableKeyboardLayout"}, | 314 | {20, nullptr, "SetDesirableKeyboardLayout"}, |
| 286 | {21, nullptr, "GetScreenShotProgramId"}, | 315 | {21, nullptr, "GetScreenShotProgramId"}, |
| 287 | {40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"}, | 316 | {40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"}, |
| 288 | {41, nullptr, "IsSystemBufferSharingEnabled"}, | 317 | {41, &ISelfController::IsSystemBufferSharingEnabled, "IsSystemBufferSharingEnabled"}, |
| 289 | {42, nullptr, "GetSystemSharedLayerHandle"}, | 318 | {42, &ISelfController::GetSystemSharedLayerHandle, "GetSystemSharedLayerHandle"}, |
| 290 | {43, nullptr, "GetSystemSharedBufferHandle"}, | 319 | {43, &ISelfController::GetSystemSharedBufferHandle, "GetSystemSharedBufferHandle"}, |
| 291 | {44, &ISelfController::CreateManagedDisplaySeparableLayer, "CreateManagedDisplaySeparableLayer"}, | 320 | {44, &ISelfController::CreateManagedDisplaySeparableLayer, "CreateManagedDisplaySeparableLayer"}, |
| 292 | {45, nullptr, "SetManagedDisplayLayerSeparationMode"}, | 321 | {45, nullptr, "SetManagedDisplayLayerSeparationMode"}, |
| 293 | {46, nullptr, "SetRecordingLayerCompositionEnabled"}, | 322 | {46, nullptr, "SetRecordingLayerCompositionEnabled"}, |
| 294 | {50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"}, | 323 | {50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"}, |
| 295 | {51, nullptr, "ApproveToDisplay"}, | 324 | {51, &ISelfController::ApproveToDisplay, "ApproveToDisplay"}, |
| 296 | {60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"}, | 325 | {60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"}, |
| 297 | {61, nullptr, "SetMediaPlaybackState"}, | 326 | {61, nullptr, "SetMediaPlaybackState"}, |
| 298 | {62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"}, | 327 | {62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"}, |
| @@ -491,6 +520,50 @@ void ISelfController::CreateManagedDisplayLayer(HLERequestContext& ctx) { | |||
| 491 | rb.Push(*layer_id); | 520 | rb.Push(*layer_id); |
| 492 | } | 521 | } |
| 493 | 522 | ||
| 523 | void ISelfController::IsSystemBufferSharingEnabled(HLERequestContext& ctx) { | ||
| 524 | LOG_WARNING(Service_AM, "(STUBBED) called"); | ||
| 525 | |||
| 526 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 527 | rb.Push(this->EnsureBufferSharingEnabled()); | ||
| 528 | } | ||
| 529 | |||
| 530 | void ISelfController::GetSystemSharedLayerHandle(HLERequestContext& ctx) { | ||
| 531 | LOG_WARNING(Service_AM, "(STUBBED) called"); | ||
| 532 | |||
| 533 | IPC::ResponseBuilder rb{ctx, 6}; | ||
| 534 | rb.Push(this->EnsureBufferSharingEnabled()); | ||
| 535 | rb.Push<s64>(system_shared_buffer_id); | ||
| 536 | rb.Push<s64>(system_shared_layer_id); | ||
| 537 | } | ||
| 538 | |||
| 539 | void ISelfController::GetSystemSharedBufferHandle(HLERequestContext& ctx) { | ||
| 540 | LOG_WARNING(Service_AM, "(STUBBED) called"); | ||
| 541 | |||
| 542 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 543 | rb.Push(this->EnsureBufferSharingEnabled()); | ||
| 544 | rb.Push<s64>(system_shared_buffer_id); | ||
| 545 | } | ||
| 546 | |||
| 547 | Result ISelfController::EnsureBufferSharingEnabled() { | ||
| 548 | if (buffer_sharing_enabled) { | ||
| 549 | return ResultSuccess; | ||
| 550 | } | ||
| 551 | |||
| 552 | if (system.GetAppletManager().GetCurrentAppletId() <= Applets::AppletId::Application) { | ||
| 553 | return VI::ResultOperationFailed; | ||
| 554 | } | ||
| 555 | |||
| 556 | const auto display_id = nvnflinger.OpenDisplay("Default"); | ||
| 557 | const auto result = nvnflinger.GetSystemBufferManager().Initialize( | ||
| 558 | &system_shared_buffer_id, &system_shared_layer_id, *display_id); | ||
| 559 | |||
| 560 | if (result.IsSuccess()) { | ||
| 561 | buffer_sharing_enabled = true; | ||
| 562 | } | ||
| 563 | |||
| 564 | return result; | ||
| 565 | } | ||
| 566 | |||
| 494 | void ISelfController::CreateManagedDisplaySeparableLayer(HLERequestContext& ctx) { | 567 | void ISelfController::CreateManagedDisplaySeparableLayer(HLERequestContext& ctx) { |
| 495 | LOG_WARNING(Service_AM, "(STUBBED) called"); | 568 | LOG_WARNING(Service_AM, "(STUBBED) called"); |
| 496 | 569 | ||
| @@ -516,6 +589,13 @@ void ISelfController::SetHandlesRequestToDisplay(HLERequestContext& ctx) { | |||
| 516 | rb.Push(ResultSuccess); | 589 | rb.Push(ResultSuccess); |
| 517 | } | 590 | } |
| 518 | 591 | ||
| 592 | void ISelfController::ApproveToDisplay(HLERequestContext& ctx) { | ||
| 593 | LOG_WARNING(Service_AM, "(STUBBED) called"); | ||
| 594 | |||
| 595 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 596 | rb.Push(ResultSuccess); | ||
| 597 | } | ||
| 598 | |||
| 519 | void ISelfController::SetIdleTimeDetectionExtension(HLERequestContext& ctx) { | 599 | void ISelfController::SetIdleTimeDetectionExtension(HLERequestContext& ctx) { |
| 520 | IPC::RequestParser rp{ctx}; | 600 | IPC::RequestParser rp{ctx}; |
| 521 | idle_time_detection_extension = rp.Pop<u32>(); | 601 | idle_time_detection_extension = rp.Pop<u32>(); |
| @@ -684,9 +764,70 @@ void AppletMessageQueue::OperationModeChanged() { | |||
| 684 | on_operation_mode_changed->Signal(); | 764 | on_operation_mode_changed->Signal(); |
| 685 | } | 765 | } |
| 686 | 766 | ||
| 767 | ILockAccessor::ILockAccessor(Core::System& system_) | ||
| 768 | : ServiceFramework{system_, "ILockAccessor"}, service_context{system_, "ILockAccessor"} { | ||
| 769 | // clang-format off | ||
| 770 | static const FunctionInfo functions[] = { | ||
| 771 | {1, &ILockAccessor::TryLock, "TryLock"}, | ||
| 772 | {2, &ILockAccessor::Unlock, "Unlock"}, | ||
| 773 | {3, &ILockAccessor::GetEvent, "GetEvent"}, | ||
| 774 | {4,&ILockAccessor::IsLocked, "IsLocked"}, | ||
| 775 | }; | ||
| 776 | // clang-format on | ||
| 777 | |||
| 778 | RegisterHandlers(functions); | ||
| 779 | |||
| 780 | lock_event = service_context.CreateEvent("ILockAccessor::LockEvent"); | ||
| 781 | } | ||
| 782 | |||
| 783 | ILockAccessor::~ILockAccessor() = default; | ||
| 784 | |||
| 785 | void ILockAccessor::TryLock(HLERequestContext& ctx) { | ||
| 786 | IPC::RequestParser rp{ctx}; | ||
| 787 | const auto return_handle = rp.Pop<bool>(); | ||
| 788 | |||
| 789 | LOG_WARNING(Service_AM, "(STUBBED) called, return_handle={}", return_handle); | ||
| 790 | |||
| 791 | // TODO: When return_handle is true this function should return the lock handle | ||
| 792 | |||
| 793 | is_locked = true; | ||
| 794 | |||
| 795 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 796 | rb.Push(ResultSuccess); | ||
| 797 | rb.Push<u8>(is_locked); | ||
| 798 | } | ||
| 799 | |||
| 800 | void ILockAccessor::Unlock(HLERequestContext& ctx) { | ||
| 801 | LOG_INFO(Service_AM, "called"); | ||
| 802 | |||
| 803 | is_locked = false; | ||
| 804 | |||
| 805 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 806 | rb.Push(ResultSuccess); | ||
| 807 | } | ||
| 808 | |||
| 809 | void ILockAccessor::GetEvent(HLERequestContext& ctx) { | ||
| 810 | LOG_INFO(Service_AM, "called"); | ||
| 811 | |||
| 812 | lock_event->Signal(); | ||
| 813 | |||
| 814 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 815 | rb.Push(ResultSuccess); | ||
| 816 | rb.PushCopyObjects(lock_event->GetReadableEvent()); | ||
| 817 | } | ||
| 818 | |||
| 819 | void ILockAccessor::IsLocked(HLERequestContext& ctx) { | ||
| 820 | LOG_INFO(Service_AM, "called"); | ||
| 821 | |||
| 822 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 823 | rb.Push(ResultSuccess); | ||
| 824 | rb.Push<u8>(is_locked); | ||
| 825 | } | ||
| 826 | |||
| 687 | ICommonStateGetter::ICommonStateGetter(Core::System& system_, | 827 | ICommonStateGetter::ICommonStateGetter(Core::System& system_, |
| 688 | std::shared_ptr<AppletMessageQueue> msg_queue_) | 828 | std::shared_ptr<AppletMessageQueue> msg_queue_) |
| 689 | : ServiceFramework{system_, "ICommonStateGetter"}, msg_queue{std::move(msg_queue_)} { | 829 | : ServiceFramework{system_, "ICommonStateGetter"}, msg_queue{std::move(msg_queue_)}, |
| 830 | service_context{system_, "ICommonStateGetter"} { | ||
| 690 | // clang-format off | 831 | // clang-format off |
| 691 | static const FunctionInfo functions[] = { | 832 | static const FunctionInfo functions[] = { |
| 692 | {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"}, | 833 | {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"}, |
| @@ -699,14 +840,14 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, | |||
| 699 | {7, nullptr, "GetCradleStatus"}, | 840 | {7, nullptr, "GetCradleStatus"}, |
| 700 | {8, &ICommonStateGetter::GetBootMode, "GetBootMode"}, | 841 | {8, &ICommonStateGetter::GetBootMode, "GetBootMode"}, |
| 701 | {9, &ICommonStateGetter::GetCurrentFocusState, "GetCurrentFocusState"}, | 842 | {9, &ICommonStateGetter::GetCurrentFocusState, "GetCurrentFocusState"}, |
| 702 | {10, nullptr, "RequestToAcquireSleepLock"}, | 843 | {10, &ICommonStateGetter::RequestToAcquireSleepLock, "RequestToAcquireSleepLock"}, |
| 703 | {11, nullptr, "ReleaseSleepLock"}, | 844 | {11, nullptr, "ReleaseSleepLock"}, |
| 704 | {12, nullptr, "ReleaseSleepLockTransiently"}, | 845 | {12, nullptr, "ReleaseSleepLockTransiently"}, |
| 705 | {13, nullptr, "GetAcquiredSleepLockEvent"}, | 846 | {13, &ICommonStateGetter::GetAcquiredSleepLockEvent, "GetAcquiredSleepLockEvent"}, |
| 706 | {14, nullptr, "GetWakeupCount"}, | 847 | {14, nullptr, "GetWakeupCount"}, |
| 707 | {20, nullptr, "PushToGeneralChannel"}, | 848 | {20, nullptr, "PushToGeneralChannel"}, |
| 708 | {30, nullptr, "GetHomeButtonReaderLockAccessor"}, | 849 | {30, nullptr, "GetHomeButtonReaderLockAccessor"}, |
| 709 | {31, nullptr, "GetReaderLockAccessorEx"}, | 850 | {31, &ICommonStateGetter::GetReaderLockAccessorEx, "GetReaderLockAccessorEx"}, |
| 710 | {32, nullptr, "GetWriterLockAccessorEx"}, | 851 | {32, nullptr, "GetWriterLockAccessorEx"}, |
| 711 | {40, nullptr, "GetCradleFwVersion"}, | 852 | {40, nullptr, "GetCradleFwVersion"}, |
| 712 | {50, &ICommonStateGetter::IsVrModeEnabled, "IsVrModeEnabled"}, | 853 | {50, &ICommonStateGetter::IsVrModeEnabled, "IsVrModeEnabled"}, |
| @@ -724,7 +865,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, | |||
| 724 | {65, nullptr, "GetApplicationIdByContentActionName"}, | 865 | {65, nullptr, "GetApplicationIdByContentActionName"}, |
| 725 | {66, &ICommonStateGetter::SetCpuBoostMode, "SetCpuBoostMode"}, | 866 | {66, &ICommonStateGetter::SetCpuBoostMode, "SetCpuBoostMode"}, |
| 726 | {67, nullptr, "CancelCpuBoostMode"}, | 867 | {67, nullptr, "CancelCpuBoostMode"}, |
| 727 | {68, nullptr, "GetBuiltInDisplayType"}, | 868 | {68, &ICommonStateGetter::GetBuiltInDisplayType, "GetBuiltInDisplayType"}, |
| 728 | {80, &ICommonStateGetter::PerformSystemButtonPressingIfInFocus, "PerformSystemButtonPressingIfInFocus"}, | 869 | {80, &ICommonStateGetter::PerformSystemButtonPressingIfInFocus, "PerformSystemButtonPressingIfInFocus"}, |
| 729 | {90, nullptr, "SetPerformanceConfigurationChangedNotification"}, | 870 | {90, nullptr, "SetPerformanceConfigurationChangedNotification"}, |
| 730 | {91, nullptr, "GetCurrentPerformanceConfiguration"}, | 871 | {91, nullptr, "GetCurrentPerformanceConfiguration"}, |
| @@ -745,6 +886,8 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, | |||
| 745 | 886 | ||
| 746 | RegisterHandlers(functions); | 887 | RegisterHandlers(functions); |
| 747 | 888 | ||
| 889 | sleep_lock_event = service_context.CreateEvent("ICommonStateGetter::SleepLockEvent"); | ||
| 890 | |||
| 748 | // Configure applets to be in foreground state | 891 | // Configure applets to be in foreground state |
| 749 | msg_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); | 892 | msg_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); |
| 750 | msg_queue->PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground); | 893 | msg_queue->PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground); |
| @@ -793,6 +936,36 @@ void ICommonStateGetter::GetCurrentFocusState(HLERequestContext& ctx) { | |||
| 793 | rb.Push(static_cast<u8>(FocusState::InFocus)); | 936 | rb.Push(static_cast<u8>(FocusState::InFocus)); |
| 794 | } | 937 | } |
| 795 | 938 | ||
| 939 | void ICommonStateGetter::RequestToAcquireSleepLock(HLERequestContext& ctx) { | ||
| 940 | LOG_WARNING(Service_AM, "(STUBBED) called"); | ||
| 941 | |||
| 942 | // Sleep lock is acquired immediately. | ||
| 943 | sleep_lock_event->Signal(); | ||
| 944 | |||
| 945 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 946 | rb.Push(ResultSuccess); | ||
| 947 | } | ||
| 948 | |||
| 949 | void ICommonStateGetter::GetReaderLockAccessorEx(HLERequestContext& ctx) { | ||
| 950 | IPC::RequestParser rp{ctx}; | ||
| 951 | const auto unknown = rp.Pop<u32>(); | ||
| 952 | |||
| 953 | LOG_INFO(Service_AM, "called, unknown={}", unknown); | ||
| 954 | |||
| 955 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 956 | |||
| 957 | rb.Push(ResultSuccess); | ||
| 958 | rb.PushIpcInterface<ILockAccessor>(system); | ||
| 959 | } | ||
| 960 | |||
| 961 | void ICommonStateGetter::GetAcquiredSleepLockEvent(HLERequestContext& ctx) { | ||
| 962 | LOG_WARNING(Service_AM, "called"); | ||
| 963 | |||
| 964 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 965 | rb.Push(ResultSuccess); | ||
| 966 | rb.PushCopyObjects(sleep_lock_event->GetReadableEvent()); | ||
| 967 | } | ||
| 968 | |||
| 796 | void ICommonStateGetter::IsVrModeEnabled(HLERequestContext& ctx) { | 969 | void ICommonStateGetter::IsVrModeEnabled(HLERequestContext& ctx) { |
| 797 | LOG_DEBUG(Service_AM, "called"); | 970 | LOG_DEBUG(Service_AM, "called"); |
| 798 | 971 | ||
| @@ -869,6 +1042,14 @@ void ICommonStateGetter::SetCpuBoostMode(HLERequestContext& ctx) { | |||
| 869 | apm_sys->SetCpuBoostMode(ctx); | 1042 | apm_sys->SetCpuBoostMode(ctx); |
| 870 | } | 1043 | } |
| 871 | 1044 | ||
| 1045 | void ICommonStateGetter::GetBuiltInDisplayType(HLERequestContext& ctx) { | ||
| 1046 | LOG_WARNING(Service_AM, "(STUBBED) called"); | ||
| 1047 | |||
| 1048 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 1049 | rb.Push(ResultSuccess); | ||
| 1050 | rb.Push(0); | ||
| 1051 | } | ||
| 1052 | |||
| 872 | void ICommonStateGetter::PerformSystemButtonPressingIfInFocus(HLERequestContext& ctx) { | 1053 | void ICommonStateGetter::PerformSystemButtonPressingIfInFocus(HLERequestContext& ctx) { |
| 873 | IPC::RequestParser rp{ctx}; | 1054 | IPC::RequestParser rp{ctx}; |
| 874 | const auto system_button{rp.PopEnum<SystemButtonType>()}; | 1055 | const auto system_button{rp.PopEnum<SystemButtonType>()}; |
| @@ -1385,7 +1566,19 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_) | |||
| 1385 | // clang-format on | 1566 | // clang-format on |
| 1386 | RegisterHandlers(functions); | 1567 | RegisterHandlers(functions); |
| 1387 | 1568 | ||
| 1388 | PushInShowMiiEditData(); | 1569 | switch (system.GetAppletManager().GetCurrentAppletId()) { |
| 1570 | case Applets::AppletId::Cabinet: | ||
| 1571 | PushInShowCabinetData(); | ||
| 1572 | break; | ||
| 1573 | case Applets::AppletId::MiiEdit: | ||
| 1574 | PushInShowMiiEditData(); | ||
| 1575 | break; | ||
| 1576 | case Applets::AppletId::PhotoViewer: | ||
| 1577 | PushInShowAlbum(); | ||
| 1578 | break; | ||
| 1579 | default: | ||
| 1580 | break; | ||
| 1581 | } | ||
| 1389 | } | 1582 | } |
| 1390 | 1583 | ||
| 1391 | ILibraryAppletSelfAccessor::~ILibraryAppletSelfAccessor() = default; | 1584 | ILibraryAppletSelfAccessor::~ILibraryAppletSelfAccessor() = default; |
| @@ -1431,7 +1624,7 @@ void ILibraryAppletSelfAccessor::GetLibraryAppletInfo(HLERequestContext& ctx) { | |||
| 1431 | LOG_WARNING(Service_AM, "(STUBBED) called"); | 1624 | LOG_WARNING(Service_AM, "(STUBBED) called"); |
| 1432 | 1625 | ||
| 1433 | const LibraryAppletInfo applet_info{ | 1626 | const LibraryAppletInfo applet_info{ |
| 1434 | .applet_id = Applets::AppletId::MiiEdit, | 1627 | .applet_id = system.GetAppletManager().GetCurrentAppletId(), |
| 1435 | .library_applet_mode = Applets::LibraryAppletMode::AllForeground, | 1628 | .library_applet_mode = Applets::LibraryAppletMode::AllForeground, |
| 1436 | }; | 1629 | }; |
| 1437 | 1630 | ||
| @@ -1459,6 +1652,52 @@ void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext& | |||
| 1459 | rb.PushRaw(applet_info); | 1652 | rb.PushRaw(applet_info); |
| 1460 | } | 1653 | } |
| 1461 | 1654 | ||
| 1655 | void ILibraryAppletSelfAccessor::PushInShowAlbum() { | ||
| 1656 | const Applets::CommonArguments arguments{ | ||
| 1657 | .arguments_version = Applets::CommonArgumentVersion::Version3, | ||
| 1658 | .size = Applets::CommonArgumentSize::Version3, | ||
| 1659 | .library_version = 1, | ||
| 1660 | .theme_color = Applets::ThemeColor::BasicBlack, | ||
| 1661 | .play_startup_sound = true, | ||
| 1662 | .system_tick = system.CoreTiming().GetClockTicks(), | ||
| 1663 | }; | ||
| 1664 | |||
| 1665 | std::vector<u8> argument_data(sizeof(arguments)); | ||
| 1666 | std::vector<u8> settings_data{2}; | ||
| 1667 | std::memcpy(argument_data.data(), &arguments, sizeof(arguments)); | ||
| 1668 | queue_data.emplace_back(std::move(argument_data)); | ||
| 1669 | queue_data.emplace_back(std::move(settings_data)); | ||
| 1670 | } | ||
| 1671 | |||
| 1672 | void ILibraryAppletSelfAccessor::PushInShowCabinetData() { | ||
| 1673 | const Applets::CommonArguments arguments{ | ||
| 1674 | .arguments_version = Applets::CommonArgumentVersion::Version3, | ||
| 1675 | .size = Applets::CommonArgumentSize::Version3, | ||
| 1676 | .library_version = static_cast<u32>(Applets::CabinetAppletVersion::Version1), | ||
| 1677 | .theme_color = Applets::ThemeColor::BasicBlack, | ||
| 1678 | .play_startup_sound = true, | ||
| 1679 | .system_tick = system.CoreTiming().GetClockTicks(), | ||
| 1680 | }; | ||
| 1681 | |||
| 1682 | const Applets::StartParamForAmiiboSettings amiibo_settings{ | ||
| 1683 | .param_1 = 0, | ||
| 1684 | .applet_mode = system.GetAppletManager().GetCabinetMode(), | ||
| 1685 | .flags = Applets::CabinetFlags::None, | ||
| 1686 | .amiibo_settings_1 = 0, | ||
| 1687 | .device_handle = 0, | ||
| 1688 | .tag_info{}, | ||
| 1689 | .register_info{}, | ||
| 1690 | .amiibo_settings_3{}, | ||
| 1691 | }; | ||
| 1692 | |||
| 1693 | std::vector<u8> argument_data(sizeof(arguments)); | ||
| 1694 | std::vector<u8> settings_data(sizeof(amiibo_settings)); | ||
| 1695 | std::memcpy(argument_data.data(), &arguments, sizeof(arguments)); | ||
| 1696 | std::memcpy(settings_data.data(), &amiibo_settings, sizeof(amiibo_settings)); | ||
| 1697 | queue_data.emplace_back(std::move(argument_data)); | ||
| 1698 | queue_data.emplace_back(std::move(settings_data)); | ||
| 1699 | } | ||
| 1700 | |||
| 1462 | void ILibraryAppletSelfAccessor::PushInShowMiiEditData() { | 1701 | void ILibraryAppletSelfAccessor::PushInShowMiiEditData() { |
| 1463 | struct MiiEditV3 { | 1702 | struct MiiEditV3 { |
| 1464 | Applets::MiiEditAppletInputCommon common; | 1703 | Applets::MiiEditAppletInputCommon common; |
| @@ -2235,7 +2474,7 @@ void IProcessWindingController::GetLaunchReason(HLERequestContext& ctx) { | |||
| 2235 | } | 2474 | } |
| 2236 | 2475 | ||
| 2237 | void IProcessWindingController::OpenCallingLibraryApplet(HLERequestContext& ctx) { | 2476 | void IProcessWindingController::OpenCallingLibraryApplet(HLERequestContext& ctx) { |
| 2238 | const auto applet_id = Applets::AppletId::MiiEdit; | 2477 | const auto applet_id = system.GetAppletManager().GetCurrentAppletId(); |
| 2239 | const auto applet_mode = Applets::LibraryAppletMode::AllForeground; | 2478 | const auto applet_mode = Applets::LibraryAppletMode::AllForeground; |
| 2240 | 2479 | ||
| 2241 | LOG_WARNING(Service_AM, "(STUBBED) called with applet_id={:08X}, applet_mode={:08X}", applet_id, | 2480 | LOG_WARNING(Service_AM, "(STUBBED) called with applet_id={:08X}, applet_mode={:08X}", applet_id, |
| @@ -2256,4 +2495,5 @@ void IProcessWindingController::OpenCallingLibraryApplet(HLERequestContext& ctx) | |||
| 2256 | rb.Push(ResultSuccess); | 2495 | rb.Push(ResultSuccess); |
| 2257 | rb.PushIpcInterface<ILibraryAppletAccessor>(system, applet); | 2496 | rb.PushIpcInterface<ILibraryAppletAccessor>(system, applet); |
| 2258 | } | 2497 | } |
| 2498 | |||
| 2259 | } // namespace Service::AM | 2499 | } // namespace Service::AM |
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 5b97eb5e3..4a045cfd4 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h | |||
| @@ -122,7 +122,10 @@ public: | |||
| 122 | ~IDisplayController() override; | 122 | ~IDisplayController() override; |
| 123 | 123 | ||
| 124 | private: | 124 | private: |
| 125 | void GetCallerAppletCaptureImageEx(HLERequestContext& ctx); | ||
| 125 | void TakeScreenShotOfOwnLayer(HLERequestContext& ctx); | 126 | void TakeScreenShotOfOwnLayer(HLERequestContext& ctx); |
| 127 | void AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx); | ||
| 128 | void ReleaseCallerAppletCaptureSharedBuffer(HLERequestContext& ctx); | ||
| 126 | }; | 129 | }; |
| 127 | 130 | ||
| 128 | class IDebugFunctions final : public ServiceFramework<IDebugFunctions> { | 131 | class IDebugFunctions final : public ServiceFramework<IDebugFunctions> { |
| @@ -150,9 +153,13 @@ private: | |||
| 150 | void SetRestartMessageEnabled(HLERequestContext& ctx); | 153 | void SetRestartMessageEnabled(HLERequestContext& ctx); |
| 151 | void SetOutOfFocusSuspendingEnabled(HLERequestContext& ctx); | 154 | void SetOutOfFocusSuspendingEnabled(HLERequestContext& ctx); |
| 152 | void SetAlbumImageOrientation(HLERequestContext& ctx); | 155 | void SetAlbumImageOrientation(HLERequestContext& ctx); |
| 156 | void IsSystemBufferSharingEnabled(HLERequestContext& ctx); | ||
| 157 | void GetSystemSharedBufferHandle(HLERequestContext& ctx); | ||
| 158 | void GetSystemSharedLayerHandle(HLERequestContext& ctx); | ||
| 153 | void CreateManagedDisplayLayer(HLERequestContext& ctx); | 159 | void CreateManagedDisplayLayer(HLERequestContext& ctx); |
| 154 | void CreateManagedDisplaySeparableLayer(HLERequestContext& ctx); | 160 | void CreateManagedDisplaySeparableLayer(HLERequestContext& ctx); |
| 155 | void SetHandlesRequestToDisplay(HLERequestContext& ctx); | 161 | void SetHandlesRequestToDisplay(HLERequestContext& ctx); |
| 162 | void ApproveToDisplay(HLERequestContext& ctx); | ||
| 156 | void SetIdleTimeDetectionExtension(HLERequestContext& ctx); | 163 | void SetIdleTimeDetectionExtension(HLERequestContext& ctx); |
| 157 | void GetIdleTimeDetectionExtension(HLERequestContext& ctx); | 164 | void GetIdleTimeDetectionExtension(HLERequestContext& ctx); |
| 158 | void ReportUserIsActive(HLERequestContext& ctx); | 165 | void ReportUserIsActive(HLERequestContext& ctx); |
| @@ -164,6 +171,8 @@ private: | |||
| 164 | void SaveCurrentScreenshot(HLERequestContext& ctx); | 171 | void SaveCurrentScreenshot(HLERequestContext& ctx); |
| 165 | void SetRecordVolumeMuted(HLERequestContext& ctx); | 172 | void SetRecordVolumeMuted(HLERequestContext& ctx); |
| 166 | 173 | ||
| 174 | Result EnsureBufferSharingEnabled(); | ||
| 175 | |||
| 167 | enum class ScreenshotPermission : u32 { | 176 | enum class ScreenshotPermission : u32 { |
| 168 | Inherit = 0, | 177 | Inherit = 0, |
| 169 | Enable = 1, | 178 | Enable = 1, |
| @@ -179,10 +188,30 @@ private: | |||
| 179 | 188 | ||
| 180 | u32 idle_time_detection_extension = 0; | 189 | u32 idle_time_detection_extension = 0; |
| 181 | u64 num_fatal_sections_entered = 0; | 190 | u64 num_fatal_sections_entered = 0; |
| 191 | u64 system_shared_buffer_id = 0; | ||
| 192 | u64 system_shared_layer_id = 0; | ||
| 182 | bool is_auto_sleep_disabled = false; | 193 | bool is_auto_sleep_disabled = false; |
| 194 | bool buffer_sharing_enabled = false; | ||
| 183 | ScreenshotPermission screenshot_permission = ScreenshotPermission::Inherit; | 195 | ScreenshotPermission screenshot_permission = ScreenshotPermission::Inherit; |
| 184 | }; | 196 | }; |
| 185 | 197 | ||
| 198 | class ILockAccessor final : public ServiceFramework<ILockAccessor> { | ||
| 199 | public: | ||
| 200 | explicit ILockAccessor(Core::System& system_); | ||
| 201 | ~ILockAccessor() override; | ||
| 202 | |||
| 203 | private: | ||
| 204 | void TryLock(HLERequestContext& ctx); | ||
| 205 | void Unlock(HLERequestContext& ctx); | ||
| 206 | void GetEvent(HLERequestContext& ctx); | ||
| 207 | void IsLocked(HLERequestContext& ctx); | ||
| 208 | |||
| 209 | bool is_locked{}; | ||
| 210 | |||
| 211 | Kernel::KEvent* lock_event; | ||
| 212 | KernelHelpers::ServiceContext service_context; | ||
| 213 | }; | ||
| 214 | |||
| 186 | class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { | 215 | class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { |
| 187 | public: | 216 | public: |
| 188 | explicit ICommonStateGetter(Core::System& system_, | 217 | explicit ICommonStateGetter(Core::System& system_, |
| @@ -223,6 +252,9 @@ private: | |||
| 223 | void GetEventHandle(HLERequestContext& ctx); | 252 | void GetEventHandle(HLERequestContext& ctx); |
| 224 | void ReceiveMessage(HLERequestContext& ctx); | 253 | void ReceiveMessage(HLERequestContext& ctx); |
| 225 | void GetCurrentFocusState(HLERequestContext& ctx); | 254 | void GetCurrentFocusState(HLERequestContext& ctx); |
| 255 | void RequestToAcquireSleepLock(HLERequestContext& ctx); | ||
| 256 | void GetAcquiredSleepLockEvent(HLERequestContext& ctx); | ||
| 257 | void GetReaderLockAccessorEx(HLERequestContext& ctx); | ||
| 226 | void GetDefaultDisplayResolutionChangeEvent(HLERequestContext& ctx); | 258 | void GetDefaultDisplayResolutionChangeEvent(HLERequestContext& ctx); |
| 227 | void GetOperationMode(HLERequestContext& ctx); | 259 | void GetOperationMode(HLERequestContext& ctx); |
| 228 | void GetPerformanceMode(HLERequestContext& ctx); | 260 | void GetPerformanceMode(HLERequestContext& ctx); |
| @@ -234,12 +266,15 @@ private: | |||
| 234 | void EndVrModeEx(HLERequestContext& ctx); | 266 | void EndVrModeEx(HLERequestContext& ctx); |
| 235 | void GetDefaultDisplayResolution(HLERequestContext& ctx); | 267 | void GetDefaultDisplayResolution(HLERequestContext& ctx); |
| 236 | void SetCpuBoostMode(HLERequestContext& ctx); | 268 | void SetCpuBoostMode(HLERequestContext& ctx); |
| 269 | void GetBuiltInDisplayType(HLERequestContext& ctx); | ||
| 237 | void PerformSystemButtonPressingIfInFocus(HLERequestContext& ctx); | 270 | void PerformSystemButtonPressingIfInFocus(HLERequestContext& ctx); |
| 238 | void GetSettingsPlatformRegion(HLERequestContext& ctx); | 271 | void GetSettingsPlatformRegion(HLERequestContext& ctx); |
| 239 | void SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(HLERequestContext& ctx); | 272 | void SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(HLERequestContext& ctx); |
| 240 | 273 | ||
| 241 | std::shared_ptr<AppletMessageQueue> msg_queue; | 274 | std::shared_ptr<AppletMessageQueue> msg_queue; |
| 242 | bool vr_mode_state{}; | 275 | bool vr_mode_state{}; |
| 276 | Kernel::KEvent* sleep_lock_event; | ||
| 277 | KernelHelpers::ServiceContext service_context; | ||
| 243 | }; | 278 | }; |
| 244 | 279 | ||
| 245 | class IStorageImpl { | 280 | class IStorageImpl { |
| @@ -311,6 +346,8 @@ private: | |||
| 311 | void ExitProcessAndReturn(HLERequestContext& ctx); | 346 | void ExitProcessAndReturn(HLERequestContext& ctx); |
| 312 | void GetCallerAppletIdentityInfo(HLERequestContext& ctx); | 347 | void GetCallerAppletIdentityInfo(HLERequestContext& ctx); |
| 313 | 348 | ||
| 349 | void PushInShowAlbum(); | ||
| 350 | void PushInShowCabinetData(); | ||
| 314 | void PushInShowMiiEditData(); | 351 | void PushInShowMiiEditData(); |
| 315 | 352 | ||
| 316 | std::deque<std::vector<u8>> queue_data; | 353 | std::deque<std::vector<u8>> queue_data; |
diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp index eb12312cc..e30e6478a 100644 --- a/src/core/hle/service/am/applet_ae.cpp +++ b/src/core/hle/service/am/applet_ae.cpp | |||
| @@ -28,8 +28,8 @@ public: | |||
| 28 | {11, &ILibraryAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"}, | 28 | {11, &ILibraryAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"}, |
| 29 | {20, &ILibraryAppletProxy::OpenLibraryAppletSelfAccessor, "OpenLibraryAppletSelfAccessor"}, | 29 | {20, &ILibraryAppletProxy::OpenLibraryAppletSelfAccessor, "OpenLibraryAppletSelfAccessor"}, |
| 30 | {21, &ILibraryAppletProxy::GetAppletCommonFunctions, "GetAppletCommonFunctions"}, | 30 | {21, &ILibraryAppletProxy::GetAppletCommonFunctions, "GetAppletCommonFunctions"}, |
| 31 | {22, nullptr, "GetHomeMenuFunctions"}, | 31 | {22, &ILibraryAppletProxy::GetHomeMenuFunctions, "GetHomeMenuFunctions"}, |
| 32 | {23, nullptr, "GetGlobalStateController"}, | 32 | {23, &ILibraryAppletProxy::GetGlobalStateController, "GetGlobalStateController"}, |
| 33 | {1000, &ILibraryAppletProxy::GetDebugFunctions, "GetDebugFunctions"}, | 33 | {1000, &ILibraryAppletProxy::GetDebugFunctions, "GetDebugFunctions"}, |
| 34 | }; | 34 | }; |
| 35 | // clang-format on | 35 | // clang-format on |
| @@ -110,6 +110,22 @@ private: | |||
| 110 | rb.PushIpcInterface<IAppletCommonFunctions>(system); | 110 | rb.PushIpcInterface<IAppletCommonFunctions>(system); |
| 111 | } | 111 | } |
| 112 | 112 | ||
| 113 | void GetHomeMenuFunctions(HLERequestContext& ctx) { | ||
| 114 | LOG_DEBUG(Service_AM, "called"); | ||
| 115 | |||
| 116 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 117 | rb.Push(ResultSuccess); | ||
| 118 | rb.PushIpcInterface<IHomeMenuFunctions>(system); | ||
| 119 | } | ||
| 120 | |||
| 121 | void GetGlobalStateController(HLERequestContext& ctx) { | ||
| 122 | LOG_DEBUG(Service_AM, "called"); | ||
| 123 | |||
| 124 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 125 | rb.Push(ResultSuccess); | ||
| 126 | rb.PushIpcInterface<IGlobalStateController>(system); | ||
| 127 | } | ||
| 128 | |||
| 113 | void GetDebugFunctions(HLERequestContext& ctx) { | 129 | void GetDebugFunctions(HLERequestContext& ctx) { |
| 114 | LOG_DEBUG(Service_AM, "called"); | 130 | LOG_DEBUG(Service_AM, "called"); |
| 115 | 131 | ||
diff --git a/src/core/hle/service/am/applets/applet_cabinet.h b/src/core/hle/service/am/applets/applet_cabinet.h index b56427021..f498796f7 100644 --- a/src/core/hle/service/am/applets/applet_cabinet.h +++ b/src/core/hle/service/am/applets/applet_cabinet.h | |||
| @@ -29,6 +29,15 @@ enum class CabinetAppletVersion : u32 { | |||
| 29 | Version1 = 0x1, | 29 | Version1 = 0x1, |
| 30 | }; | 30 | }; |
| 31 | 31 | ||
| 32 | enum class CabinetFlags : u8 { | ||
| 33 | None = 0, | ||
| 34 | DeviceHandle = 1 << 0, | ||
| 35 | TagInfo = 1 << 1, | ||
| 36 | RegisterInfo = 1 << 2, | ||
| 37 | All = DeviceHandle | TagInfo | RegisterInfo, | ||
| 38 | }; | ||
| 39 | DECLARE_ENUM_FLAG_OPERATORS(CabinetFlags) | ||
| 40 | |||
| 32 | enum class CabinetResult : u8 { | 41 | enum class CabinetResult : u8 { |
| 33 | Cancel = 0, | 42 | Cancel = 0, |
| 34 | TagInfo = 1 << 1, | 43 | TagInfo = 1 << 1, |
| @@ -51,7 +60,7 @@ static_assert(sizeof(AmiiboSettingsStartParam) == 0x30, | |||
| 51 | struct StartParamForAmiiboSettings { | 60 | struct StartParamForAmiiboSettings { |
| 52 | u8 param_1; | 61 | u8 param_1; |
| 53 | Service::NFP::CabinetMode applet_mode; | 62 | Service::NFP::CabinetMode applet_mode; |
| 54 | u8 flags; | 63 | CabinetFlags flags; |
| 55 | u8 amiibo_settings_1; | 64 | u8 amiibo_settings_1; |
| 56 | u64 device_handle; | 65 | u64 device_handle; |
| 57 | Service::NFP::TagInfo tag_info; | 66 | Service::NFP::TagInfo tag_info; |
diff --git a/src/core/hle/service/am/applets/applet_error.cpp b/src/core/hle/service/am/applets/applet_error.cpp index b46ea840c..5d17c353f 100644 --- a/src/core/hle/service/am/applets/applet_error.cpp +++ b/src/core/hle/service/am/applets/applet_error.cpp | |||
| @@ -138,6 +138,10 @@ void Error::Initialize() { | |||
| 138 | CopyArgumentData(data, args->application_error); | 138 | CopyArgumentData(data, args->application_error); |
| 139 | error_code = Result(args->application_error.error_code); | 139 | error_code = Result(args->application_error.error_code); |
| 140 | break; | 140 | break; |
| 141 | case ErrorAppletMode::ShowErrorPctl: | ||
| 142 | CopyArgumentData(data, args->error_record); | ||
| 143 | error_code = Decode64BitError(args->error_record.error_code_64); | ||
| 144 | break; | ||
| 141 | case ErrorAppletMode::ShowErrorRecord: | 145 | case ErrorAppletMode::ShowErrorRecord: |
| 142 | CopyArgumentData(data, args->error_record); | 146 | CopyArgumentData(data, args->error_record); |
| 143 | error_code = Decode64BitError(args->error_record.error_code_64); | 147 | error_code = Decode64BitError(args->error_record.error_code_64); |
| @@ -191,6 +195,7 @@ void Error::Execute() { | |||
| 191 | frontend.ShowCustomErrorText(error_code, main_text_string, detail_text_string, callback); | 195 | frontend.ShowCustomErrorText(error_code, main_text_string, detail_text_string, callback); |
| 192 | break; | 196 | break; |
| 193 | } | 197 | } |
| 198 | case ErrorAppletMode::ShowErrorPctl: | ||
| 194 | case ErrorAppletMode::ShowErrorRecord: | 199 | case ErrorAppletMode::ShowErrorRecord: |
| 195 | reporter.SaveErrorReport(title_id, error_code, | 200 | reporter.SaveErrorReport(title_id, error_code, |
| 196 | fmt::format("{:016X}", args->error_record.posix_time)); | 201 | fmt::format("{:016X}", args->error_record.posix_time)); |
diff --git a/src/core/hle/service/am/applets/applet_general_backend.cpp b/src/core/hle/service/am/applets/applet_general_backend.cpp index 8b352020e..c0032f652 100644 --- a/src/core/hle/service/am/applets/applet_general_backend.cpp +++ b/src/core/hle/service/am/applets/applet_general_backend.cpp | |||
| @@ -223,9 +223,9 @@ void StubApplet::Initialize() { | |||
| 223 | 223 | ||
| 224 | const auto data = broker.PeekDataToAppletForDebug(); | 224 | const auto data = broker.PeekDataToAppletForDebug(); |
| 225 | system.GetReporter().SaveUnimplementedAppletReport( | 225 | system.GetReporter().SaveUnimplementedAppletReport( |
| 226 | static_cast<u32>(id), common_args.arguments_version, common_args.library_version, | 226 | static_cast<u32>(id), static_cast<u32>(common_args.arguments_version), |
| 227 | common_args.theme_color, common_args.play_startup_sound, common_args.system_tick, | 227 | common_args.library_version, static_cast<u32>(common_args.theme_color), |
| 228 | data.normal, data.interactive); | 228 | common_args.play_startup_sound, common_args.system_tick, data.normal, data.interactive); |
| 229 | 229 | ||
| 230 | LogCurrentStorage(broker, "Initialize"); | 230 | LogCurrentStorage(broker, "Initialize"); |
| 231 | } | 231 | } |
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index 10afbc2da..89d5434af 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp | |||
| @@ -199,6 +199,14 @@ const AppletFrontendSet& AppletManager::GetAppletFrontendSet() const { | |||
| 199 | return frontend; | 199 | return frontend; |
| 200 | } | 200 | } |
| 201 | 201 | ||
| 202 | NFP::CabinetMode AppletManager::GetCabinetMode() const { | ||
| 203 | return cabinet_mode; | ||
| 204 | } | ||
| 205 | |||
| 206 | AppletId AppletManager::GetCurrentAppletId() const { | ||
| 207 | return current_applet_id; | ||
| 208 | } | ||
| 209 | |||
| 202 | void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) { | 210 | void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) { |
| 203 | if (set.cabinet != nullptr) { | 211 | if (set.cabinet != nullptr) { |
| 204 | frontend.cabinet = std::move(set.cabinet); | 212 | frontend.cabinet = std::move(set.cabinet); |
| @@ -237,6 +245,14 @@ void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) { | |||
| 237 | } | 245 | } |
| 238 | } | 246 | } |
| 239 | 247 | ||
| 248 | void AppletManager::SetCabinetMode(NFP::CabinetMode mode) { | ||
| 249 | cabinet_mode = mode; | ||
| 250 | } | ||
| 251 | |||
| 252 | void AppletManager::SetCurrentAppletId(AppletId applet_id) { | ||
| 253 | current_applet_id = applet_id; | ||
| 254 | } | ||
| 255 | |||
| 240 | void AppletManager::SetDefaultAppletFrontendSet() { | 256 | void AppletManager::SetDefaultAppletFrontendSet() { |
| 241 | ClearAll(); | 257 | ClearAll(); |
| 242 | SetDefaultAppletsIfMissing(); | 258 | SetDefaultAppletsIfMissing(); |
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index 12f374199..f02bbc450 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h | |||
| @@ -34,6 +34,10 @@ class KEvent; | |||
| 34 | class KReadableEvent; | 34 | class KReadableEvent; |
| 35 | } // namespace Kernel | 35 | } // namespace Kernel |
| 36 | 36 | ||
| 37 | namespace Service::NFP { | ||
| 38 | enum class CabinetMode : u8; | ||
| 39 | } // namespace Service::NFP | ||
| 40 | |||
| 37 | namespace Service::AM { | 41 | namespace Service::AM { |
| 38 | 42 | ||
| 39 | class IStorage; | 43 | class IStorage; |
| @@ -41,6 +45,8 @@ class IStorage; | |||
| 41 | namespace Applets { | 45 | namespace Applets { |
| 42 | 46 | ||
| 43 | enum class AppletId : u32 { | 47 | enum class AppletId : u32 { |
| 48 | None = 0x00, | ||
| 49 | Application = 0x01, | ||
| 44 | OverlayDisplay = 0x02, | 50 | OverlayDisplay = 0x02, |
| 45 | QLaunch = 0x03, | 51 | QLaunch = 0x03, |
| 46 | Starter = 0x04, | 52 | Starter = 0x04, |
| @@ -71,6 +77,32 @@ enum class LibraryAppletMode : u32 { | |||
| 71 | AllForegroundInitiallyHidden = 4, | 77 | AllForegroundInitiallyHidden = 4, |
| 72 | }; | 78 | }; |
| 73 | 79 | ||
| 80 | enum class CommonArgumentVersion : u32 { | ||
| 81 | Version0, | ||
| 82 | Version1, | ||
| 83 | Version2, | ||
| 84 | Version3, | ||
| 85 | }; | ||
| 86 | |||
| 87 | enum class CommonArgumentSize : u32 { | ||
| 88 | Version3 = 0x20, | ||
| 89 | }; | ||
| 90 | |||
| 91 | enum class ThemeColor : u32 { | ||
| 92 | BasicWhite = 0, | ||
| 93 | BasicBlack = 3, | ||
| 94 | }; | ||
| 95 | |||
| 96 | struct CommonArguments { | ||
| 97 | CommonArgumentVersion arguments_version; | ||
| 98 | CommonArgumentSize size; | ||
| 99 | u32 library_version; | ||
| 100 | ThemeColor theme_color; | ||
| 101 | bool play_startup_sound; | ||
| 102 | u64_le system_tick; | ||
| 103 | }; | ||
| 104 | static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size."); | ||
| 105 | |||
| 74 | class AppletDataBroker final { | 106 | class AppletDataBroker final { |
| 75 | public: | 107 | public: |
| 76 | explicit AppletDataBroker(Core::System& system_, LibraryAppletMode applet_mode_); | 108 | explicit AppletDataBroker(Core::System& system_, LibraryAppletMode applet_mode_); |
| @@ -161,16 +193,6 @@ public: | |||
| 161 | } | 193 | } |
| 162 | 194 | ||
| 163 | protected: | 195 | protected: |
| 164 | struct CommonArguments { | ||
| 165 | u32_le arguments_version; | ||
| 166 | u32_le size; | ||
| 167 | u32_le library_version; | ||
| 168 | u32_le theme_color; | ||
| 169 | bool play_startup_sound; | ||
| 170 | u64_le system_tick; | ||
| 171 | }; | ||
| 172 | static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size."); | ||
| 173 | |||
| 174 | CommonArguments common_args{}; | 196 | CommonArguments common_args{}; |
| 175 | AppletDataBroker broker; | 197 | AppletDataBroker broker; |
| 176 | LibraryAppletMode applet_mode; | 198 | LibraryAppletMode applet_mode; |
| @@ -219,8 +241,12 @@ public: | |||
| 219 | ~AppletManager(); | 241 | ~AppletManager(); |
| 220 | 242 | ||
| 221 | const AppletFrontendSet& GetAppletFrontendSet() const; | 243 | const AppletFrontendSet& GetAppletFrontendSet() const; |
| 244 | NFP::CabinetMode GetCabinetMode() const; | ||
| 245 | AppletId GetCurrentAppletId() const; | ||
| 222 | 246 | ||
| 223 | void SetAppletFrontendSet(AppletFrontendSet set); | 247 | void SetAppletFrontendSet(AppletFrontendSet set); |
| 248 | void SetCabinetMode(NFP::CabinetMode mode); | ||
| 249 | void SetCurrentAppletId(AppletId applet_id); | ||
| 224 | void SetDefaultAppletFrontendSet(); | 250 | void SetDefaultAppletFrontendSet(); |
| 225 | void SetDefaultAppletsIfMissing(); | 251 | void SetDefaultAppletsIfMissing(); |
| 226 | void ClearAll(); | 252 | void ClearAll(); |
| @@ -228,6 +254,9 @@ public: | |||
| 228 | std::shared_ptr<Applet> GetApplet(AppletId id, LibraryAppletMode mode) const; | 254 | std::shared_ptr<Applet> GetApplet(AppletId id, LibraryAppletMode mode) const; |
| 229 | 255 | ||
| 230 | private: | 256 | private: |
| 257 | AppletId current_applet_id{}; | ||
| 258 | NFP::CabinetMode cabinet_mode{}; | ||
| 259 | |||
| 231 | AppletFrontendSet frontend; | 260 | AppletFrontendSet frontend; |
| 232 | Core::System& system; | 261 | Core::System& system; |
| 233 | }; | 262 | }; |
diff --git a/src/core/hle/service/caps/caps.cpp b/src/core/hle/service/caps/caps.cpp index 610fe9940..31dd98140 100644 --- a/src/core/hle/service/caps/caps.cpp +++ b/src/core/hle/service/caps/caps.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | #include "core/hle/service/caps/caps.h" | 4 | #include "core/hle/service/caps/caps.h" |
| 5 | #include "core/hle/service/caps/caps_a.h" | 5 | #include "core/hle/service/caps/caps_a.h" |
| 6 | #include "core/hle/service/caps/caps_c.h" | 6 | #include "core/hle/service/caps/caps_c.h" |
| 7 | #include "core/hle/service/caps/caps_manager.h" | ||
| 7 | #include "core/hle/service/caps/caps_sc.h" | 8 | #include "core/hle/service/caps/caps_sc.h" |
| 8 | #include "core/hle/service/caps/caps_ss.h" | 9 | #include "core/hle/service/caps/caps_ss.h" |
| 9 | #include "core/hle/service/caps/caps_su.h" | 10 | #include "core/hle/service/caps/caps_su.h" |
| @@ -15,13 +16,21 @@ namespace Service::Capture { | |||
| 15 | 16 | ||
| 16 | void LoopProcess(Core::System& system) { | 17 | void LoopProcess(Core::System& system) { |
| 17 | auto server_manager = std::make_unique<ServerManager>(system); | 18 | auto server_manager = std::make_unique<ServerManager>(system); |
| 19 | auto album_manager = std::make_shared<AlbumManager>(system); | ||
| 20 | |||
| 21 | server_manager->RegisterNamedService( | ||
| 22 | "caps:a", std::make_shared<IAlbumAccessorService>(system, album_manager)); | ||
| 23 | server_manager->RegisterNamedService( | ||
| 24 | "caps:c", std::make_shared<IAlbumControlService>(system, album_manager)); | ||
| 25 | server_manager->RegisterNamedService( | ||
| 26 | "caps:u", std::make_shared<IAlbumApplicationService>(system, album_manager)); | ||
| 27 | |||
| 28 | server_manager->RegisterNamedService("caps:ss", std::make_shared<IScreenShotService>(system)); | ||
| 29 | server_manager->RegisterNamedService("caps:sc", | ||
| 30 | std::make_shared<IScreenShotControlService>(system)); | ||
| 31 | server_manager->RegisterNamedService("caps:su", | ||
| 32 | std::make_shared<IScreenShotApplicationService>(system)); | ||
| 18 | 33 | ||
| 19 | server_manager->RegisterNamedService("caps:a", std::make_shared<CAPS_A>(system)); | ||
| 20 | server_manager->RegisterNamedService("caps:c", std::make_shared<CAPS_C>(system)); | ||
| 21 | server_manager->RegisterNamedService("caps:u", std::make_shared<CAPS_U>(system)); | ||
| 22 | server_manager->RegisterNamedService("caps:sc", std::make_shared<CAPS_SC>(system)); | ||
| 23 | server_manager->RegisterNamedService("caps:ss", std::make_shared<CAPS_SS>(system)); | ||
| 24 | server_manager->RegisterNamedService("caps:su", std::make_shared<CAPS_SU>(system)); | ||
| 25 | ServerManager::RunServer(std::move(server_manager)); | 34 | ServerManager::RunServer(std::move(server_manager)); |
| 26 | } | 35 | } |
| 27 | 36 | ||
diff --git a/src/core/hle/service/caps/caps.h b/src/core/hle/service/caps/caps.h index 15f0ecfaa..58e9725b8 100644 --- a/src/core/hle/service/caps/caps.h +++ b/src/core/hle/service/caps/caps.h | |||
| @@ -3,93 +3,12 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include "common/common_funcs.h" | ||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | namespace Core { | 6 | namespace Core { |
| 10 | class System; | 7 | class System; |
| 11 | } | 8 | } |
| 12 | 9 | ||
| 13 | namespace Service::SM { | ||
| 14 | class ServiceManager; | ||
| 15 | } | ||
| 16 | |||
| 17 | namespace Service::Capture { | 10 | namespace Service::Capture { |
| 18 | 11 | ||
| 19 | enum class AlbumImageOrientation { | ||
| 20 | Orientation0 = 0, | ||
| 21 | Orientation1 = 1, | ||
| 22 | Orientation2 = 2, | ||
| 23 | Orientation3 = 3, | ||
| 24 | }; | ||
| 25 | |||
| 26 | enum class AlbumReportOption : s32 { | ||
| 27 | Disable = 0, | ||
| 28 | Enable = 1, | ||
| 29 | }; | ||
| 30 | |||
| 31 | enum class ContentType : u8 { | ||
| 32 | Screenshot = 0, | ||
| 33 | Movie = 1, | ||
| 34 | ExtraMovie = 3, | ||
| 35 | }; | ||
| 36 | |||
| 37 | enum class AlbumStorage : u8 { | ||
| 38 | NAND = 0, | ||
| 39 | SD = 1, | ||
| 40 | }; | ||
| 41 | |||
| 42 | struct AlbumFileDateTime { | ||
| 43 | s16 year{}; | ||
| 44 | s8 month{}; | ||
| 45 | s8 day{}; | ||
| 46 | s8 hour{}; | ||
| 47 | s8 minute{}; | ||
| 48 | s8 second{}; | ||
| 49 | s8 uid{}; | ||
| 50 | }; | ||
| 51 | static_assert(sizeof(AlbumFileDateTime) == 0x8, "AlbumFileDateTime has incorrect size."); | ||
| 52 | |||
| 53 | struct AlbumEntry { | ||
| 54 | u64 size{}; | ||
| 55 | u64 application_id{}; | ||
| 56 | AlbumFileDateTime datetime{}; | ||
| 57 | AlbumStorage storage{}; | ||
| 58 | ContentType content{}; | ||
| 59 | INSERT_PADDING_BYTES(6); | ||
| 60 | }; | ||
| 61 | static_assert(sizeof(AlbumEntry) == 0x20, "AlbumEntry has incorrect size."); | ||
| 62 | |||
| 63 | struct AlbumFileEntry { | ||
| 64 | u64 size{}; // Size of the entry | ||
| 65 | u64 hash{}; // AES256 with hardcoded key over AlbumEntry | ||
| 66 | AlbumFileDateTime datetime{}; | ||
| 67 | AlbumStorage storage{}; | ||
| 68 | ContentType content{}; | ||
| 69 | INSERT_PADDING_BYTES(5); | ||
| 70 | u8 unknown{1}; // Set to 1 on official SW | ||
| 71 | }; | ||
| 72 | static_assert(sizeof(AlbumFileEntry) == 0x20, "AlbumFileEntry has incorrect size."); | ||
| 73 | |||
| 74 | struct ApplicationAlbumEntry { | ||
| 75 | u64 size{}; // Size of the entry | ||
| 76 | u64 hash{}; // AES256 with hardcoded key over AlbumEntry | ||
| 77 | AlbumFileDateTime datetime{}; | ||
| 78 | AlbumStorage storage{}; | ||
| 79 | ContentType content{}; | ||
| 80 | INSERT_PADDING_BYTES(5); | ||
| 81 | u8 unknown{1}; // Set to 1 on official SW | ||
| 82 | }; | ||
| 83 | static_assert(sizeof(ApplicationAlbumEntry) == 0x20, "ApplicationAlbumEntry has incorrect size."); | ||
| 84 | |||
| 85 | struct ApplicationAlbumFileEntry { | ||
| 86 | ApplicationAlbumEntry entry{}; | ||
| 87 | AlbumFileDateTime datetime{}; | ||
| 88 | u64 unknown{}; | ||
| 89 | }; | ||
| 90 | static_assert(sizeof(ApplicationAlbumFileEntry) == 0x30, | ||
| 91 | "ApplicationAlbumFileEntry has incorrect size."); | ||
| 92 | |||
| 93 | void LoopProcess(Core::System& system); | 12 | void LoopProcess(Core::System& system); |
| 94 | 13 | ||
| 95 | } // namespace Service::Capture | 14 | } // namespace Service::Capture |
diff --git a/src/core/hle/service/caps/caps_a.cpp b/src/core/hle/service/caps/caps_a.cpp index 44267b284..9925720a3 100644 --- a/src/core/hle/service/caps/caps_a.cpp +++ b/src/core/hle/service/caps/caps_a.cpp | |||
| @@ -1,40 +1,26 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "common/logging/log.h" | ||
| 4 | #include "core/hle/service/caps/caps_a.h" | 5 | #include "core/hle/service/caps/caps_a.h" |
| 6 | #include "core/hle/service/caps/caps_manager.h" | ||
| 7 | #include "core/hle/service/caps/caps_result.h" | ||
| 8 | #include "core/hle/service/caps/caps_types.h" | ||
| 9 | #include "core/hle/service/ipc_helpers.h" | ||
| 5 | 10 | ||
| 6 | namespace Service::Capture { | 11 | namespace Service::Capture { |
| 7 | 12 | ||
| 8 | class IAlbumAccessorSession final : public ServiceFramework<IAlbumAccessorSession> { | 13 | IAlbumAccessorService::IAlbumAccessorService(Core::System& system_, |
| 9 | public: | 14 | std::shared_ptr<AlbumManager> album_manager) |
| 10 | explicit IAlbumAccessorSession(Core::System& system_) | 15 | : ServiceFramework{system_, "caps:a"}, manager{album_manager} { |
| 11 | : ServiceFramework{system_, "IAlbumAccessorSession"} { | ||
| 12 | // clang-format off | ||
| 13 | static const FunctionInfo functions[] = { | ||
| 14 | {2001, nullptr, "OpenAlbumMovieReadStream"}, | ||
| 15 | {2002, nullptr, "CloseAlbumMovieReadStream"}, | ||
| 16 | {2003, nullptr, "GetAlbumMovieReadStreamMovieDataSize"}, | ||
| 17 | {2004, nullptr, "ReadMovieDataFromAlbumMovieReadStream"}, | ||
| 18 | {2005, nullptr, "GetAlbumMovieReadStreamBrokenReason"}, | ||
| 19 | {2006, nullptr, "GetAlbumMovieReadStreamImageDataSize"}, | ||
| 20 | {2007, nullptr, "ReadImageDataFromAlbumMovieReadStream"}, | ||
| 21 | {2008, nullptr, "ReadFileAttributeFromAlbumMovieReadStream"}, | ||
| 22 | }; | ||
| 23 | // clang-format on | ||
| 24 | |||
| 25 | RegisterHandlers(functions); | ||
| 26 | } | ||
| 27 | }; | ||
| 28 | |||
| 29 | CAPS_A::CAPS_A(Core::System& system_) : ServiceFramework{system_, "caps:a"} { | ||
| 30 | // clang-format off | 16 | // clang-format off |
| 31 | static const FunctionInfo functions[] = { | 17 | static const FunctionInfo functions[] = { |
| 32 | {0, nullptr, "GetAlbumFileCount"}, | 18 | {0, nullptr, "GetAlbumFileCount"}, |
| 33 | {1, nullptr, "GetAlbumFileList"}, | 19 | {1, nullptr, "GetAlbumFileList"}, |
| 34 | {2, nullptr, "LoadAlbumFile"}, | 20 | {2, nullptr, "LoadAlbumFile"}, |
| 35 | {3, nullptr, "DeleteAlbumFile"}, | 21 | {3, &IAlbumAccessorService::DeleteAlbumFile, "DeleteAlbumFile"}, |
| 36 | {4, nullptr, "StorageCopyAlbumFile"}, | 22 | {4, nullptr, "StorageCopyAlbumFile"}, |
| 37 | {5, nullptr, "IsAlbumMounted"}, | 23 | {5, &IAlbumAccessorService::IsAlbumMounted, "IsAlbumMounted"}, |
| 38 | {6, nullptr, "GetAlbumUsage"}, | 24 | {6, nullptr, "GetAlbumUsage"}, |
| 39 | {7, nullptr, "GetAlbumFileSize"}, | 25 | {7, nullptr, "GetAlbumFileSize"}, |
| 40 | {8, nullptr, "LoadAlbumFileThumbnail"}, | 26 | {8, nullptr, "LoadAlbumFileThumbnail"}, |
| @@ -47,18 +33,18 @@ CAPS_A::CAPS_A(Core::System& system_) : ServiceFramework{system_, "caps:a"} { | |||
| 47 | {15, nullptr, "GetAlbumUsage3"}, | 33 | {15, nullptr, "GetAlbumUsage3"}, |
| 48 | {16, nullptr, "GetAlbumMountResult"}, | 34 | {16, nullptr, "GetAlbumMountResult"}, |
| 49 | {17, nullptr, "GetAlbumUsage16"}, | 35 | {17, nullptr, "GetAlbumUsage16"}, |
| 50 | {18, nullptr, "Unknown18"}, | 36 | {18, &IAlbumAccessorService::Unknown18, "Unknown18"}, |
| 51 | {19, nullptr, "Unknown19"}, | 37 | {19, nullptr, "Unknown19"}, |
| 52 | {100, nullptr, "GetAlbumFileCountEx0"}, | 38 | {100, nullptr, "GetAlbumFileCountEx0"}, |
| 53 | {101, nullptr, "GetAlbumFileListEx0"}, | 39 | {101, &IAlbumAccessorService::GetAlbumFileListEx0, "GetAlbumFileListEx0"}, |
| 54 | {202, nullptr, "SaveEditedScreenShot"}, | 40 | {202, nullptr, "SaveEditedScreenShot"}, |
| 55 | {301, nullptr, "GetLastThumbnail"}, | 41 | {301, nullptr, "GetLastThumbnail"}, |
| 56 | {302, nullptr, "GetLastOverlayMovieThumbnail"}, | 42 | {302, nullptr, "GetLastOverlayMovieThumbnail"}, |
| 57 | {401, nullptr, "GetAutoSavingStorage"}, | 43 | {401, &IAlbumAccessorService::GetAutoSavingStorage, "GetAutoSavingStorage"}, |
| 58 | {501, nullptr, "GetRequiredStorageSpaceSizeToCopyAll"}, | 44 | {501, nullptr, "GetRequiredStorageSpaceSizeToCopyAll"}, |
| 59 | {1001, nullptr, "LoadAlbumScreenShotThumbnailImageEx0"}, | 45 | {1001, nullptr, "LoadAlbumScreenShotThumbnailImageEx0"}, |
| 60 | {1002, nullptr, "LoadAlbumScreenShotImageEx1"}, | 46 | {1002, &IAlbumAccessorService::LoadAlbumScreenShotImageEx1, "LoadAlbumScreenShotImageEx1"}, |
| 61 | {1003, nullptr, "LoadAlbumScreenShotThumbnailImageEx1"}, | 47 | {1003, &IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1, "LoadAlbumScreenShotThumbnailImageEx1"}, |
| 62 | {8001, nullptr, "ForceAlbumUnmounted"}, | 48 | {8001, nullptr, "ForceAlbumUnmounted"}, |
| 63 | {8002, nullptr, "ResetAlbumMountStatus"}, | 49 | {8002, nullptr, "ResetAlbumMountStatus"}, |
| 64 | {8011, nullptr, "RefreshAlbumCache"}, | 50 | {8011, nullptr, "RefreshAlbumCache"}, |
| @@ -74,6 +60,199 @@ CAPS_A::CAPS_A(Core::System& system_) : ServiceFramework{system_, "caps:a"} { | |||
| 74 | RegisterHandlers(functions); | 60 | RegisterHandlers(functions); |
| 75 | } | 61 | } |
| 76 | 62 | ||
| 77 | CAPS_A::~CAPS_A() = default; | 63 | IAlbumAccessorService::~IAlbumAccessorService() = default; |
| 64 | |||
| 65 | void IAlbumAccessorService::DeleteAlbumFile(HLERequestContext& ctx) { | ||
| 66 | IPC::RequestParser rp{ctx}; | ||
| 67 | const auto file_id{rp.PopRaw<AlbumFileId>()}; | ||
| 68 | |||
| 69 | LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}", | ||
| 70 | file_id.application_id, file_id.storage, file_id.type); | ||
| 71 | |||
| 72 | Result result = manager->DeleteAlbumFile(file_id); | ||
| 73 | result = TranslateResult(result); | ||
| 74 | |||
| 75 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 76 | rb.Push(result); | ||
| 77 | } | ||
| 78 | |||
| 79 | void IAlbumAccessorService::IsAlbumMounted(HLERequestContext& ctx) { | ||
| 80 | IPC::RequestParser rp{ctx}; | ||
| 81 | const auto storage{rp.PopEnum<AlbumStorage>()}; | ||
| 82 | |||
| 83 | LOG_INFO(Service_Capture, "called, storage={}", storage); | ||
| 84 | |||
| 85 | Result result = manager->IsAlbumMounted(storage); | ||
| 86 | const bool is_mounted = result.IsSuccess(); | ||
| 87 | result = TranslateResult(result); | ||
| 88 | |||
| 89 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 90 | rb.Push(result); | ||
| 91 | rb.Push<u8>(is_mounted); | ||
| 92 | } | ||
| 93 | |||
| 94 | void IAlbumAccessorService::Unknown18(HLERequestContext& ctx) { | ||
| 95 | struct UnknownBuffer { | ||
| 96 | INSERT_PADDING_BYTES(0x10); | ||
| 97 | }; | ||
| 98 | static_assert(sizeof(UnknownBuffer) == 0x10, "UnknownBuffer is an invalid size"); | ||
| 99 | |||
| 100 | LOG_WARNING(Service_Capture, "(STUBBED) called"); | ||
| 101 | |||
| 102 | std::vector<UnknownBuffer> buffer{}; | ||
| 103 | |||
| 104 | if (!buffer.empty()) { | ||
| 105 | ctx.WriteBuffer(buffer); | ||
| 106 | } | ||
| 107 | |||
| 108 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 109 | rb.Push(ResultSuccess); | ||
| 110 | rb.Push(static_cast<u32>(buffer.size())); | ||
| 111 | } | ||
| 112 | |||
| 113 | void IAlbumAccessorService::GetAlbumFileListEx0(HLERequestContext& ctx) { | ||
| 114 | IPC::RequestParser rp{ctx}; | ||
| 115 | const auto storage{rp.PopEnum<AlbumStorage>()}; | ||
| 116 | const auto flags{rp.Pop<u8>()}; | ||
| 117 | const auto album_entry_size{ctx.GetWriteBufferNumElements<AlbumEntry>()}; | ||
| 118 | |||
| 119 | LOG_INFO(Service_Capture, "called, storage={}, flags={}", storage, flags); | ||
| 120 | |||
| 121 | std::vector<AlbumEntry> entries; | ||
| 122 | Result result = manager->GetAlbumFileList(entries, storage, flags); | ||
| 123 | result = TranslateResult(result); | ||
| 124 | |||
| 125 | entries.resize(std::min(album_entry_size, entries.size())); | ||
| 126 | |||
| 127 | if (!entries.empty()) { | ||
| 128 | ctx.WriteBuffer(entries); | ||
| 129 | } | ||
| 130 | |||
| 131 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 132 | rb.Push(result); | ||
| 133 | rb.Push<u64>(entries.size()); | ||
| 134 | } | ||
| 135 | |||
| 136 | void IAlbumAccessorService::GetAutoSavingStorage(HLERequestContext& ctx) { | ||
| 137 | LOG_WARNING(Service_Capture, "(STUBBED) called"); | ||
| 138 | |||
| 139 | bool is_autosaving{}; | ||
| 140 | Result result = manager->GetAutoSavingStorage(is_autosaving); | ||
| 141 | result = TranslateResult(result); | ||
| 142 | |||
| 143 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 144 | rb.Push(result); | ||
| 145 | rb.Push<u8>(is_autosaving); | ||
| 146 | } | ||
| 147 | |||
| 148 | void IAlbumAccessorService::LoadAlbumScreenShotImageEx1(HLERequestContext& ctx) { | ||
| 149 | IPC::RequestParser rp{ctx}; | ||
| 150 | const auto file_id{rp.PopRaw<AlbumFileId>()}; | ||
| 151 | const auto decoder_options{rp.PopRaw<ScreenShotDecodeOption>()}; | ||
| 152 | const auto image_buffer_size{ctx.GetWriteBufferSize(1)}; | ||
| 153 | |||
| 154 | LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}, flags={}", | ||
| 155 | file_id.application_id, file_id.storage, file_id.type, decoder_options.flags); | ||
| 156 | |||
| 157 | std::vector<u8> image; | ||
| 158 | LoadAlbumScreenShotImageOutput image_output; | ||
| 159 | Result result = | ||
| 160 | manager->LoadAlbumScreenShotImage(image_output, image, file_id, decoder_options); | ||
| 161 | result = TranslateResult(result); | ||
| 162 | |||
| 163 | if (image.size() > image_buffer_size) { | ||
| 164 | result = ResultWorkMemoryError; | ||
| 165 | } | ||
| 166 | |||
| 167 | if (result.IsSuccess()) { | ||
| 168 | ctx.WriteBuffer(image_output, 0); | ||
| 169 | ctx.WriteBuffer(image, 1); | ||
| 170 | } | ||
| 171 | |||
| 172 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 173 | rb.Push(result); | ||
| 174 | } | ||
| 175 | |||
| 176 | void IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1(HLERequestContext& ctx) { | ||
| 177 | IPC::RequestParser rp{ctx}; | ||
| 178 | const auto file_id{rp.PopRaw<AlbumFileId>()}; | ||
| 179 | const auto decoder_options{rp.PopRaw<ScreenShotDecodeOption>()}; | ||
| 180 | |||
| 181 | LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}, flags={}", | ||
| 182 | file_id.application_id, file_id.storage, file_id.type, decoder_options.flags); | ||
| 183 | |||
| 184 | std::vector<u8> image(ctx.GetWriteBufferSize(1)); | ||
| 185 | LoadAlbumScreenShotImageOutput image_output; | ||
| 186 | Result result = | ||
| 187 | manager->LoadAlbumScreenShotThumbnail(image_output, image, file_id, decoder_options); | ||
| 188 | result = TranslateResult(result); | ||
| 189 | |||
| 190 | if (result.IsSuccess()) { | ||
| 191 | ctx.WriteBuffer(image_output, 0); | ||
| 192 | ctx.WriteBuffer(image, 1); | ||
| 193 | } | ||
| 194 | |||
| 195 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 196 | rb.Push(result); | ||
| 197 | } | ||
| 198 | |||
| 199 | Result IAlbumAccessorService::TranslateResult(Result in_result) { | ||
| 200 | if (in_result.IsSuccess()) { | ||
| 201 | return in_result; | ||
| 202 | } | ||
| 203 | |||
| 204 | if ((in_result.raw & 0x3801ff) == ResultUnknown1024.raw) { | ||
| 205 | if (in_result.description - 0x514 < 100) { | ||
| 206 | return ResultInvalidFileData; | ||
| 207 | } | ||
| 208 | if (in_result.description - 0x5dc < 100) { | ||
| 209 | return ResultInvalidFileData; | ||
| 210 | } | ||
| 211 | |||
| 212 | if (in_result.description - 0x578 < 100) { | ||
| 213 | if (in_result == ResultFileCountLimit) { | ||
| 214 | return ResultUnknown22; | ||
| 215 | } | ||
| 216 | return ResultUnknown25; | ||
| 217 | } | ||
| 218 | |||
| 219 | if (in_result.raw < ResultUnknown1801.raw) { | ||
| 220 | if (in_result == ResultUnknown1202) { | ||
| 221 | return ResultUnknown810; | ||
| 222 | } | ||
| 223 | if (in_result == ResultUnknown1203) { | ||
| 224 | return ResultUnknown810; | ||
| 225 | } | ||
| 226 | if (in_result == ResultUnknown1701) { | ||
| 227 | return ResultUnknown5; | ||
| 228 | } | ||
| 229 | } else if (in_result.raw < ResultUnknown1803.raw) { | ||
| 230 | if (in_result == ResultUnknown1801) { | ||
| 231 | return ResultUnknown5; | ||
| 232 | } | ||
| 233 | if (in_result == ResultUnknown1802) { | ||
| 234 | return ResultUnknown6; | ||
| 235 | } | ||
| 236 | } else { | ||
| 237 | if (in_result == ResultUnknown1803) { | ||
| 238 | return ResultUnknown7; | ||
| 239 | } | ||
| 240 | if (in_result == ResultUnknown1804) { | ||
| 241 | return ResultOutOfRange; | ||
| 242 | } | ||
| 243 | } | ||
| 244 | return ResultUnknown1024; | ||
| 245 | } | ||
| 246 | |||
| 247 | if (in_result.module == ErrorModule::FS) { | ||
| 248 | if ((in_result.description >> 0xc < 0x7d) || (in_result.description - 1000 < 2000) || | ||
| 249 | (((in_result.description - 3000) >> 3) < 0x271)) { | ||
| 250 | // TODO: Translate FS error | ||
| 251 | return in_result; | ||
| 252 | } | ||
| 253 | } | ||
| 254 | |||
| 255 | return in_result; | ||
| 256 | } | ||
| 78 | 257 | ||
| 79 | } // namespace Service::Capture | 258 | } // namespace Service::Capture |
diff --git a/src/core/hle/service/caps/caps_a.h b/src/core/hle/service/caps/caps_a.h index 98a21a5ad..c90cff71e 100644 --- a/src/core/hle/service/caps/caps_a.h +++ b/src/core/hle/service/caps/caps_a.h | |||
| @@ -10,11 +10,26 @@ class System; | |||
| 10 | } | 10 | } |
| 11 | 11 | ||
| 12 | namespace Service::Capture { | 12 | namespace Service::Capture { |
| 13 | class AlbumManager; | ||
| 13 | 14 | ||
| 14 | class CAPS_A final : public ServiceFramework<CAPS_A> { | 15 | class IAlbumAccessorService final : public ServiceFramework<IAlbumAccessorService> { |
| 15 | public: | 16 | public: |
| 16 | explicit CAPS_A(Core::System& system_); | 17 | explicit IAlbumAccessorService(Core::System& system_, |
| 17 | ~CAPS_A() override; | 18 | std::shared_ptr<AlbumManager> album_manager); |
| 19 | ~IAlbumAccessorService() override; | ||
| 20 | |||
| 21 | private: | ||
| 22 | void DeleteAlbumFile(HLERequestContext& ctx); | ||
| 23 | void IsAlbumMounted(HLERequestContext& ctx); | ||
| 24 | void Unknown18(HLERequestContext& ctx); | ||
| 25 | void GetAlbumFileListEx0(HLERequestContext& ctx); | ||
| 26 | void GetAutoSavingStorage(HLERequestContext& ctx); | ||
| 27 | void LoadAlbumScreenShotImageEx1(HLERequestContext& ctx); | ||
| 28 | void LoadAlbumScreenShotThumbnailImageEx1(HLERequestContext& ctx); | ||
| 29 | |||
| 30 | Result TranslateResult(Result in_result); | ||
| 31 | |||
| 32 | std::shared_ptr<AlbumManager> manager = nullptr; | ||
| 18 | }; | 33 | }; |
| 19 | 34 | ||
| 20 | } // namespace Service::Capture | 35 | } // namespace Service::Capture |
diff --git a/src/core/hle/service/caps/caps_c.cpp b/src/core/hle/service/caps/caps_c.cpp index fc77e35cd..1e7fe6474 100644 --- a/src/core/hle/service/caps/caps_c.cpp +++ b/src/core/hle/service/caps/caps_c.cpp | |||
| @@ -3,53 +3,21 @@ | |||
| 3 | 3 | ||
| 4 | #include "common/logging/log.h" | 4 | #include "common/logging/log.h" |
| 5 | #include "core/hle/service/caps/caps_c.h" | 5 | #include "core/hle/service/caps/caps_c.h" |
| 6 | #include "core/hle/service/caps/caps_manager.h" | ||
| 7 | #include "core/hle/service/caps/caps_result.h" | ||
| 8 | #include "core/hle/service/caps/caps_types.h" | ||
| 6 | #include "core/hle/service/ipc_helpers.h" | 9 | #include "core/hle/service/ipc_helpers.h" |
| 7 | 10 | ||
| 8 | namespace Service::Capture { | 11 | namespace Service::Capture { |
| 9 | 12 | ||
| 10 | class IAlbumControlSession final : public ServiceFramework<IAlbumControlSession> { | 13 | IAlbumControlService::IAlbumControlService(Core::System& system_, |
| 11 | public: | 14 | std::shared_ptr<AlbumManager> album_manager) |
| 12 | explicit IAlbumControlSession(Core::System& system_) | 15 | : ServiceFramework{system_, "caps:c"}, manager{album_manager} { |
| 13 | : ServiceFramework{system_, "IAlbumControlSession"} { | ||
| 14 | // clang-format off | ||
| 15 | static const FunctionInfo functions[] = { | ||
| 16 | {2001, nullptr, "OpenAlbumMovieReadStream"}, | ||
| 17 | {2002, nullptr, "CloseAlbumMovieReadStream"}, | ||
| 18 | {2003, nullptr, "GetAlbumMovieReadStreamMovieDataSize"}, | ||
| 19 | {2004, nullptr, "ReadMovieDataFromAlbumMovieReadStream"}, | ||
| 20 | {2005, nullptr, "GetAlbumMovieReadStreamBrokenReason"}, | ||
| 21 | {2006, nullptr, "GetAlbumMovieReadStreamImageDataSize"}, | ||
| 22 | {2007, nullptr, "ReadImageDataFromAlbumMovieReadStream"}, | ||
| 23 | {2008, nullptr, "ReadFileAttributeFromAlbumMovieReadStream"}, | ||
| 24 | {2401, nullptr, "OpenAlbumMovieWriteStream"}, | ||
| 25 | {2402, nullptr, "FinishAlbumMovieWriteStream"}, | ||
| 26 | {2403, nullptr, "CommitAlbumMovieWriteStream"}, | ||
| 27 | {2404, nullptr, "DiscardAlbumMovieWriteStream"}, | ||
| 28 | {2405, nullptr, "DiscardAlbumMovieWriteStreamNoDelete"}, | ||
| 29 | {2406, nullptr, "CommitAlbumMovieWriteStreamEx"}, | ||
| 30 | {2411, nullptr, "StartAlbumMovieWriteStreamDataSection"}, | ||
| 31 | {2412, nullptr, "EndAlbumMovieWriteStreamDataSection"}, | ||
| 32 | {2413, nullptr, "StartAlbumMovieWriteStreamMetaSection"}, | ||
| 33 | {2414, nullptr, "EndAlbumMovieWriteStreamMetaSection"}, | ||
| 34 | {2421, nullptr, "ReadDataFromAlbumMovieWriteStream"}, | ||
| 35 | {2422, nullptr, "WriteDataToAlbumMovieWriteStream"}, | ||
| 36 | {2424, nullptr, "WriteMetaToAlbumMovieWriteStream"}, | ||
| 37 | {2431, nullptr, "GetAlbumMovieWriteStreamBrokenReason"}, | ||
| 38 | {2433, nullptr, "GetAlbumMovieWriteStreamDataSize"}, | ||
| 39 | {2434, nullptr, "SetAlbumMovieWriteStreamDataSize"}, | ||
| 40 | }; | ||
| 41 | // clang-format on | ||
| 42 | |||
| 43 | RegisterHandlers(functions); | ||
| 44 | } | ||
| 45 | }; | ||
| 46 | |||
| 47 | CAPS_C::CAPS_C(Core::System& system_) : ServiceFramework{system_, "caps:c"} { | ||
| 48 | // clang-format off | 16 | // clang-format off |
| 49 | static const FunctionInfo functions[] = { | 17 | static const FunctionInfo functions[] = { |
| 50 | {1, nullptr, "CaptureRawImage"}, | 18 | {1, nullptr, "CaptureRawImage"}, |
| 51 | {2, nullptr, "CaptureRawImageWithTimeout"}, | 19 | {2, nullptr, "CaptureRawImageWithTimeout"}, |
| 52 | {33, &CAPS_C::SetShimLibraryVersion, "SetShimLibraryVersion"}, | 20 | {33, &IAlbumControlService::SetShimLibraryVersion, "SetShimLibraryVersion"}, |
| 53 | {1001, nullptr, "RequestTakingScreenShot"}, | 21 | {1001, nullptr, "RequestTakingScreenShot"}, |
| 54 | {1002, nullptr, "RequestTakingScreenShotWithTimeout"}, | 22 | {1002, nullptr, "RequestTakingScreenShotWithTimeout"}, |
| 55 | {1011, nullptr, "NotifyTakingScreenShotRefused"}, | 23 | {1011, nullptr, "NotifyTakingScreenShotRefused"}, |
| @@ -72,9 +40,9 @@ CAPS_C::CAPS_C(Core::System& system_) : ServiceFramework{system_, "caps:c"} { | |||
| 72 | RegisterHandlers(functions); | 40 | RegisterHandlers(functions); |
| 73 | } | 41 | } |
| 74 | 42 | ||
| 75 | CAPS_C::~CAPS_C() = default; | 43 | IAlbumControlService::~IAlbumControlService() = default; |
| 76 | 44 | ||
| 77 | void CAPS_C::SetShimLibraryVersion(HLERequestContext& ctx) { | 45 | void IAlbumControlService::SetShimLibraryVersion(HLERequestContext& ctx) { |
| 78 | IPC::RequestParser rp{ctx}; | 46 | IPC::RequestParser rp{ctx}; |
| 79 | const auto library_version{rp.Pop<u64>()}; | 47 | const auto library_version{rp.Pop<u64>()}; |
| 80 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 48 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
diff --git a/src/core/hle/service/caps/caps_c.h b/src/core/hle/service/caps/caps_c.h index 537b3a2e3..92ba242db 100644 --- a/src/core/hle/service/caps/caps_c.h +++ b/src/core/hle/service/caps/caps_c.h | |||
| @@ -10,14 +10,18 @@ class System; | |||
| 10 | } | 10 | } |
| 11 | 11 | ||
| 12 | namespace Service::Capture { | 12 | namespace Service::Capture { |
| 13 | class AlbumManager; | ||
| 13 | 14 | ||
| 14 | class CAPS_C final : public ServiceFramework<CAPS_C> { | 15 | class IAlbumControlService final : public ServiceFramework<IAlbumControlService> { |
| 15 | public: | 16 | public: |
| 16 | explicit CAPS_C(Core::System& system_); | 17 | explicit IAlbumControlService(Core::System& system_, |
| 17 | ~CAPS_C() override; | 18 | std::shared_ptr<AlbumManager> album_manager); |
| 19 | ~IAlbumControlService() override; | ||
| 18 | 20 | ||
| 19 | private: | 21 | private: |
| 20 | void SetShimLibraryVersion(HLERequestContext& ctx); | 22 | void SetShimLibraryVersion(HLERequestContext& ctx); |
| 23 | |||
| 24 | std::shared_ptr<AlbumManager> manager = nullptr; | ||
| 21 | }; | 25 | }; |
| 22 | 26 | ||
| 23 | } // namespace Service::Capture | 27 | } // namespace Service::Capture |
diff --git a/src/core/hle/service/caps/caps_manager.cpp b/src/core/hle/service/caps/caps_manager.cpp new file mode 100644 index 000000000..2b4e3f076 --- /dev/null +++ b/src/core/hle/service/caps/caps_manager.cpp | |||
| @@ -0,0 +1,386 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <sstream> | ||
| 5 | #include <stb_image.h> | ||
| 6 | #include <stb_image_resize.h> | ||
| 7 | |||
| 8 | #include "common/fs/file.h" | ||
| 9 | #include "common/fs/path_util.h" | ||
| 10 | #include "common/logging/log.h" | ||
| 11 | #include "core/core.h" | ||
| 12 | #include "core/hle/service/caps/caps_manager.h" | ||
| 13 | #include "core/hle/service/caps/caps_result.h" | ||
| 14 | #include "core/hle/service/time/time_manager.h" | ||
| 15 | #include "core/hle/service/time/time_zone_content_manager.h" | ||
| 16 | |||
| 17 | namespace Service::Capture { | ||
| 18 | |||
| 19 | AlbumManager::AlbumManager(Core::System& system_) : system{system_} {} | ||
| 20 | |||
| 21 | AlbumManager::~AlbumManager() = default; | ||
| 22 | |||
| 23 | Result AlbumManager::DeleteAlbumFile(const AlbumFileId& file_id) { | ||
| 24 | if (file_id.storage > AlbumStorage::Sd) { | ||
| 25 | return ResultInvalidStorage; | ||
| 26 | } | ||
| 27 | |||
| 28 | if (!is_mounted) { | ||
| 29 | return ResultIsNotMounted; | ||
| 30 | } | ||
| 31 | |||
| 32 | std::filesystem::path path; | ||
| 33 | const auto result = GetFile(path, file_id); | ||
| 34 | |||
| 35 | if (result.IsError()) { | ||
| 36 | return result; | ||
| 37 | } | ||
| 38 | |||
| 39 | if (!Common::FS::RemoveFile(path)) { | ||
| 40 | return ResultFileNotFound; | ||
| 41 | } | ||
| 42 | |||
| 43 | return ResultSuccess; | ||
| 44 | } | ||
| 45 | |||
| 46 | Result AlbumManager::IsAlbumMounted(AlbumStorage storage) { | ||
| 47 | if (storage > AlbumStorage::Sd) { | ||
| 48 | return ResultInvalidStorage; | ||
| 49 | } | ||
| 50 | |||
| 51 | is_mounted = true; | ||
| 52 | |||
| 53 | if (storage == AlbumStorage::Sd) { | ||
| 54 | FindScreenshots(); | ||
| 55 | } | ||
| 56 | |||
| 57 | return is_mounted ? ResultSuccess : ResultIsNotMounted; | ||
| 58 | } | ||
| 59 | |||
| 60 | Result AlbumManager::GetAlbumFileList(std::vector<AlbumEntry>& out_entries, AlbumStorage storage, | ||
| 61 | u8 flags) const { | ||
| 62 | if (storage > AlbumStorage::Sd) { | ||
| 63 | return ResultInvalidStorage; | ||
| 64 | } | ||
| 65 | |||
| 66 | if (!is_mounted) { | ||
| 67 | return ResultIsNotMounted; | ||
| 68 | } | ||
| 69 | |||
| 70 | for (auto& [file_id, path] : album_files) { | ||
| 71 | if (file_id.storage != storage) { | ||
| 72 | continue; | ||
| 73 | } | ||
| 74 | if (out_entries.size() >= SdAlbumFileLimit) { | ||
| 75 | break; | ||
| 76 | } | ||
| 77 | |||
| 78 | const auto entry_size = Common::FS::GetSize(path); | ||
| 79 | out_entries.push_back({ | ||
| 80 | .entry_size = entry_size, | ||
| 81 | .file_id = file_id, | ||
| 82 | }); | ||
| 83 | } | ||
| 84 | |||
| 85 | return ResultSuccess; | ||
| 86 | } | ||
| 87 | |||
| 88 | Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries, | ||
| 89 | ContentType contex_type, s64 start_posix_time, | ||
| 90 | s64 end_posix_time, u64 aruid) const { | ||
| 91 | if (!is_mounted) { | ||
| 92 | return ResultIsNotMounted; | ||
| 93 | } | ||
| 94 | |||
| 95 | std::vector<ApplicationAlbumEntry> album_entries; | ||
| 96 | const auto start_date = ConvertToAlbumDateTime(start_posix_time); | ||
| 97 | const auto end_date = ConvertToAlbumDateTime(end_posix_time); | ||
| 98 | const auto result = GetAlbumFileList(album_entries, contex_type, start_date, end_date, aruid); | ||
| 99 | |||
| 100 | if (result.IsError()) { | ||
| 101 | return result; | ||
| 102 | } | ||
| 103 | |||
| 104 | for (const auto& album_entry : album_entries) { | ||
| 105 | ApplicationAlbumFileEntry entry{ | ||
| 106 | .entry = album_entry, | ||
| 107 | .datetime = album_entry.datetime, | ||
| 108 | .unknown = {}, | ||
| 109 | }; | ||
| 110 | out_entries.push_back(entry); | ||
| 111 | } | ||
| 112 | |||
| 113 | return ResultSuccess; | ||
| 114 | } | ||
| 115 | |||
| 116 | Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_entries, | ||
| 117 | ContentType contex_type, AlbumFileDateTime start_date, | ||
| 118 | AlbumFileDateTime end_date, u64 aruid) const { | ||
| 119 | if (!is_mounted) { | ||
| 120 | return ResultIsNotMounted; | ||
| 121 | } | ||
| 122 | |||
| 123 | for (auto& [file_id, path] : album_files) { | ||
| 124 | if (file_id.type != contex_type) { | ||
| 125 | continue; | ||
| 126 | } | ||
| 127 | if (file_id.date > start_date) { | ||
| 128 | continue; | ||
| 129 | } | ||
| 130 | if (file_id.date < end_date) { | ||
| 131 | continue; | ||
| 132 | } | ||
| 133 | if (out_entries.size() >= SdAlbumFileLimit) { | ||
| 134 | break; | ||
| 135 | } | ||
| 136 | |||
| 137 | const auto entry_size = Common::FS::GetSize(path); | ||
| 138 | ApplicationAlbumEntry entry{ | ||
| 139 | .size = entry_size, | ||
| 140 | .hash{}, | ||
| 141 | .datetime = file_id.date, | ||
| 142 | .storage = file_id.storage, | ||
| 143 | .content = contex_type, | ||
| 144 | .unknown = 1, | ||
| 145 | }; | ||
| 146 | out_entries.push_back(entry); | ||
| 147 | } | ||
| 148 | |||
| 149 | return ResultSuccess; | ||
| 150 | } | ||
| 151 | |||
| 152 | Result AlbumManager::GetAutoSavingStorage(bool& out_is_autosaving) const { | ||
| 153 | out_is_autosaving = false; | ||
| 154 | return ResultSuccess; | ||
| 155 | } | ||
| 156 | |||
| 157 | Result AlbumManager::LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& out_image_output, | ||
| 158 | std::vector<u8>& out_image, | ||
| 159 | const AlbumFileId& file_id, | ||
| 160 | const ScreenShotDecodeOption& decoder_options) const { | ||
| 161 | if (file_id.storage > AlbumStorage::Sd) { | ||
| 162 | return ResultInvalidStorage; | ||
| 163 | } | ||
| 164 | |||
| 165 | if (!is_mounted) { | ||
| 166 | return ResultIsNotMounted; | ||
| 167 | } | ||
| 168 | |||
| 169 | out_image_output = { | ||
| 170 | .width = 1280, | ||
| 171 | .height = 720, | ||
| 172 | .attribute = | ||
| 173 | { | ||
| 174 | .unknown_0{}, | ||
| 175 | .orientation = AlbumImageOrientation::None, | ||
| 176 | .unknown_1{}, | ||
| 177 | .unknown_2{}, | ||
| 178 | }, | ||
| 179 | }; | ||
| 180 | |||
| 181 | std::filesystem::path path; | ||
| 182 | const auto result = GetFile(path, file_id); | ||
| 183 | |||
| 184 | if (result.IsError()) { | ||
| 185 | return result; | ||
| 186 | } | ||
| 187 | |||
| 188 | out_image.resize(out_image_output.height * out_image_output.width * STBI_rgb_alpha); | ||
| 189 | |||
| 190 | return LoadImage(out_image, path, static_cast<int>(out_image_output.width), | ||
| 191 | +static_cast<int>(out_image_output.height), decoder_options.flags); | ||
| 192 | } | ||
| 193 | |||
| 194 | Result AlbumManager::LoadAlbumScreenShotThumbnail( | ||
| 195 | LoadAlbumScreenShotImageOutput& out_image_output, std::vector<u8>& out_image, | ||
| 196 | const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options) const { | ||
| 197 | if (file_id.storage > AlbumStorage::Sd) { | ||
| 198 | return ResultInvalidStorage; | ||
| 199 | } | ||
| 200 | |||
| 201 | if (!is_mounted) { | ||
| 202 | return ResultIsNotMounted; | ||
| 203 | } | ||
| 204 | |||
| 205 | out_image_output = { | ||
| 206 | .width = 320, | ||
| 207 | .height = 180, | ||
| 208 | .attribute = | ||
| 209 | { | ||
| 210 | .unknown_0{}, | ||
| 211 | .orientation = AlbumImageOrientation::None, | ||
| 212 | .unknown_1{}, | ||
| 213 | .unknown_2{}, | ||
| 214 | }, | ||
| 215 | }; | ||
| 216 | |||
| 217 | std::filesystem::path path; | ||
| 218 | const auto result = GetFile(path, file_id); | ||
| 219 | |||
| 220 | if (result.IsError()) { | ||
| 221 | return result; | ||
| 222 | } | ||
| 223 | |||
| 224 | out_image.resize(out_image_output.height * out_image_output.width * STBI_rgb_alpha); | ||
| 225 | |||
| 226 | return LoadImage(out_image, path, static_cast<int>(out_image_output.width), | ||
| 227 | +static_cast<int>(out_image_output.height), decoder_options.flags); | ||
| 228 | } | ||
| 229 | |||
| 230 | Result AlbumManager::GetFile(std::filesystem::path& out_path, const AlbumFileId& file_id) const { | ||
| 231 | const auto file = album_files.find(file_id); | ||
| 232 | |||
| 233 | if (file == album_files.end()) { | ||
| 234 | return ResultFileNotFound; | ||
| 235 | } | ||
| 236 | |||
| 237 | out_path = file->second; | ||
| 238 | return ResultSuccess; | ||
| 239 | } | ||
| 240 | |||
| 241 | void AlbumManager::FindScreenshots() { | ||
| 242 | is_mounted = false; | ||
| 243 | album_files.clear(); | ||
| 244 | |||
| 245 | // TODO: Swap this with a blocking operation. | ||
| 246 | const auto screenshots_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ScreenshotsDir); | ||
| 247 | Common::FS::IterateDirEntries( | ||
| 248 | screenshots_dir, | ||
| 249 | [this](const std::filesystem::path& full_path) { | ||
| 250 | AlbumEntry entry; | ||
| 251 | if (GetAlbumEntry(entry, full_path).IsError()) { | ||
| 252 | return true; | ||
| 253 | } | ||
| 254 | while (album_files.contains(entry.file_id)) { | ||
| 255 | if (++entry.file_id.date.unique_id == 0) { | ||
| 256 | break; | ||
| 257 | } | ||
| 258 | } | ||
| 259 | album_files[entry.file_id] = full_path; | ||
| 260 | return true; | ||
| 261 | }, | ||
| 262 | Common::FS::DirEntryFilter::File); | ||
| 263 | |||
| 264 | is_mounted = true; | ||
| 265 | } | ||
| 266 | |||
| 267 | Result AlbumManager::GetAlbumEntry(AlbumEntry& out_entry, const std::filesystem::path& path) const { | ||
| 268 | std::istringstream line_stream(path.filename().string()); | ||
| 269 | std::string date; | ||
| 270 | std::string application; | ||
| 271 | std::string time; | ||
| 272 | |||
| 273 | // Parse filename to obtain entry properties | ||
| 274 | std::getline(line_stream, application, '_'); | ||
| 275 | std::getline(line_stream, date, '_'); | ||
| 276 | std::getline(line_stream, time, '_'); | ||
| 277 | |||
| 278 | std::istringstream date_stream(date); | ||
| 279 | std::istringstream time_stream(time); | ||
| 280 | std::string year; | ||
| 281 | std::string month; | ||
| 282 | std::string day; | ||
| 283 | std::string hour; | ||
| 284 | std::string minute; | ||
| 285 | std::string second; | ||
| 286 | |||
| 287 | std::getline(date_stream, year, '-'); | ||
| 288 | std::getline(date_stream, month, '-'); | ||
| 289 | std::getline(date_stream, day, '-'); | ||
| 290 | |||
| 291 | std::getline(time_stream, hour, '-'); | ||
| 292 | std::getline(time_stream, minute, '-'); | ||
| 293 | std::getline(time_stream, second, '-'); | ||
| 294 | |||
| 295 | try { | ||
| 296 | out_entry = { | ||
| 297 | .entry_size = 1, | ||
| 298 | .file_id{ | ||
| 299 | .application_id = static_cast<u64>(std::stoll(application, 0, 16)), | ||
| 300 | .date = | ||
| 301 | { | ||
| 302 | .year = static_cast<s16>(std::stoi(year)), | ||
| 303 | .month = static_cast<s8>(std::stoi(month)), | ||
| 304 | .day = static_cast<s8>(std::stoi(day)), | ||
| 305 | .hour = static_cast<s8>(std::stoi(hour)), | ||
| 306 | .minute = static_cast<s8>(std::stoi(minute)), | ||
| 307 | .second = static_cast<s8>(std::stoi(second)), | ||
| 308 | .unique_id = 0, | ||
| 309 | }, | ||
| 310 | .storage = AlbumStorage::Sd, | ||
| 311 | .type = ContentType::Screenshot, | ||
| 312 | .unknown = 1, | ||
| 313 | }, | ||
| 314 | }; | ||
| 315 | } catch (const std::invalid_argument&) { | ||
| 316 | return ResultUnknown; | ||
| 317 | } catch (const std::out_of_range&) { | ||
| 318 | return ResultUnknown; | ||
| 319 | } catch (const std::exception&) { | ||
| 320 | return ResultUnknown; | ||
| 321 | } | ||
| 322 | |||
| 323 | return ResultSuccess; | ||
| 324 | } | ||
| 325 | |||
| 326 | Result AlbumManager::LoadImage(std::span<u8> out_image, const std::filesystem::path& path, | ||
| 327 | int width, int height, ScreenShotDecoderFlag flag) const { | ||
| 328 | if (out_image.size() != static_cast<std::size_t>(width * height * STBI_rgb_alpha)) { | ||
| 329 | return ResultUnknown; | ||
| 330 | } | ||
| 331 | |||
| 332 | const Common::FS::IOFile db_file{path, Common::FS::FileAccessMode::Read, | ||
| 333 | Common::FS::FileType::BinaryFile}; | ||
| 334 | |||
| 335 | std::vector<u8> raw_file(db_file.GetSize()); | ||
| 336 | if (db_file.Read(raw_file) != raw_file.size()) { | ||
| 337 | return ResultUnknown; | ||
| 338 | } | ||
| 339 | |||
| 340 | int filter_flag = STBIR_FILTER_DEFAULT; | ||
| 341 | int original_width, original_height, color_channels; | ||
| 342 | const auto dbi_image = | ||
| 343 | stbi_load_from_memory(raw_file.data(), static_cast<int>(raw_file.size()), &original_width, | ||
| 344 | &original_height, &color_channels, STBI_rgb_alpha); | ||
| 345 | |||
| 346 | if (dbi_image == nullptr) { | ||
| 347 | return ResultUnknown; | ||
| 348 | } | ||
| 349 | |||
| 350 | switch (flag) { | ||
| 351 | case ScreenShotDecoderFlag::EnableFancyUpsampling: | ||
| 352 | filter_flag = STBIR_FILTER_TRIANGLE; | ||
| 353 | break; | ||
| 354 | case ScreenShotDecoderFlag::EnableBlockSmoothing: | ||
| 355 | filter_flag = STBIR_FILTER_BOX; | ||
| 356 | break; | ||
| 357 | default: | ||
| 358 | filter_flag = STBIR_FILTER_DEFAULT; | ||
| 359 | break; | ||
| 360 | } | ||
| 361 | |||
| 362 | stbir_resize_uint8_srgb(dbi_image, original_width, original_height, 0, out_image.data(), width, | ||
| 363 | height, 0, STBI_rgb_alpha, 3, filter_flag); | ||
| 364 | |||
| 365 | return ResultSuccess; | ||
| 366 | } | ||
| 367 | |||
| 368 | AlbumFileDateTime AlbumManager::ConvertToAlbumDateTime(u64 posix_time) const { | ||
| 369 | Time::TimeZone::CalendarInfo calendar_date{}; | ||
| 370 | const auto& time_zone_manager = | ||
| 371 | system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager(); | ||
| 372 | |||
| 373 | time_zone_manager.ToCalendarTimeWithMyRules(posix_time, calendar_date); | ||
| 374 | |||
| 375 | return { | ||
| 376 | .year = calendar_date.time.year, | ||
| 377 | .month = calendar_date.time.month, | ||
| 378 | .day = calendar_date.time.day, | ||
| 379 | .hour = calendar_date.time.hour, | ||
| 380 | .minute = calendar_date.time.minute, | ||
| 381 | .second = calendar_date.time.second, | ||
| 382 | .unique_id = 0, | ||
| 383 | }; | ||
| 384 | } | ||
| 385 | |||
| 386 | } // namespace Service::Capture | ||
diff --git a/src/core/hle/service/caps/caps_manager.h b/src/core/hle/service/caps/caps_manager.h new file mode 100644 index 000000000..f65eb12c1 --- /dev/null +++ b/src/core/hle/service/caps/caps_manager.h | |||
| @@ -0,0 +1,79 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <unordered_map> | ||
| 7 | |||
| 8 | #include "common/fs/fs.h" | ||
| 9 | #include "core/hle/result.h" | ||
| 10 | #include "core/hle/service/caps/caps_types.h" | ||
| 11 | |||
| 12 | namespace Core { | ||
| 13 | class System; | ||
| 14 | } | ||
| 15 | |||
| 16 | namespace std { | ||
| 17 | // Hash used to create lists from AlbumFileId data | ||
| 18 | template <> | ||
| 19 | struct hash<Service::Capture::AlbumFileId> { | ||
| 20 | size_t operator()(const Service::Capture::AlbumFileId& pad_id) const noexcept { | ||
| 21 | u64 hash_value = (static_cast<u64>(pad_id.date.year) << 8); | ||
| 22 | hash_value ^= (static_cast<u64>(pad_id.date.month) << 7); | ||
| 23 | hash_value ^= (static_cast<u64>(pad_id.date.day) << 6); | ||
| 24 | hash_value ^= (static_cast<u64>(pad_id.date.hour) << 5); | ||
| 25 | hash_value ^= (static_cast<u64>(pad_id.date.minute) << 4); | ||
| 26 | hash_value ^= (static_cast<u64>(pad_id.date.second) << 3); | ||
| 27 | hash_value ^= (static_cast<u64>(pad_id.date.unique_id) << 2); | ||
| 28 | hash_value ^= (static_cast<u64>(pad_id.storage) << 1); | ||
| 29 | hash_value ^= static_cast<u64>(pad_id.type); | ||
| 30 | return static_cast<size_t>(hash_value); | ||
| 31 | } | ||
| 32 | }; | ||
| 33 | |||
| 34 | } // namespace std | ||
| 35 | |||
| 36 | namespace Service::Capture { | ||
| 37 | |||
| 38 | class AlbumManager { | ||
| 39 | public: | ||
| 40 | explicit AlbumManager(Core::System& system_); | ||
| 41 | ~AlbumManager(); | ||
| 42 | |||
| 43 | Result DeleteAlbumFile(const AlbumFileId& file_id); | ||
| 44 | Result IsAlbumMounted(AlbumStorage storage); | ||
| 45 | Result GetAlbumFileList(std::vector<AlbumEntry>& out_entries, AlbumStorage storage, | ||
| 46 | u8 flags) const; | ||
| 47 | Result GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries, | ||
| 48 | ContentType contex_type, s64 start_posix_time, s64 end_posix_time, | ||
| 49 | u64 aruid) const; | ||
| 50 | Result GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_entries, | ||
| 51 | ContentType contex_type, AlbumFileDateTime start_date, | ||
| 52 | AlbumFileDateTime end_date, u64 aruid) const; | ||
| 53 | Result GetAutoSavingStorage(bool& out_is_autosaving) const; | ||
| 54 | Result LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& out_image_output, | ||
| 55 | std::vector<u8>& out_image, const AlbumFileId& file_id, | ||
| 56 | const ScreenShotDecodeOption& decoder_options) const; | ||
| 57 | Result LoadAlbumScreenShotThumbnail(LoadAlbumScreenShotImageOutput& out_image_output, | ||
| 58 | std::vector<u8>& out_image, const AlbumFileId& file_id, | ||
| 59 | const ScreenShotDecodeOption& decoder_options) const; | ||
| 60 | |||
| 61 | private: | ||
| 62 | static constexpr std::size_t NandAlbumFileLimit = 1000; | ||
| 63 | static constexpr std::size_t SdAlbumFileLimit = 10000; | ||
| 64 | |||
| 65 | void FindScreenshots(); | ||
| 66 | Result GetFile(std::filesystem::path& out_path, const AlbumFileId& file_id) const; | ||
| 67 | Result GetAlbumEntry(AlbumEntry& out_entry, const std::filesystem::path& path) const; | ||
| 68 | Result LoadImage(std::span<u8> out_image, const std::filesystem::path& path, int width, | ||
| 69 | int height, ScreenShotDecoderFlag flag) const; | ||
| 70 | |||
| 71 | AlbumFileDateTime ConvertToAlbumDateTime(u64 posix_time) const; | ||
| 72 | |||
| 73 | bool is_mounted{}; | ||
| 74 | std::unordered_map<AlbumFileId, std::filesystem::path> album_files; | ||
| 75 | |||
| 76 | Core::System& system; | ||
| 77 | }; | ||
| 78 | |||
| 79 | } // namespace Service::Capture | ||
diff --git a/src/core/hle/service/caps/caps_result.h b/src/core/hle/service/caps/caps_result.h new file mode 100644 index 000000000..c65e5fb9a --- /dev/null +++ b/src/core/hle/service/caps/caps_result.h | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/result.h" | ||
| 7 | |||
| 8 | namespace Service::Capture { | ||
| 9 | |||
| 10 | constexpr Result ResultWorkMemoryError(ErrorModule::Capture, 3); | ||
| 11 | constexpr Result ResultUnknown5(ErrorModule::Capture, 5); | ||
| 12 | constexpr Result ResultUnknown6(ErrorModule::Capture, 6); | ||
| 13 | constexpr Result ResultUnknown7(ErrorModule::Capture, 7); | ||
| 14 | constexpr Result ResultOutOfRange(ErrorModule::Capture, 8); | ||
| 15 | constexpr Result ResulInvalidTimestamp(ErrorModule::Capture, 12); | ||
| 16 | constexpr Result ResultInvalidStorage(ErrorModule::Capture, 13); | ||
| 17 | constexpr Result ResultInvalidFileContents(ErrorModule::Capture, 14); | ||
| 18 | constexpr Result ResultIsNotMounted(ErrorModule::Capture, 21); | ||
| 19 | constexpr Result ResultUnknown22(ErrorModule::Capture, 22); | ||
| 20 | constexpr Result ResultFileNotFound(ErrorModule::Capture, 23); | ||
| 21 | constexpr Result ResultInvalidFileData(ErrorModule::Capture, 24); | ||
| 22 | constexpr Result ResultUnknown25(ErrorModule::Capture, 25); | ||
| 23 | constexpr Result ResultReadBufferShortage(ErrorModule::Capture, 30); | ||
| 24 | constexpr Result ResultUnknown810(ErrorModule::Capture, 810); | ||
| 25 | constexpr Result ResultUnknown1024(ErrorModule::Capture, 1024); | ||
| 26 | constexpr Result ResultUnknown1202(ErrorModule::Capture, 1202); | ||
| 27 | constexpr Result ResultUnknown1203(ErrorModule::Capture, 1203); | ||
| 28 | constexpr Result ResultFileCountLimit(ErrorModule::Capture, 1401); | ||
| 29 | constexpr Result ResultUnknown1701(ErrorModule::Capture, 1701); | ||
| 30 | constexpr Result ResultUnknown1801(ErrorModule::Capture, 1801); | ||
| 31 | constexpr Result ResultUnknown1802(ErrorModule::Capture, 1802); | ||
| 32 | constexpr Result ResultUnknown1803(ErrorModule::Capture, 1803); | ||
| 33 | constexpr Result ResultUnknown1804(ErrorModule::Capture, 1804); | ||
| 34 | |||
| 35 | } // namespace Service::Capture | ||
diff --git a/src/core/hle/service/caps/caps_sc.cpp b/src/core/hle/service/caps/caps_sc.cpp index 395b13da7..6117cb7c6 100644 --- a/src/core/hle/service/caps/caps_sc.cpp +++ b/src/core/hle/service/caps/caps_sc.cpp | |||
| @@ -5,7 +5,8 @@ | |||
| 5 | 5 | ||
| 6 | namespace Service::Capture { | 6 | namespace Service::Capture { |
| 7 | 7 | ||
| 8 | CAPS_SC::CAPS_SC(Core::System& system_) : ServiceFramework{system_, "caps:sc"} { | 8 | IScreenShotControlService::IScreenShotControlService(Core::System& system_) |
| 9 | : ServiceFramework{system_, "caps:sc"} { | ||
| 9 | // clang-format off | 10 | // clang-format off |
| 10 | static const FunctionInfo functions[] = { | 11 | static const FunctionInfo functions[] = { |
| 11 | {1, nullptr, "CaptureRawImage"}, | 12 | {1, nullptr, "CaptureRawImage"}, |
| @@ -34,6 +35,6 @@ CAPS_SC::CAPS_SC(Core::System& system_) : ServiceFramework{system_, "caps:sc"} { | |||
| 34 | RegisterHandlers(functions); | 35 | RegisterHandlers(functions); |
| 35 | } | 36 | } |
| 36 | 37 | ||
| 37 | CAPS_SC::~CAPS_SC() = default; | 38 | IScreenShotControlService::~IScreenShotControlService() = default; |
| 38 | 39 | ||
| 39 | } // namespace Service::Capture | 40 | } // namespace Service::Capture |
diff --git a/src/core/hle/service/caps/caps_sc.h b/src/core/hle/service/caps/caps_sc.h index e5600f6d7..d555f4979 100644 --- a/src/core/hle/service/caps/caps_sc.h +++ b/src/core/hle/service/caps/caps_sc.h | |||
| @@ -11,10 +11,10 @@ class System; | |||
| 11 | 11 | ||
| 12 | namespace Service::Capture { | 12 | namespace Service::Capture { |
| 13 | 13 | ||
| 14 | class CAPS_SC final : public ServiceFramework<CAPS_SC> { | 14 | class IScreenShotControlService final : public ServiceFramework<IScreenShotControlService> { |
| 15 | public: | 15 | public: |
| 16 | explicit CAPS_SC(Core::System& system_); | 16 | explicit IScreenShotControlService(Core::System& system_); |
| 17 | ~CAPS_SC() override; | 17 | ~IScreenShotControlService() override; |
| 18 | }; | 18 | }; |
| 19 | 19 | ||
| 20 | } // namespace Service::Capture | 20 | } // namespace Service::Capture |
diff --git a/src/core/hle/service/caps/caps_ss.cpp b/src/core/hle/service/caps/caps_ss.cpp index 62b9edd41..d0d1b5425 100644 --- a/src/core/hle/service/caps/caps_ss.cpp +++ b/src/core/hle/service/caps/caps_ss.cpp | |||
| @@ -5,7 +5,8 @@ | |||
| 5 | 5 | ||
| 6 | namespace Service::Capture { | 6 | namespace Service::Capture { |
| 7 | 7 | ||
| 8 | CAPS_SS::CAPS_SS(Core::System& system_) : ServiceFramework{system_, "caps:ss"} { | 8 | IScreenShotService::IScreenShotService(Core::System& system_) |
| 9 | : ServiceFramework{system_, "caps:ss"} { | ||
| 9 | // clang-format off | 10 | // clang-format off |
| 10 | static const FunctionInfo functions[] = { | 11 | static const FunctionInfo functions[] = { |
| 11 | {201, nullptr, "SaveScreenShot"}, | 12 | {201, nullptr, "SaveScreenShot"}, |
| @@ -21,6 +22,6 @@ CAPS_SS::CAPS_SS(Core::System& system_) : ServiceFramework{system_, "caps:ss"} { | |||
| 21 | RegisterHandlers(functions); | 22 | RegisterHandlers(functions); |
| 22 | } | 23 | } |
| 23 | 24 | ||
| 24 | CAPS_SS::~CAPS_SS() = default; | 25 | IScreenShotService::~IScreenShotService() = default; |
| 25 | 26 | ||
| 26 | } // namespace Service::Capture | 27 | } // namespace Service::Capture |
diff --git a/src/core/hle/service/caps/caps_ss.h b/src/core/hle/service/caps/caps_ss.h index 718ade485..381e44fd4 100644 --- a/src/core/hle/service/caps/caps_ss.h +++ b/src/core/hle/service/caps/caps_ss.h | |||
| @@ -11,10 +11,10 @@ class System; | |||
| 11 | 11 | ||
| 12 | namespace Service::Capture { | 12 | namespace Service::Capture { |
| 13 | 13 | ||
| 14 | class CAPS_SS final : public ServiceFramework<CAPS_SS> { | 14 | class IScreenShotService final : public ServiceFramework<IScreenShotService> { |
| 15 | public: | 15 | public: |
| 16 | explicit CAPS_SS(Core::System& system_); | 16 | explicit IScreenShotService(Core::System& system_); |
| 17 | ~CAPS_SS() override; | 17 | ~IScreenShotService() override; |
| 18 | }; | 18 | }; |
| 19 | 19 | ||
| 20 | } // namespace Service::Capture | 20 | } // namespace Service::Capture |
diff --git a/src/core/hle/service/caps/caps_su.cpp b/src/core/hle/service/caps/caps_su.cpp index 3b11cc95c..cad173dc7 100644 --- a/src/core/hle/service/caps/caps_su.cpp +++ b/src/core/hle/service/caps/caps_su.cpp | |||
| @@ -7,10 +7,11 @@ | |||
| 7 | 7 | ||
| 8 | namespace Service::Capture { | 8 | namespace Service::Capture { |
| 9 | 9 | ||
| 10 | CAPS_SU::CAPS_SU(Core::System& system_) : ServiceFramework{system_, "caps:su"} { | 10 | IScreenShotApplicationService::IScreenShotApplicationService(Core::System& system_) |
| 11 | : ServiceFramework{system_, "caps:su"} { | ||
| 11 | // clang-format off | 12 | // clang-format off |
| 12 | static const FunctionInfo functions[] = { | 13 | static const FunctionInfo functions[] = { |
| 13 | {32, &CAPS_SU::SetShimLibraryVersion, "SetShimLibraryVersion"}, | 14 | {32, &IScreenShotApplicationService::SetShimLibraryVersion, "SetShimLibraryVersion"}, |
| 14 | {201, nullptr, "SaveScreenShot"}, | 15 | {201, nullptr, "SaveScreenShot"}, |
| 15 | {203, nullptr, "SaveScreenShotEx0"}, | 16 | {203, nullptr, "SaveScreenShotEx0"}, |
| 16 | {205, nullptr, "SaveScreenShotEx1"}, | 17 | {205, nullptr, "SaveScreenShotEx1"}, |
| @@ -21,9 +22,9 @@ CAPS_SU::CAPS_SU(Core::System& system_) : ServiceFramework{system_, "caps:su"} { | |||
| 21 | RegisterHandlers(functions); | 22 | RegisterHandlers(functions); |
| 22 | } | 23 | } |
| 23 | 24 | ||
| 24 | CAPS_SU::~CAPS_SU() = default; | 25 | IScreenShotApplicationService::~IScreenShotApplicationService() = default; |
| 25 | 26 | ||
| 26 | void CAPS_SU::SetShimLibraryVersion(HLERequestContext& ctx) { | 27 | void IScreenShotApplicationService::SetShimLibraryVersion(HLERequestContext& ctx) { |
| 27 | IPC::RequestParser rp{ctx}; | 28 | IPC::RequestParser rp{ctx}; |
| 28 | const auto library_version{rp.Pop<u64>()}; | 29 | const auto library_version{rp.Pop<u64>()}; |
| 29 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 30 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
diff --git a/src/core/hle/service/caps/caps_su.h b/src/core/hle/service/caps/caps_su.h index c6398858d..647e3059d 100644 --- a/src/core/hle/service/caps/caps_su.h +++ b/src/core/hle/service/caps/caps_su.h | |||
| @@ -11,10 +11,10 @@ class System; | |||
| 11 | 11 | ||
| 12 | namespace Service::Capture { | 12 | namespace Service::Capture { |
| 13 | 13 | ||
| 14 | class CAPS_SU final : public ServiceFramework<CAPS_SU> { | 14 | class IScreenShotApplicationService final : public ServiceFramework<IScreenShotApplicationService> { |
| 15 | public: | 15 | public: |
| 16 | explicit CAPS_SU(Core::System& system_); | 16 | explicit IScreenShotApplicationService(Core::System& system_); |
| 17 | ~CAPS_SU() override; | 17 | ~IScreenShotApplicationService() override; |
| 18 | 18 | ||
| 19 | private: | 19 | private: |
| 20 | void SetShimLibraryVersion(HLERequestContext& ctx); | 20 | void SetShimLibraryVersion(HLERequestContext& ctx); |
diff --git a/src/core/hle/service/caps/caps_types.h b/src/core/hle/service/caps/caps_types.h new file mode 100644 index 000000000..7fd357954 --- /dev/null +++ b/src/core/hle/service/caps/caps_types.h | |||
| @@ -0,0 +1,184 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/common_funcs.h" | ||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | namespace Service::Capture { | ||
| 10 | |||
| 11 | // This is nn::album::ImageOrientation | ||
| 12 | enum class AlbumImageOrientation { | ||
| 13 | None, | ||
| 14 | Rotate90, | ||
| 15 | Rotate180, | ||
| 16 | Rotate270, | ||
| 17 | }; | ||
| 18 | |||
| 19 | // This is nn::album::AlbumReportOption | ||
| 20 | enum class AlbumReportOption : s32 { | ||
| 21 | Disable, | ||
| 22 | Enable, | ||
| 23 | }; | ||
| 24 | |||
| 25 | enum class ContentType : u8 { | ||
| 26 | Screenshot = 0, | ||
| 27 | Movie = 1, | ||
| 28 | ExtraMovie = 3, | ||
| 29 | }; | ||
| 30 | |||
| 31 | enum class AlbumStorage : u8 { | ||
| 32 | Nand, | ||
| 33 | Sd, | ||
| 34 | }; | ||
| 35 | |||
| 36 | enum class ScreenShotDecoderFlag : u64 { | ||
| 37 | None = 0, | ||
| 38 | EnableFancyUpsampling = 1 << 0, | ||
| 39 | EnableBlockSmoothing = 1 << 1, | ||
| 40 | }; | ||
| 41 | |||
| 42 | // This is nn::capsrv::AlbumFileDateTime | ||
| 43 | struct AlbumFileDateTime { | ||
| 44 | s16 year{}; | ||
| 45 | s8 month{}; | ||
| 46 | s8 day{}; | ||
| 47 | s8 hour{}; | ||
| 48 | s8 minute{}; | ||
| 49 | s8 second{}; | ||
| 50 | s8 unique_id{}; | ||
| 51 | |||
| 52 | friend constexpr bool operator==(const AlbumFileDateTime&, const AlbumFileDateTime&) = default; | ||
| 53 | friend constexpr bool operator>(const AlbumFileDateTime& a, const AlbumFileDateTime& b) { | ||
| 54 | if (a.year > b.year) { | ||
| 55 | return true; | ||
| 56 | } | ||
| 57 | if (a.month > b.month) { | ||
| 58 | return true; | ||
| 59 | } | ||
| 60 | if (a.day > b.day) { | ||
| 61 | return true; | ||
| 62 | } | ||
| 63 | if (a.hour > b.hour) { | ||
| 64 | return true; | ||
| 65 | } | ||
| 66 | if (a.minute > b.minute) { | ||
| 67 | return true; | ||
| 68 | } | ||
| 69 | return a.second > b.second; | ||
| 70 | }; | ||
| 71 | friend constexpr bool operator<(const AlbumFileDateTime& a, const AlbumFileDateTime& b) { | ||
| 72 | if (a.year < b.year) { | ||
| 73 | return true; | ||
| 74 | } | ||
| 75 | if (a.month < b.month) { | ||
| 76 | return true; | ||
| 77 | } | ||
| 78 | if (a.day < b.day) { | ||
| 79 | return true; | ||
| 80 | } | ||
| 81 | if (a.hour < b.hour) { | ||
| 82 | return true; | ||
| 83 | } | ||
| 84 | if (a.minute < b.minute) { | ||
| 85 | return true; | ||
| 86 | } | ||
| 87 | return a.second < b.second; | ||
| 88 | }; | ||
| 89 | }; | ||
| 90 | static_assert(sizeof(AlbumFileDateTime) == 0x8, "AlbumFileDateTime has incorrect size."); | ||
| 91 | |||
| 92 | // This is nn::album::AlbumEntry | ||
| 93 | struct AlbumFileEntry { | ||
| 94 | u64 size{}; // Size of the entry | ||
| 95 | u64 hash{}; // AES256 with hardcoded key over AlbumEntry | ||
| 96 | AlbumFileDateTime datetime{}; | ||
| 97 | AlbumStorage storage{}; | ||
| 98 | ContentType content{}; | ||
| 99 | INSERT_PADDING_BYTES(5); | ||
| 100 | u8 unknown{}; // Set to 1 on official SW | ||
| 101 | }; | ||
| 102 | static_assert(sizeof(AlbumFileEntry) == 0x20, "AlbumFileEntry has incorrect size."); | ||
| 103 | |||
| 104 | struct AlbumFileId { | ||
| 105 | u64 application_id{}; | ||
| 106 | AlbumFileDateTime date{}; | ||
| 107 | AlbumStorage storage{}; | ||
| 108 | ContentType type{}; | ||
| 109 | INSERT_PADDING_BYTES(0x5); | ||
| 110 | u8 unknown{}; | ||
| 111 | |||
| 112 | friend constexpr bool operator==(const AlbumFileId&, const AlbumFileId&) = default; | ||
| 113 | }; | ||
| 114 | static_assert(sizeof(AlbumFileId) == 0x18, "AlbumFileId is an invalid size"); | ||
| 115 | |||
| 116 | // This is nn::capsrv::AlbumEntry | ||
| 117 | struct AlbumEntry { | ||
| 118 | u64 entry_size{}; | ||
| 119 | AlbumFileId file_id{}; | ||
| 120 | }; | ||
| 121 | static_assert(sizeof(AlbumEntry) == 0x20, "AlbumEntry has incorrect size."); | ||
| 122 | |||
| 123 | // This is nn::capsrv::ApplicationAlbumEntry | ||
| 124 | struct ApplicationAlbumEntry { | ||
| 125 | u64 size{}; // Size of the entry | ||
| 126 | u64 hash{}; // AES256 with hardcoded key over AlbumEntry | ||
| 127 | AlbumFileDateTime datetime{}; | ||
| 128 | AlbumStorage storage{}; | ||
| 129 | ContentType content{}; | ||
| 130 | INSERT_PADDING_BYTES(5); | ||
| 131 | u8 unknown{1}; // Set to 1 on official SW | ||
| 132 | }; | ||
| 133 | static_assert(sizeof(ApplicationAlbumEntry) == 0x20, "ApplicationAlbumEntry has incorrect size."); | ||
| 134 | |||
| 135 | // This is nn::capsrv::ApplicationAlbumFileEntry | ||
| 136 | struct ApplicationAlbumFileEntry { | ||
| 137 | ApplicationAlbumEntry entry{}; | ||
| 138 | AlbumFileDateTime datetime{}; | ||
| 139 | u64 unknown{}; | ||
| 140 | }; | ||
| 141 | static_assert(sizeof(ApplicationAlbumFileEntry) == 0x30, | ||
| 142 | "ApplicationAlbumFileEntry has incorrect size."); | ||
| 143 | |||
| 144 | struct ApplicationData { | ||
| 145 | std::array<u8, 0x400> data{}; | ||
| 146 | u32 data_size{}; | ||
| 147 | }; | ||
| 148 | static_assert(sizeof(ApplicationData) == 0x404, "ApplicationData is an invalid size"); | ||
| 149 | |||
| 150 | struct ScreenShotAttribute { | ||
| 151 | u32 unknown_0{}; | ||
| 152 | AlbumImageOrientation orientation{}; | ||
| 153 | u32 unknown_1{}; | ||
| 154 | u32 unknown_2{}; | ||
| 155 | INSERT_PADDING_BYTES(0x30); | ||
| 156 | }; | ||
| 157 | static_assert(sizeof(ScreenShotAttribute) == 0x40, "ScreenShotAttribute is an invalid size"); | ||
| 158 | |||
| 159 | struct ScreenShotDecodeOption { | ||
| 160 | ScreenShotDecoderFlag flags{}; | ||
| 161 | INSERT_PADDING_BYTES(0x18); | ||
| 162 | }; | ||
| 163 | static_assert(sizeof(ScreenShotDecodeOption) == 0x20, "ScreenShotDecodeOption is an invalid size"); | ||
| 164 | |||
| 165 | struct LoadAlbumScreenShotImageOutput { | ||
| 166 | s64 width{}; | ||
| 167 | s64 height{}; | ||
| 168 | ScreenShotAttribute attribute{}; | ||
| 169 | INSERT_PADDING_BYTES(0x400); | ||
| 170 | }; | ||
| 171 | static_assert(sizeof(LoadAlbumScreenShotImageOutput) == 0x450, | ||
| 172 | "LoadAlbumScreenShotImageOutput is an invalid size"); | ||
| 173 | |||
| 174 | struct LoadAlbumScreenShotImageOutputForApplication { | ||
| 175 | s64 width{}; | ||
| 176 | s64 height{}; | ||
| 177 | ScreenShotAttribute attribute{}; | ||
| 178 | ApplicationData data{}; | ||
| 179 | INSERT_PADDING_BYTES(0xAC); | ||
| 180 | }; | ||
| 181 | static_assert(sizeof(LoadAlbumScreenShotImageOutputForApplication) == 0x500, | ||
| 182 | "LoadAlbumScreenShotImageOutput is an invalid size"); | ||
| 183 | |||
| 184 | } // namespace Service::Capture | ||
diff --git a/src/core/hle/service/caps/caps_u.cpp b/src/core/hle/service/caps/caps_u.cpp index bffe0f8d0..b6b33fb2f 100644 --- a/src/core/hle/service/caps/caps_u.cpp +++ b/src/core/hle/service/caps/caps_u.cpp | |||
| @@ -2,45 +2,29 @@ | |||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "common/logging/log.h" | 4 | #include "common/logging/log.h" |
| 5 | #include "core/hle/service/caps/caps.h" | 5 | #include "core/hle/service/caps/caps_manager.h" |
| 6 | #include "core/hle/service/caps/caps_types.h" | ||
| 6 | #include "core/hle/service/caps/caps_u.h" | 7 | #include "core/hle/service/caps/caps_u.h" |
| 7 | #include "core/hle/service/ipc_helpers.h" | 8 | #include "core/hle/service/ipc_helpers.h" |
| 8 | 9 | ||
| 9 | namespace Service::Capture { | 10 | namespace Service::Capture { |
| 10 | 11 | ||
| 11 | class IAlbumAccessorApplicationSession final | 12 | IAlbumApplicationService::IAlbumApplicationService(Core::System& system_, |
| 12 | : public ServiceFramework<IAlbumAccessorApplicationSession> { | 13 | std::shared_ptr<AlbumManager> album_manager) |
| 13 | public: | 14 | : ServiceFramework{system_, "caps:u"}, manager{album_manager} { |
| 14 | explicit IAlbumAccessorApplicationSession(Core::System& system_) | ||
| 15 | : ServiceFramework{system_, "IAlbumAccessorApplicationSession"} { | ||
| 16 | // clang-format off | ||
| 17 | static const FunctionInfo functions[] = { | ||
| 18 | {2001, nullptr, "OpenAlbumMovieReadStream"}, | ||
| 19 | {2002, nullptr, "CloseAlbumMovieReadStream"}, | ||
| 20 | {2003, nullptr, "GetAlbumMovieReadStreamMovieDataSize"}, | ||
| 21 | {2004, nullptr, "ReadMovieDataFromAlbumMovieReadStream"}, | ||
| 22 | {2005, nullptr, "GetAlbumMovieReadStreamBrokenReason"}, | ||
| 23 | }; | ||
| 24 | // clang-format on | ||
| 25 | |||
| 26 | RegisterHandlers(functions); | ||
| 27 | } | ||
| 28 | }; | ||
| 29 | |||
| 30 | CAPS_U::CAPS_U(Core::System& system_) : ServiceFramework{system_, "caps:u"} { | ||
| 31 | // clang-format off | 15 | // clang-format off |
| 32 | static const FunctionInfo functions[] = { | 16 | static const FunctionInfo functions[] = { |
| 33 | {32, &CAPS_U::SetShimLibraryVersion, "SetShimLibraryVersion"}, | 17 | {32, &IAlbumApplicationService::SetShimLibraryVersion, "SetShimLibraryVersion"}, |
| 34 | {102, &CAPS_U::GetAlbumContentsFileListForApplication, "GetAlbumContentsFileListForApplication"}, | 18 | {102, &IAlbumApplicationService::GetAlbumFileList0AafeAruidDeprecated, "GetAlbumFileList0AafeAruidDeprecated"}, |
| 35 | {103, nullptr, "DeleteAlbumContentsFileForApplication"}, | 19 | {103, nullptr, "DeleteAlbumFileByAruid"}, |
| 36 | {104, nullptr, "GetAlbumContentsFileSizeForApplication"}, | 20 | {104, nullptr, "GetAlbumFileSizeByAruid"}, |
| 37 | {105, nullptr, "DeleteAlbumFileByAruidForDebug"}, | 21 | {105, nullptr, "DeleteAlbumFileByAruidForDebug"}, |
| 38 | {110, nullptr, "LoadAlbumContentsFileScreenShotImageForApplication"}, | 22 | {110, nullptr, "LoadAlbumScreenShotImageByAruid"}, |
| 39 | {120, nullptr, "LoadAlbumContentsFileThumbnailImageForApplication"}, | 23 | {120, nullptr, "LoadAlbumScreenShotThumbnailImageByAruid"}, |
| 40 | {130, nullptr, "PrecheckToCreateContentsForApplication"}, | 24 | {130, nullptr, "PrecheckToCreateContentsByAruid"}, |
| 41 | {140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"}, | 25 | {140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"}, |
| 42 | {141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"}, | 26 | {141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"}, |
| 43 | {142, &CAPS_U::GetAlbumFileList3AaeAruid, "GetAlbumFileList3AaeAruid"}, | 27 | {142, &IAlbumApplicationService::GetAlbumFileList3AaeAruid, "GetAlbumFileList3AaeAruid"}, |
| 44 | {143, nullptr, "GetAlbumFileList4AaeUidAruid"}, | 28 | {143, nullptr, "GetAlbumFileList4AaeUidAruid"}, |
| 45 | {144, nullptr, "GetAllAlbumFileList3AaeAruid"}, | 29 | {144, nullptr, "GetAllAlbumFileList3AaeAruid"}, |
| 46 | {60002, nullptr, "OpenAccessorSessionForApplication"}, | 30 | {60002, nullptr, "OpenAccessorSessionForApplication"}, |
| @@ -50,9 +34,9 @@ CAPS_U::CAPS_U(Core::System& system_) : ServiceFramework{system_, "caps:u"} { | |||
| 50 | RegisterHandlers(functions); | 34 | RegisterHandlers(functions); |
| 51 | } | 35 | } |
| 52 | 36 | ||
| 53 | CAPS_U::~CAPS_U() = default; | 37 | IAlbumApplicationService::~IAlbumApplicationService() = default; |
| 54 | 38 | ||
| 55 | void CAPS_U::SetShimLibraryVersion(HLERequestContext& ctx) { | 39 | void IAlbumApplicationService::SetShimLibraryVersion(HLERequestContext& ctx) { |
| 56 | IPC::RequestParser rp{ctx}; | 40 | IPC::RequestParser rp{ctx}; |
| 57 | const auto library_version{rp.Pop<u64>()}; | 41 | const auto library_version{rp.Pop<u64>()}; |
| 58 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 42 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| @@ -64,37 +48,89 @@ void CAPS_U::SetShimLibraryVersion(HLERequestContext& ctx) { | |||
| 64 | rb.Push(ResultSuccess); | 48 | rb.Push(ResultSuccess); |
| 65 | } | 49 | } |
| 66 | 50 | ||
| 67 | void CAPS_U::GetAlbumContentsFileListForApplication(HLERequestContext& ctx) { | 51 | void IAlbumApplicationService::GetAlbumFileList0AafeAruidDeprecated(HLERequestContext& ctx) { |
| 68 | // Takes a type-0x6 output buffer containing an array of ApplicationAlbumFileEntry, a PID, an | ||
| 69 | // u8 ContentType, two s64s, and an u64 AppletResourceUserId. Returns an output u64 for total | ||
| 70 | // output entries (which is copied to a s32 by official SW). | ||
| 71 | IPC::RequestParser rp{ctx}; | 52 | IPC::RequestParser rp{ctx}; |
| 72 | const auto pid{rp.Pop<s32>()}; | 53 | struct Parameters { |
| 73 | const auto content_type{rp.PopEnum<ContentType>()}; | 54 | ContentType content_type; |
| 74 | const auto start_posix_time{rp.Pop<s64>()}; | 55 | INSERT_PADDING_BYTES(7); |
| 75 | const auto end_posix_time{rp.Pop<s64>()}; | 56 | s64 start_posix_time; |
| 76 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 57 | s64 end_posix_time; |
| 58 | u64 applet_resource_user_id; | ||
| 59 | }; | ||
| 60 | static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size."); | ||
| 77 | 61 | ||
| 78 | // TODO: Update this when we implement the album. | 62 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 79 | // Currently we do not have a method of accessing album entries, set this to 0 for now. | ||
| 80 | constexpr u32 total_entries_1{}; | ||
| 81 | constexpr u32 total_entries_2{}; | ||
| 82 | 63 | ||
| 83 | LOG_WARNING( | 64 | LOG_WARNING(Service_Capture, |
| 84 | Service_Capture, | 65 | "(STUBBED) called. content_type={}, start_posix_time={}, end_posix_time={}, " |
| 85 | "(STUBBED) called. pid={}, content_type={}, start_posix_time={}, " | 66 | "applet_resource_user_id={}", |
| 86 | "end_posix_time={}, applet_resource_user_id={}, total_entries_1={}, total_entries_2={}", | 67 | parameters.content_type, parameters.start_posix_time, parameters.end_posix_time, |
| 87 | pid, content_type, start_posix_time, end_posix_time, applet_resource_user_id, | 68 | parameters.applet_resource_user_id); |
| 88 | total_entries_1, total_entries_2); | 69 | |
| 70 | Result result = ResultSuccess; | ||
| 71 | |||
| 72 | if (result.IsSuccess()) { | ||
| 73 | result = manager->IsAlbumMounted(AlbumStorage::Sd); | ||
| 74 | } | ||
| 75 | |||
| 76 | std::vector<ApplicationAlbumFileEntry> entries; | ||
| 77 | if (result.IsSuccess()) { | ||
| 78 | result = manager->GetAlbumFileList(entries, parameters.content_type, | ||
| 79 | parameters.start_posix_time, parameters.end_posix_time, | ||
| 80 | parameters.applet_resource_user_id); | ||
| 81 | } | ||
| 82 | |||
| 83 | if (!entries.empty()) { | ||
| 84 | ctx.WriteBuffer(entries); | ||
| 85 | } | ||
| 89 | 86 | ||
| 90 | IPC::ResponseBuilder rb{ctx, 4}; | 87 | IPC::ResponseBuilder rb{ctx, 4}; |
| 91 | rb.Push(ResultSuccess); | 88 | rb.Push(result); |
| 92 | rb.Push(total_entries_1); | 89 | rb.Push<u64>(entries.size()); |
| 93 | rb.Push(total_entries_2); | ||
| 94 | } | 90 | } |
| 95 | 91 | ||
| 96 | void CAPS_U::GetAlbumFileList3AaeAruid(HLERequestContext& ctx) { | 92 | void IAlbumApplicationService::GetAlbumFileList3AaeAruid(HLERequestContext& ctx) { |
| 97 | GetAlbumContentsFileListForApplication(ctx); | 93 | IPC::RequestParser rp{ctx}; |
| 94 | struct Parameters { | ||
| 95 | ContentType content_type; | ||
| 96 | INSERT_PADDING_BYTES(1); | ||
| 97 | AlbumFileDateTime start_date_time; | ||
| 98 | AlbumFileDateTime end_date_time; | ||
| 99 | INSERT_PADDING_BYTES(6); | ||
| 100 | u64 applet_resource_user_id; | ||
| 101 | }; | ||
| 102 | static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size."); | ||
| 103 | |||
| 104 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 105 | |||
| 106 | LOG_WARNING(Service_Capture, | ||
| 107 | "(STUBBED) called. content_type={}, start_date={}/{}/{}, " | ||
| 108 | "end_date={}/{}/{}, applet_resource_user_id={}", | ||
| 109 | parameters.content_type, parameters.start_date_time.year, | ||
| 110 | parameters.start_date_time.month, parameters.start_date_time.day, | ||
| 111 | parameters.end_date_time.year, parameters.end_date_time.month, | ||
| 112 | parameters.end_date_time.day, parameters.applet_resource_user_id); | ||
| 113 | |||
| 114 | Result result = ResultSuccess; | ||
| 115 | |||
| 116 | if (result.IsSuccess()) { | ||
| 117 | result = manager->IsAlbumMounted(AlbumStorage::Sd); | ||
| 118 | } | ||
| 119 | |||
| 120 | std::vector<ApplicationAlbumEntry> entries; | ||
| 121 | if (result.IsSuccess()) { | ||
| 122 | result = | ||
| 123 | manager->GetAlbumFileList(entries, parameters.content_type, parameters.start_date_time, | ||
| 124 | parameters.end_date_time, parameters.applet_resource_user_id); | ||
| 125 | } | ||
| 126 | |||
| 127 | if (!entries.empty()) { | ||
| 128 | ctx.WriteBuffer(entries); | ||
| 129 | } | ||
| 130 | |||
| 131 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 132 | rb.Push(result); | ||
| 133 | rb.Push<u64>(entries.size()); | ||
| 98 | } | 134 | } |
| 99 | 135 | ||
| 100 | } // namespace Service::Capture | 136 | } // namespace Service::Capture |
diff --git a/src/core/hle/service/caps/caps_u.h b/src/core/hle/service/caps/caps_u.h index e8dd037d7..9458c128e 100644 --- a/src/core/hle/service/caps/caps_u.h +++ b/src/core/hle/service/caps/caps_u.h | |||
| @@ -10,16 +10,20 @@ class System; | |||
| 10 | } | 10 | } |
| 11 | 11 | ||
| 12 | namespace Service::Capture { | 12 | namespace Service::Capture { |
| 13 | class AlbumManager; | ||
| 13 | 14 | ||
| 14 | class CAPS_U final : public ServiceFramework<CAPS_U> { | 15 | class IAlbumApplicationService final : public ServiceFramework<IAlbumApplicationService> { |
| 15 | public: | 16 | public: |
| 16 | explicit CAPS_U(Core::System& system_); | 17 | explicit IAlbumApplicationService(Core::System& system_, |
| 17 | ~CAPS_U() override; | 18 | std::shared_ptr<AlbumManager> album_manager); |
| 19 | ~IAlbumApplicationService() override; | ||
| 18 | 20 | ||
| 19 | private: | 21 | private: |
| 20 | void SetShimLibraryVersion(HLERequestContext& ctx); | 22 | void SetShimLibraryVersion(HLERequestContext& ctx); |
| 21 | void GetAlbumContentsFileListForApplication(HLERequestContext& ctx); | 23 | void GetAlbumFileList0AafeAruidDeprecated(HLERequestContext& ctx); |
| 22 | void GetAlbumFileList3AaeAruid(HLERequestContext& ctx); | 24 | void GetAlbumFileList3AaeAruid(HLERequestContext& ctx); |
| 25 | |||
| 26 | std::shared_ptr<AlbumManager> manager = nullptr; | ||
| 23 | }; | 27 | }; |
| 24 | 28 | ||
| 25 | } // namespace Service::Capture | 29 | } // namespace Service::Capture |
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index c2054e8a0..126cd6ffd 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp | |||
| @@ -855,6 +855,9 @@ FSP_SRV::FSP_SRV(Core::System& system_) | |||
| 855 | if (Settings::values.enable_fs_access_log) { | 855 | if (Settings::values.enable_fs_access_log) { |
| 856 | access_log_mode = AccessLogMode::SdCard; | 856 | access_log_mode = AccessLogMode::SdCard; |
| 857 | } | 857 | } |
| 858 | |||
| 859 | // This should be true on creation | ||
| 860 | fsc.SetAutoSaveDataCreation(true); | ||
| 858 | } | 861 | } |
| 859 | 862 | ||
| 860 | FSP_SRV::~FSP_SRV() = default; | 863 | FSP_SRV::~FSP_SRV() = default; |
diff --git a/src/core/hle/service/jit/jit_context.cpp b/src/core/hle/service/jit/jit_context.cpp index 4ed3f02e2..0090e8568 100644 --- a/src/core/hle/service/jit/jit_context.cpp +++ b/src/core/hle/service/jit/jit_context.cpp | |||
| @@ -156,6 +156,8 @@ public: | |||
| 156 | 156 | ||
| 157 | bool LoadNRO(std::span<const u8> data) { | 157 | bool LoadNRO(std::span<const u8> data) { |
| 158 | local_memory.clear(); | 158 | local_memory.clear(); |
| 159 | |||
| 160 | relocbase = local_memory.size(); | ||
| 159 | local_memory.insert(local_memory.end(), data.begin(), data.end()); | 161 | local_memory.insert(local_memory.end(), data.begin(), data.end()); |
| 160 | 162 | ||
| 161 | if (FixupRelocations()) { | 163 | if (FixupRelocations()) { |
| @@ -181,8 +183,8 @@ public: | |||
| 181 | // https://refspecs.linuxbase.org/elf/gabi4+/ch5.dynamic.html | 183 | // https://refspecs.linuxbase.org/elf/gabi4+/ch5.dynamic.html |
| 182 | // https://refspecs.linuxbase.org/elf/gabi4+/ch4.reloc.html | 184 | // https://refspecs.linuxbase.org/elf/gabi4+/ch4.reloc.html |
| 183 | VAddr dynamic_offset{mod_offset + callbacks->MemoryRead32(mod_offset + 4)}; | 185 | VAddr dynamic_offset{mod_offset + callbacks->MemoryRead32(mod_offset + 4)}; |
| 184 | VAddr rela_dyn = 0; | 186 | VAddr rela_dyn = 0, relr_dyn = 0; |
| 185 | size_t num_rela = 0; | 187 | size_t num_rela = 0, num_relr = 0; |
| 186 | while (true) { | 188 | while (true) { |
| 187 | const auto dyn{callbacks->ReadMemory<Elf64_Dyn>(dynamic_offset)}; | 189 | const auto dyn{callbacks->ReadMemory<Elf64_Dyn>(dynamic_offset)}; |
| 188 | dynamic_offset += sizeof(Elf64_Dyn); | 190 | dynamic_offset += sizeof(Elf64_Dyn); |
| @@ -196,6 +198,12 @@ public: | |||
| 196 | if (dyn.d_tag == ElfDtRelasz) { | 198 | if (dyn.d_tag == ElfDtRelasz) { |
| 197 | num_rela = dyn.d_un.d_val / sizeof(Elf64_Rela); | 199 | num_rela = dyn.d_un.d_val / sizeof(Elf64_Rela); |
| 198 | } | 200 | } |
| 201 | if (dyn.d_tag == ElfDtRelr) { | ||
| 202 | relr_dyn = dyn.d_un.d_ptr; | ||
| 203 | } | ||
| 204 | if (dyn.d_tag == ElfDtRelrsz) { | ||
| 205 | num_relr = dyn.d_un.d_val / sizeof(Elf64_Relr); | ||
| 206 | } | ||
| 199 | } | 207 | } |
| 200 | 208 | ||
| 201 | for (size_t i = 0; i < num_rela; i++) { | 209 | for (size_t i = 0; i < num_rela; i++) { |
| @@ -207,6 +215,29 @@ public: | |||
| 207 | callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend); | 215 | callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend); |
| 208 | } | 216 | } |
| 209 | 217 | ||
| 218 | VAddr relr_where = 0; | ||
| 219 | for (size_t i = 0; i < num_relr; i++) { | ||
| 220 | const auto relr{callbacks->ReadMemory<Elf64_Relr>(relr_dyn + i * sizeof(Elf64_Relr))}; | ||
| 221 | const auto incr{[&](VAddr where) { | ||
| 222 | callbacks->MemoryWrite64(where, callbacks->MemoryRead64(where) + relocbase); | ||
| 223 | }}; | ||
| 224 | |||
| 225 | if ((relr & 1) == 0) { | ||
| 226 | // where pointer | ||
| 227 | relr_where = relocbase + relr; | ||
| 228 | incr(relr_where); | ||
| 229 | relr_where += sizeof(Elf64_Addr); | ||
| 230 | } else { | ||
| 231 | // bitmap | ||
| 232 | for (int bit = 1; bit < 64; bit++) { | ||
| 233 | if ((relr & (1ULL << bit)) != 0) { | ||
| 234 | incr(relr_where + i * sizeof(Elf64_Addr)); | ||
| 235 | } | ||
| 236 | } | ||
| 237 | relr_where += 63 * sizeof(Elf64_Addr); | ||
| 238 | } | ||
| 239 | } | ||
| 240 | |||
| 210 | return true; | 241 | return true; |
| 211 | } | 242 | } |
| 212 | 243 | ||
| @@ -313,6 +344,7 @@ public: | |||
| 313 | Core::Memory::Memory& memory; | 344 | Core::Memory::Memory& memory; |
| 314 | VAddr top_of_stack; | 345 | VAddr top_of_stack; |
| 315 | VAddr heap_pointer; | 346 | VAddr heap_pointer; |
| 347 | VAddr relocbase; | ||
| 316 | }; | 348 | }; |
| 317 | 349 | ||
| 318 | void DynarmicCallbacks64::CallSVC(u32 swi) { | 350 | void DynarmicCallbacks64::CallSVC(u32 swi) { |
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp index 9d149a7cd..7927f8264 100644 --- a/src/core/hle/service/ldn/ldn.cpp +++ b/src/core/hle/service/ldn/ldn.cpp | |||
| @@ -23,19 +23,39 @@ public: | |||
| 23 | explicit IMonitorService(Core::System& system_) : ServiceFramework{system_, "IMonitorService"} { | 23 | explicit IMonitorService(Core::System& system_) : ServiceFramework{system_, "IMonitorService"} { |
| 24 | // clang-format off | 24 | // clang-format off |
| 25 | static const FunctionInfo functions[] = { | 25 | static const FunctionInfo functions[] = { |
| 26 | {0, nullptr, "GetStateForMonitor"}, | 26 | {0, &IMonitorService::GetStateForMonitor, "GetStateForMonitor"}, |
| 27 | {1, nullptr, "GetNetworkInfoForMonitor"}, | 27 | {1, nullptr, "GetNetworkInfoForMonitor"}, |
| 28 | {2, nullptr, "GetIpv4AddressForMonitor"}, | 28 | {2, nullptr, "GetIpv4AddressForMonitor"}, |
| 29 | {3, nullptr, "GetDisconnectReasonForMonitor"}, | 29 | {3, nullptr, "GetDisconnectReasonForMonitor"}, |
| 30 | {4, nullptr, "GetSecurityParameterForMonitor"}, | 30 | {4, nullptr, "GetSecurityParameterForMonitor"}, |
| 31 | {5, nullptr, "GetNetworkConfigForMonitor"}, | 31 | {5, nullptr, "GetNetworkConfigForMonitor"}, |
| 32 | {100, nullptr, "InitializeMonitor"}, | 32 | {100, &IMonitorService::InitializeMonitor, "InitializeMonitor"}, |
| 33 | {101, nullptr, "FinalizeMonitor"}, | 33 | {101, nullptr, "FinalizeMonitor"}, |
| 34 | }; | 34 | }; |
| 35 | // clang-format on | 35 | // clang-format on |
| 36 | 36 | ||
| 37 | RegisterHandlers(functions); | 37 | RegisterHandlers(functions); |
| 38 | } | 38 | } |
| 39 | |||
| 40 | private: | ||
| 41 | void GetStateForMonitor(HLERequestContext& ctx) { | ||
| 42 | LOG_INFO(Service_LDN, "called"); | ||
| 43 | |||
| 44 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 45 | rb.Push(ResultSuccess); | ||
| 46 | rb.PushEnum(state); | ||
| 47 | } | ||
| 48 | |||
| 49 | void InitializeMonitor(HLERequestContext& ctx) { | ||
| 50 | LOG_INFO(Service_LDN, "called"); | ||
| 51 | |||
| 52 | state = State::Initialized; | ||
| 53 | |||
| 54 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 55 | rb.Push(ResultSuccess); | ||
| 56 | } | ||
| 57 | |||
| 58 | State state{State::None}; | ||
| 39 | }; | 59 | }; |
| 40 | 60 | ||
| 41 | class LDNM final : public ServiceFramework<LDNM> { | 61 | class LDNM final : public ServiceFramework<LDNM> { |
| @@ -731,14 +751,81 @@ public: | |||
| 731 | } | 751 | } |
| 732 | }; | 752 | }; |
| 733 | 753 | ||
| 754 | class ISfMonitorService final : public ServiceFramework<ISfMonitorService> { | ||
| 755 | public: | ||
| 756 | explicit ISfMonitorService(Core::System& system_) | ||
| 757 | : ServiceFramework{system_, "ISfMonitorService"} { | ||
| 758 | // clang-format off | ||
| 759 | static const FunctionInfo functions[] = { | ||
| 760 | {0, &ISfMonitorService::Initialize, "Initialize"}, | ||
| 761 | {288, &ISfMonitorService::GetGroupInfo, "GetGroupInfo"}, | ||
| 762 | {320, nullptr, "GetLinkLevel"}, | ||
| 763 | }; | ||
| 764 | // clang-format on | ||
| 765 | |||
| 766 | RegisterHandlers(functions); | ||
| 767 | } | ||
| 768 | |||
| 769 | private: | ||
| 770 | void Initialize(HLERequestContext& ctx) { | ||
| 771 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | ||
| 772 | |||
| 773 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 774 | rb.Push(ResultSuccess); | ||
| 775 | rb.Push(0); | ||
| 776 | } | ||
| 777 | |||
| 778 | void GetGroupInfo(HLERequestContext& ctx) { | ||
| 779 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | ||
| 780 | |||
| 781 | struct GroupInfo { | ||
| 782 | std::array<u8, 0x200> info; | ||
| 783 | }; | ||
| 784 | |||
| 785 | GroupInfo group_info{}; | ||
| 786 | |||
| 787 | ctx.WriteBuffer(group_info); | ||
| 788 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 789 | rb.Push(ResultSuccess); | ||
| 790 | } | ||
| 791 | }; | ||
| 792 | |||
| 793 | class LP2PM final : public ServiceFramework<LP2PM> { | ||
| 794 | public: | ||
| 795 | explicit LP2PM(Core::System& system_) : ServiceFramework{system_, "lp2p:m"} { | ||
| 796 | // clang-format off | ||
| 797 | static const FunctionInfo functions[] = { | ||
| 798 | {0, &LP2PM::CreateMonitorService, "CreateMonitorService"}, | ||
| 799 | }; | ||
| 800 | // clang-format on | ||
| 801 | |||
| 802 | RegisterHandlers(functions); | ||
| 803 | } | ||
| 804 | |||
| 805 | private: | ||
| 806 | void CreateMonitorService(HLERequestContext& ctx) { | ||
| 807 | IPC::RequestParser rp{ctx}; | ||
| 808 | const u64 reserved_input = rp.Pop<u64>(); | ||
| 809 | |||
| 810 | LOG_INFO(Service_LDN, "called, reserved_input={}", reserved_input); | ||
| 811 | |||
| 812 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 813 | rb.Push(ResultSuccess); | ||
| 814 | rb.PushIpcInterface<ISfMonitorService>(system); | ||
| 815 | } | ||
| 816 | }; | ||
| 817 | |||
| 734 | void LoopProcess(Core::System& system) { | 818 | void LoopProcess(Core::System& system) { |
| 735 | auto server_manager = std::make_unique<ServerManager>(system); | 819 | auto server_manager = std::make_unique<ServerManager>(system); |
| 736 | 820 | ||
| 737 | server_manager->RegisterNamedService("ldn:m", std::make_shared<LDNM>(system)); | 821 | server_manager->RegisterNamedService("ldn:m", std::make_shared<LDNM>(system)); |
| 738 | server_manager->RegisterNamedService("ldn:s", std::make_shared<LDNS>(system)); | 822 | server_manager->RegisterNamedService("ldn:s", std::make_shared<LDNS>(system)); |
| 739 | server_manager->RegisterNamedService("ldn:u", std::make_shared<LDNU>(system)); | 823 | server_manager->RegisterNamedService("ldn:u", std::make_shared<LDNU>(system)); |
| 824 | |||
| 740 | server_manager->RegisterNamedService("lp2p:app", std::make_shared<LP2PAPP>(system)); | 825 | server_manager->RegisterNamedService("lp2p:app", std::make_shared<LP2PAPP>(system)); |
| 741 | server_manager->RegisterNamedService("lp2p:sys", std::make_shared<LP2PSYS>(system)); | 826 | server_manager->RegisterNamedService("lp2p:sys", std::make_shared<LP2PSYS>(system)); |
| 827 | server_manager->RegisterNamedService("lp2p:m", std::make_shared<LP2PM>(system)); | ||
| 828 | |||
| 742 | ServerManager::RunServer(std::move(server_manager)); | 829 | ServerManager::RunServer(std::move(server_manager)); |
| 743 | } | 830 | } |
| 744 | 831 | ||
diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp index 68c407f81..e7a00deb3 100644 --- a/src/core/hle/service/nfc/common/device.cpp +++ b/src/core/hle/service/nfc/common/device.cpp | |||
| @@ -830,11 +830,6 @@ Result NfcDevice::SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& registe | |||
| 830 | return ResultWrongDeviceState; | 830 | return ResultWrongDeviceState; |
| 831 | } | 831 | } |
| 832 | 832 | ||
| 833 | Service::Mii::StoreData store_data{}; | ||
| 834 | Service::Mii::NfpStoreDataExtension extension{}; | ||
| 835 | store_data.BuildBase(Mii::Gender::Male); | ||
| 836 | extension.SetFromStoreData(store_data); | ||
| 837 | |||
| 838 | auto& settings = tag_data.settings; | 833 | auto& settings = tag_data.settings; |
| 839 | 834 | ||
| 840 | if (tag_data.settings.settings.amiibo_initialized == 0) { | 835 | if (tag_data.settings.settings.amiibo_initialized == 0) { |
| @@ -843,8 +838,8 @@ Result NfcDevice::SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& registe | |||
| 843 | } | 838 | } |
| 844 | 839 | ||
| 845 | SetAmiiboName(settings, register_info.amiibo_name); | 840 | SetAmiiboName(settings, register_info.amiibo_name); |
| 846 | tag_data.owner_mii.BuildFromStoreData(store_data); | 841 | tag_data.owner_mii.BuildFromStoreData(register_info.mii_store_data); |
| 847 | tag_data.mii_extension = extension; | 842 | tag_data.mii_extension.SetFromStoreData(register_info.mii_store_data); |
| 848 | tag_data.unknown = 0; | 843 | tag_data.unknown = 0; |
| 849 | tag_data.unknown2 = {}; | 844 | tag_data.unknown2 = {}; |
| 850 | settings.country_code_id = 0; | 845 | settings.country_code_id = 0; |
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index 21b06d10b..22dc55a6d 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp | |||
| @@ -545,6 +545,16 @@ void IGeneralService::IsAnyInternetRequestAccepted(HLERequestContext& ctx) { | |||
| 545 | } | 545 | } |
| 546 | } | 546 | } |
| 547 | 547 | ||
| 548 | void IGeneralService::IsAnyForegroundRequestAccepted(HLERequestContext& ctx) { | ||
| 549 | const bool is_accepted{}; | ||
| 550 | |||
| 551 | LOG_WARNING(Service_NIFM, "(STUBBED) called, is_accepted={}", is_accepted); | ||
| 552 | |||
| 553 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 554 | rb.Push(ResultSuccess); | ||
| 555 | rb.Push<u8>(is_accepted); | ||
| 556 | } | ||
| 557 | |||
| 548 | IGeneralService::IGeneralService(Core::System& system_) | 558 | IGeneralService::IGeneralService(Core::System& system_) |
| 549 | : ServiceFramework{system_, "IGeneralService"}, network{system_.GetRoomNetwork()} { | 559 | : ServiceFramework{system_, "IGeneralService"}, network{system_.GetRoomNetwork()} { |
| 550 | // clang-format off | 560 | // clang-format off |
| @@ -569,7 +579,7 @@ IGeneralService::IGeneralService(Core::System& system_) | |||
| 569 | {19, nullptr, "SetEthernetCommunicationEnabled"}, | 579 | {19, nullptr, "SetEthernetCommunicationEnabled"}, |
| 570 | {20, &IGeneralService::IsEthernetCommunicationEnabled, "IsEthernetCommunicationEnabled"}, | 580 | {20, &IGeneralService::IsEthernetCommunicationEnabled, "IsEthernetCommunicationEnabled"}, |
| 571 | {21, &IGeneralService::IsAnyInternetRequestAccepted, "IsAnyInternetRequestAccepted"}, | 581 | {21, &IGeneralService::IsAnyInternetRequestAccepted, "IsAnyInternetRequestAccepted"}, |
| 572 | {22, nullptr, "IsAnyForegroundRequestAccepted"}, | 582 | {22, &IGeneralService::IsAnyForegroundRequestAccepted, "IsAnyForegroundRequestAccepted"}, |
| 573 | {23, nullptr, "PutToSleep"}, | 583 | {23, nullptr, "PutToSleep"}, |
| 574 | {24, nullptr, "WakeUp"}, | 584 | {24, nullptr, "WakeUp"}, |
| 575 | {25, nullptr, "GetSsidListVersion"}, | 585 | {25, nullptr, "GetSsidListVersion"}, |
diff --git a/src/core/hle/service/nifm/nifm.h b/src/core/hle/service/nifm/nifm.h index ae99c4695..b74b66438 100644 --- a/src/core/hle/service/nifm/nifm.h +++ b/src/core/hle/service/nifm/nifm.h | |||
| @@ -35,6 +35,7 @@ private: | |||
| 35 | void GetInternetConnectionStatus(HLERequestContext& ctx); | 35 | void GetInternetConnectionStatus(HLERequestContext& ctx); |
| 36 | void IsEthernetCommunicationEnabled(HLERequestContext& ctx); | 36 | void IsEthernetCommunicationEnabled(HLERequestContext& ctx); |
| 37 | void IsAnyInternetRequestAccepted(HLERequestContext& ctx); | 37 | void IsAnyInternetRequestAccepted(HLERequestContext& ctx); |
| 38 | void IsAnyForegroundRequestAccepted(HLERequestContext& ctx); | ||
| 38 | 39 | ||
| 39 | Network::RoomNetwork& network; | 40 | Network::RoomNetwork& network; |
| 40 | }; | 41 | }; |
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index 6e0baf0be..f9e0e272d 100644 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include "core/file_sys/control_metadata.h" | 7 | #include "core/file_sys/control_metadata.h" |
| 8 | #include "core/file_sys/patch_manager.h" | 8 | #include "core/file_sys/patch_manager.h" |
| 9 | #include "core/file_sys/vfs.h" | 9 | #include "core/file_sys/vfs.h" |
| 10 | #include "core/hle/service/filesystem/filesystem.h" | ||
| 10 | #include "core/hle/service/glue/glue_manager.h" | 11 | #include "core/hle/service/glue/glue_manager.h" |
| 11 | #include "core/hle/service/ipc_helpers.h" | 12 | #include "core/hle/service/ipc_helpers.h" |
| 12 | #include "core/hle/service/ns/errors.h" | 13 | #include "core/hle/service/ns/errors.h" |
| @@ -502,8 +503,8 @@ IContentManagementInterface::IContentManagementInterface(Core::System& system_) | |||
| 502 | static const FunctionInfo functions[] = { | 503 | static const FunctionInfo functions[] = { |
| 503 | {11, nullptr, "CalculateApplicationOccupiedSize"}, | 504 | {11, nullptr, "CalculateApplicationOccupiedSize"}, |
| 504 | {43, nullptr, "CheckSdCardMountStatus"}, | 505 | {43, nullptr, "CheckSdCardMountStatus"}, |
| 505 | {47, nullptr, "GetTotalSpaceSize"}, | 506 | {47, &IContentManagementInterface::GetTotalSpaceSize, "GetTotalSpaceSize"}, |
| 506 | {48, nullptr, "GetFreeSpaceSize"}, | 507 | {48, &IContentManagementInterface::GetFreeSpaceSize, "GetFreeSpaceSize"}, |
| 507 | {600, nullptr, "CountApplicationContentMeta"}, | 508 | {600, nullptr, "CountApplicationContentMeta"}, |
| 508 | {601, nullptr, "ListApplicationContentMetaStatus"}, | 509 | {601, nullptr, "ListApplicationContentMetaStatus"}, |
| 509 | {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"}, | 510 | {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"}, |
| @@ -516,6 +517,28 @@ IContentManagementInterface::IContentManagementInterface(Core::System& system_) | |||
| 516 | 517 | ||
| 517 | IContentManagementInterface::~IContentManagementInterface() = default; | 518 | IContentManagementInterface::~IContentManagementInterface() = default; |
| 518 | 519 | ||
| 520 | void IContentManagementInterface::GetTotalSpaceSize(HLERequestContext& ctx) { | ||
| 521 | IPC::RequestParser rp{ctx}; | ||
| 522 | const auto storage{rp.PopEnum<FileSys::StorageId>()}; | ||
| 523 | |||
| 524 | LOG_INFO(Service_Capture, "called, storage={}", storage); | ||
| 525 | |||
| 526 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 527 | rb.Push(ResultSuccess); | ||
| 528 | rb.Push<u64>(system.GetFileSystemController().GetTotalSpaceSize(storage)); | ||
| 529 | } | ||
| 530 | |||
| 531 | void IContentManagementInterface::GetFreeSpaceSize(HLERequestContext& ctx) { | ||
| 532 | IPC::RequestParser rp{ctx}; | ||
| 533 | const auto storage{rp.PopEnum<FileSys::StorageId>()}; | ||
| 534 | |||
| 535 | LOG_INFO(Service_Capture, "called, storage={}", storage); | ||
| 536 | |||
| 537 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 538 | rb.Push(ResultSuccess); | ||
| 539 | rb.Push<u64>(system.GetFileSystemController().GetFreeSpaceSize(storage)); | ||
| 540 | } | ||
| 541 | |||
| 519 | IDocumentInterface::IDocumentInterface(Core::System& system_) | 542 | IDocumentInterface::IDocumentInterface(Core::System& system_) |
| 520 | : ServiceFramework{system_, "IDocumentInterface"} { | 543 | : ServiceFramework{system_, "IDocumentInterface"} { |
| 521 | // clang-format off | 544 | // clang-format off |
diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h index 175dad780..34d2a45dc 100644 --- a/src/core/hle/service/ns/ns.h +++ b/src/core/hle/service/ns/ns.h | |||
| @@ -48,6 +48,10 @@ class IContentManagementInterface final : public ServiceFramework<IContentManage | |||
| 48 | public: | 48 | public: |
| 49 | explicit IContentManagementInterface(Core::System& system_); | 49 | explicit IContentManagementInterface(Core::System& system_); |
| 50 | ~IContentManagementInterface() override; | 50 | ~IContentManagementInterface() override; |
| 51 | |||
| 52 | private: | ||
| 53 | void GetTotalSpaceSize(HLERequestContext& ctx); | ||
| 54 | void GetFreeSpaceSize(HLERequestContext& ctx); | ||
| 51 | }; | 55 | }; |
| 52 | 56 | ||
| 53 | class IDocumentInterface final : public ServiceFramework<IDocumentInterface> { | 57 | class IDocumentInterface final : public ServiceFramework<IDocumentInterface> { |
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h index 40c65b430..4c0cc71cd 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.h +++ b/src/core/hle/service/nvdrv/devices/nvmap.h | |||
| @@ -45,13 +45,6 @@ public: | |||
| 45 | IsSharedMemMapped = 6 | 45 | IsSharedMemMapped = 6 |
| 46 | }; | 46 | }; |
| 47 | 47 | ||
| 48 | private: | ||
| 49 | /// Id to use for the next handle that is created. | ||
| 50 | u32 next_handle = 0; | ||
| 51 | |||
| 52 | /// Id to use for the next object that is created. | ||
| 53 | u32 next_id = 0; | ||
| 54 | |||
| 55 | struct IocCreateParams { | 48 | struct IocCreateParams { |
| 56 | // Input | 49 | // Input |
| 57 | u32_le size{}; | 50 | u32_le size{}; |
| @@ -113,6 +106,13 @@ private: | |||
| 113 | NvResult IocParam(std::span<const u8> input, std::span<u8> output); | 106 | NvResult IocParam(std::span<const u8> input, std::span<u8> output); |
| 114 | NvResult IocFree(std::span<const u8> input, std::span<u8> output); | 107 | NvResult IocFree(std::span<const u8> input, std::span<u8> output); |
| 115 | 108 | ||
| 109 | private: | ||
| 110 | /// Id to use for the next handle that is created. | ||
| 111 | u32 next_handle = 0; | ||
| 112 | |||
| 113 | /// Id to use for the next object that is created. | ||
| 114 | u32 next_id = 0; | ||
| 115 | |||
| 116 | NvCore::Container& container; | 116 | NvCore::Container& container; |
| 117 | NvCore::NvMap& file; | 117 | NvCore::NvMap& file; |
| 118 | }; | 118 | }; |
diff --git a/src/core/hle/service/nvnflinger/buffer_item.h b/src/core/hle/service/nvnflinger/buffer_item.h index 7fd808f54..3da8cc3aa 100644 --- a/src/core/hle/service/nvnflinger/buffer_item.h +++ b/src/core/hle/service/nvnflinger/buffer_item.h | |||
| @@ -15,7 +15,7 @@ | |||
| 15 | 15 | ||
| 16 | namespace Service::android { | 16 | namespace Service::android { |
| 17 | 17 | ||
| 18 | class GraphicBuffer; | 18 | struct GraphicBuffer; |
| 19 | 19 | ||
| 20 | class BufferItem final { | 20 | class BufferItem final { |
| 21 | public: | 21 | public: |
diff --git a/src/core/hle/service/nvnflinger/buffer_slot.h b/src/core/hle/service/nvnflinger/buffer_slot.h index d25bca049..d8c9dec3b 100644 --- a/src/core/hle/service/nvnflinger/buffer_slot.h +++ b/src/core/hle/service/nvnflinger/buffer_slot.h | |||
| @@ -13,7 +13,7 @@ | |||
| 13 | 13 | ||
| 14 | namespace Service::android { | 14 | namespace Service::android { |
| 15 | 15 | ||
| 16 | class GraphicBuffer; | 16 | struct GraphicBuffer; |
| 17 | 17 | ||
| 18 | enum class BufferState : u32 { | 18 | enum class BufferState : u32 { |
| 19 | Free = 0, | 19 | Free = 0, |
diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp new file mode 100644 index 000000000..469a53244 --- /dev/null +++ b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp | |||
| @@ -0,0 +1,351 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <random> | ||
| 5 | |||
| 6 | #include "core/core.h" | ||
| 7 | #include "core/hle/kernel/k_process.h" | ||
| 8 | #include "core/hle/kernel/k_system_resource.h" | ||
| 9 | #include "core/hle/service/nvdrv/devices/nvmap.h" | ||
| 10 | #include "core/hle/service/nvdrv/nvdrv.h" | ||
| 11 | #include "core/hle/service/nvnflinger/buffer_queue_producer.h" | ||
| 12 | #include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" | ||
| 13 | #include "core/hle/service/nvnflinger/pixel_format.h" | ||
| 14 | #include "core/hle/service/nvnflinger/ui/graphic_buffer.h" | ||
| 15 | #include "core/hle/service/vi/layer/vi_layer.h" | ||
| 16 | #include "core/hle/service/vi/vi_results.h" | ||
| 17 | |||
| 18 | namespace Service::Nvnflinger { | ||
| 19 | |||
| 20 | namespace { | ||
| 21 | |||
| 22 | Result AllocateIoForProcessAddressSpace(Common::ProcessAddress* out_map_address, | ||
| 23 | std::unique_ptr<Kernel::KPageGroup>* out_page_group, | ||
| 24 | Core::System& system, u32 size) { | ||
| 25 | using Core::Memory::YUZU_PAGESIZE; | ||
| 26 | |||
| 27 | // Allocate memory for the system shared buffer. | ||
| 28 | // FIXME: Because the gmmu can only point to cpu addresses, we need | ||
| 29 | // to map this in the application space to allow it to be used. | ||
| 30 | // FIXME: Add proper smmu emulation. | ||
| 31 | // FIXME: This memory belongs to vi's .data section. | ||
| 32 | auto& kernel = system.Kernel(); | ||
| 33 | auto* process = system.ApplicationProcess(); | ||
| 34 | auto& page_table = process->GetPageTable(); | ||
| 35 | |||
| 36 | // Hold a temporary page group reference while we try to map it. | ||
| 37 | auto pg = std::make_unique<Kernel::KPageGroup>( | ||
| 38 | kernel, std::addressof(kernel.GetSystemSystemResource().GetBlockInfoManager())); | ||
| 39 | |||
| 40 | // Allocate memory from secure pool. | ||
| 41 | R_TRY(kernel.MemoryManager().AllocateAndOpen( | ||
| 42 | pg.get(), size / YUZU_PAGESIZE, | ||
| 43 | Kernel::KMemoryManager::EncodeOption(Kernel::KMemoryManager::Pool::Secure, | ||
| 44 | Kernel::KMemoryManager::Direction::FromBack))); | ||
| 45 | |||
| 46 | // Get bounds of where mapping is possible. | ||
| 47 | const VAddr alias_code_begin = GetInteger(page_table.GetAliasCodeRegionStart()); | ||
| 48 | const VAddr alias_code_size = page_table.GetAliasCodeRegionSize() / YUZU_PAGESIZE; | ||
| 49 | const auto state = Kernel::KMemoryState::Io; | ||
| 50 | const auto perm = Kernel::KMemoryPermission::UserReadWrite; | ||
| 51 | std::mt19937_64 rng{process->GetRandomEntropy(0)}; | ||
| 52 | |||
| 53 | // Retry up to 64 times to map into alias code range. | ||
| 54 | Result res = ResultSuccess; | ||
| 55 | int i; | ||
| 56 | for (i = 0; i < 64; i++) { | ||
| 57 | *out_map_address = alias_code_begin + ((rng() % alias_code_size) * YUZU_PAGESIZE); | ||
| 58 | res = page_table.MapPageGroup(*out_map_address, *pg, state, perm); | ||
| 59 | if (R_SUCCEEDED(res)) { | ||
| 60 | break; | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | // Return failure, if necessary | ||
| 65 | R_UNLESS(i < 64, res); | ||
| 66 | |||
| 67 | // Return the mapped page group. | ||
| 68 | *out_page_group = std::move(pg); | ||
| 69 | |||
| 70 | // We succeeded. | ||
| 71 | R_SUCCEED(); | ||
| 72 | } | ||
| 73 | |||
| 74 | template <typename T> | ||
| 75 | std::span<u8> SerializeIoc(T& params) { | ||
| 76 | return std::span(reinterpret_cast<u8*>(std::addressof(params)), sizeof(T)); | ||
| 77 | } | ||
| 78 | |||
| 79 | Result CreateNvMapHandle(u32* out_nv_map_handle, Nvidia::Devices::nvmap& nvmap, u32 size) { | ||
| 80 | // Create a handle. | ||
| 81 | Nvidia::Devices::nvmap::IocCreateParams create_in_params{ | ||
| 82 | .size = size, | ||
| 83 | .handle = 0, | ||
| 84 | }; | ||
| 85 | Nvidia::Devices::nvmap::IocCreateParams create_out_params{}; | ||
| 86 | R_UNLESS(nvmap.IocCreate(SerializeIoc(create_in_params), SerializeIoc(create_out_params)) == | ||
| 87 | Nvidia::NvResult::Success, | ||
| 88 | VI::ResultOperationFailed); | ||
| 89 | |||
| 90 | // Assign the output handle. | ||
| 91 | *out_nv_map_handle = create_out_params.handle; | ||
| 92 | |||
| 93 | // We succeeded. | ||
| 94 | R_SUCCEED(); | ||
| 95 | } | ||
| 96 | |||
| 97 | Result FreeNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle) { | ||
| 98 | // Free the handle. | ||
| 99 | Nvidia::Devices::nvmap::IocFreeParams free_in_params{ | ||
| 100 | .handle = handle, | ||
| 101 | }; | ||
| 102 | Nvidia::Devices::nvmap::IocFreeParams free_out_params{}; | ||
| 103 | R_UNLESS(nvmap.IocFree(SerializeIoc(free_in_params), SerializeIoc(free_out_params)) == | ||
| 104 | Nvidia::NvResult::Success, | ||
| 105 | VI::ResultOperationFailed); | ||
| 106 | |||
| 107 | // We succeeded. | ||
| 108 | R_SUCCEED(); | ||
| 109 | } | ||
| 110 | |||
| 111 | Result AllocNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Common::ProcessAddress buffer, | ||
| 112 | u32 size) { | ||
| 113 | // Assign the allocated memory to the handle. | ||
| 114 | Nvidia::Devices::nvmap::IocAllocParams alloc_in_params{ | ||
| 115 | .handle = handle, | ||
| 116 | .heap_mask = 0, | ||
| 117 | .flags = {}, | ||
| 118 | .align = 0, | ||
| 119 | .kind = 0, | ||
| 120 | .address = GetInteger(buffer), | ||
| 121 | }; | ||
| 122 | Nvidia::Devices::nvmap::IocAllocParams alloc_out_params{}; | ||
| 123 | R_UNLESS(nvmap.IocAlloc(SerializeIoc(alloc_in_params), SerializeIoc(alloc_out_params)) == | ||
| 124 | Nvidia::NvResult::Success, | ||
| 125 | VI::ResultOperationFailed); | ||
| 126 | |||
| 127 | // We succeeded. | ||
| 128 | R_SUCCEED(); | ||
| 129 | } | ||
| 130 | |||
| 131 | Result AllocateHandleForBuffer(u32* out_handle, Nvidia::Module& nvdrv, | ||
| 132 | Common::ProcessAddress buffer, u32 size) { | ||
| 133 | // Get the nvmap device. | ||
| 134 | auto nvmap_fd = nvdrv.Open("/dev/nvmap"); | ||
| 135 | auto nvmap = nvdrv.GetDevice<Nvidia::Devices::nvmap>(nvmap_fd); | ||
| 136 | ASSERT(nvmap != nullptr); | ||
| 137 | |||
| 138 | // Create a handle. | ||
| 139 | R_TRY(CreateNvMapHandle(out_handle, *nvmap, size)); | ||
| 140 | |||
| 141 | // Ensure we maintain a clean state on failure. | ||
| 142 | ON_RESULT_FAILURE { | ||
| 143 | ASSERT(R_SUCCEEDED(FreeNvMapHandle(*nvmap, *out_handle))); | ||
| 144 | }; | ||
| 145 | |||
| 146 | // Assign the allocated memory to the handle. | ||
| 147 | R_RETURN(AllocNvMapHandle(*nvmap, *out_handle, buffer, size)); | ||
| 148 | } | ||
| 149 | |||
| 150 | constexpr auto SharedBufferBlockLinearFormat = android::PixelFormat::Rgba8888; | ||
| 151 | constexpr u32 SharedBufferBlockLinearBpp = 4; | ||
| 152 | |||
| 153 | constexpr u32 SharedBufferBlockLinearWidth = 1280; | ||
| 154 | constexpr u32 SharedBufferBlockLinearHeight = 768; | ||
| 155 | constexpr u32 SharedBufferBlockLinearStride = | ||
| 156 | SharedBufferBlockLinearWidth * SharedBufferBlockLinearBpp; | ||
| 157 | constexpr u32 SharedBufferNumSlots = 7; | ||
| 158 | |||
| 159 | constexpr u32 SharedBufferWidth = 1280; | ||
| 160 | constexpr u32 SharedBufferHeight = 720; | ||
| 161 | constexpr u32 SharedBufferAsync = false; | ||
| 162 | |||
| 163 | constexpr u32 SharedBufferSlotSize = | ||
| 164 | SharedBufferBlockLinearWidth * SharedBufferBlockLinearHeight * SharedBufferBlockLinearBpp; | ||
| 165 | constexpr u32 SharedBufferSize = SharedBufferSlotSize * SharedBufferNumSlots; | ||
| 166 | |||
| 167 | constexpr SharedMemoryPoolLayout SharedBufferPoolLayout = [] { | ||
| 168 | SharedMemoryPoolLayout layout{}; | ||
| 169 | layout.num_slots = SharedBufferNumSlots; | ||
| 170 | |||
| 171 | for (u32 i = 0; i < SharedBufferNumSlots; i++) { | ||
| 172 | layout.slots[i].buffer_offset = i * SharedBufferSlotSize; | ||
| 173 | layout.slots[i].size = SharedBufferSlotSize; | ||
| 174 | layout.slots[i].width = SharedBufferWidth; | ||
| 175 | layout.slots[i].height = SharedBufferHeight; | ||
| 176 | } | ||
| 177 | |||
| 178 | return layout; | ||
| 179 | }(); | ||
| 180 | |||
| 181 | void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 handle) { | ||
| 182 | auto buffer = std::make_shared<android::GraphicBuffer>(); | ||
| 183 | buffer->width = SharedBufferWidth; | ||
| 184 | buffer->height = SharedBufferHeight; | ||
| 185 | buffer->stride = SharedBufferBlockLinearStride; | ||
| 186 | buffer->format = SharedBufferBlockLinearFormat; | ||
| 187 | buffer->buffer_id = handle; | ||
| 188 | buffer->offset = slot * SharedBufferSlotSize; | ||
| 189 | ASSERT(producer.SetPreallocatedBuffer(slot, buffer) == android::Status::NoError); | ||
| 190 | } | ||
| 191 | |||
| 192 | } // namespace | ||
| 193 | |||
| 194 | FbShareBufferManager::FbShareBufferManager(Core::System& system, Nvnflinger& flinger, | ||
| 195 | std::shared_ptr<Nvidia::Module> nvdrv) | ||
| 196 | : m_system(system), m_flinger(flinger), m_nvdrv(std::move(nvdrv)) {} | ||
| 197 | |||
| 198 | FbShareBufferManager::~FbShareBufferManager() = default; | ||
| 199 | |||
| 200 | Result FbShareBufferManager::Initialize(u64* out_buffer_id, u64* out_layer_id, u64 display_id) { | ||
| 201 | std::scoped_lock lk{m_guard}; | ||
| 202 | |||
| 203 | // Ensure we have not already created a buffer. | ||
| 204 | R_UNLESS(m_buffer_id == 0, VI::ResultOperationFailed); | ||
| 205 | |||
| 206 | // Allocate memory and space for the shared buffer. | ||
| 207 | Common::ProcessAddress map_address; | ||
| 208 | R_TRY(AllocateIoForProcessAddressSpace(std::addressof(map_address), | ||
| 209 | std::addressof(m_buffer_page_group), m_system, | ||
| 210 | SharedBufferSize)); | ||
| 211 | |||
| 212 | // Create an nvmap handle for the buffer and assign the memory to it. | ||
| 213 | R_TRY(AllocateHandleForBuffer(std::addressof(m_buffer_nvmap_handle), *m_nvdrv, map_address, | ||
| 214 | SharedBufferSize)); | ||
| 215 | |||
| 216 | // Record the display id. | ||
| 217 | m_display_id = display_id; | ||
| 218 | |||
| 219 | // Create a layer for the display. | ||
| 220 | m_layer_id = m_flinger.CreateLayer(m_display_id).value(); | ||
| 221 | |||
| 222 | // Set up the buffer. | ||
| 223 | m_buffer_id = m_next_buffer_id++; | ||
| 224 | |||
| 225 | // Get the layer. | ||
| 226 | VI::Layer* layer = m_flinger.FindLayer(m_display_id, m_layer_id); | ||
| 227 | ASSERT(layer != nullptr); | ||
| 228 | |||
| 229 | // Get the producer and set preallocated buffers. | ||
| 230 | auto& producer = layer->GetBufferQueue(); | ||
| 231 | MakeGraphicBuffer(producer, 0, m_buffer_nvmap_handle); | ||
| 232 | MakeGraphicBuffer(producer, 1, m_buffer_nvmap_handle); | ||
| 233 | |||
| 234 | // Assign outputs. | ||
| 235 | *out_buffer_id = m_buffer_id; | ||
| 236 | *out_layer_id = m_layer_id; | ||
| 237 | |||
| 238 | // We succeeded. | ||
| 239 | R_SUCCEED(); | ||
| 240 | } | ||
| 241 | |||
| 242 | Result FbShareBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size, | ||
| 243 | s32* out_nvmap_handle, | ||
| 244 | SharedMemoryPoolLayout* out_pool_layout, | ||
| 245 | u64 buffer_id, | ||
| 246 | u64 applet_resource_user_id) { | ||
| 247 | std::scoped_lock lk{m_guard}; | ||
| 248 | |||
| 249 | R_UNLESS(m_buffer_id > 0, VI::ResultNotFound); | ||
| 250 | R_UNLESS(buffer_id == m_buffer_id, VI::ResultNotFound); | ||
| 251 | |||
| 252 | *out_pool_layout = SharedBufferPoolLayout; | ||
| 253 | *out_buffer_size = SharedBufferSize; | ||
| 254 | *out_nvmap_handle = m_buffer_nvmap_handle; | ||
| 255 | |||
| 256 | R_SUCCEED(); | ||
| 257 | } | ||
| 258 | |||
| 259 | Result FbShareBufferManager::GetLayerFromId(VI::Layer** out_layer, u64 layer_id) { | ||
| 260 | // Ensure the layer id is valid. | ||
| 261 | R_UNLESS(m_layer_id > 0 && layer_id == m_layer_id, VI::ResultNotFound); | ||
| 262 | |||
| 263 | // Get the layer. | ||
| 264 | VI::Layer* layer = m_flinger.FindLayer(m_display_id, layer_id); | ||
| 265 | R_UNLESS(layer != nullptr, VI::ResultNotFound); | ||
| 266 | |||
| 267 | // We succeeded. | ||
| 268 | *out_layer = layer; | ||
| 269 | R_SUCCEED(); | ||
| 270 | } | ||
| 271 | |||
| 272 | Result FbShareBufferManager::AcquireSharedFrameBuffer(android::Fence* out_fence, | ||
| 273 | std::array<s32, 4>& out_slot_indexes, | ||
| 274 | s64* out_target_slot, u64 layer_id) { | ||
| 275 | std::scoped_lock lk{m_guard}; | ||
| 276 | |||
| 277 | // Get the layer. | ||
| 278 | VI::Layer* layer; | ||
| 279 | R_TRY(this->GetLayerFromId(std::addressof(layer), layer_id)); | ||
| 280 | |||
| 281 | // Get the producer. | ||
| 282 | auto& producer = layer->GetBufferQueue(); | ||
| 283 | |||
| 284 | // Get the next buffer from the producer. | ||
| 285 | s32 slot; | ||
| 286 | R_UNLESS(producer.DequeueBuffer(std::addressof(slot), out_fence, SharedBufferAsync != 0, | ||
| 287 | SharedBufferWidth, SharedBufferHeight, | ||
| 288 | SharedBufferBlockLinearFormat, 0) == android::Status::NoError, | ||
| 289 | VI::ResultOperationFailed); | ||
| 290 | |||
| 291 | // Assign remaining outputs. | ||
| 292 | *out_target_slot = slot; | ||
| 293 | out_slot_indexes = {0, 1, -1, -1}; | ||
| 294 | |||
| 295 | // We succeeded. | ||
| 296 | R_SUCCEED(); | ||
| 297 | } | ||
| 298 | |||
| 299 | Result FbShareBufferManager::PresentSharedFrameBuffer(android::Fence fence, | ||
| 300 | Common::Rectangle<s32> crop_region, | ||
| 301 | u32 transform, s32 swap_interval, | ||
| 302 | u64 layer_id, s64 slot) { | ||
| 303 | std::scoped_lock lk{m_guard}; | ||
| 304 | |||
| 305 | // Get the layer. | ||
| 306 | VI::Layer* layer; | ||
| 307 | R_TRY(this->GetLayerFromId(std::addressof(layer), layer_id)); | ||
| 308 | |||
| 309 | // Get the producer. | ||
| 310 | auto& producer = layer->GetBufferQueue(); | ||
| 311 | |||
| 312 | // Request to queue the buffer. | ||
| 313 | std::shared_ptr<android::GraphicBuffer> buffer; | ||
| 314 | R_UNLESS(producer.RequestBuffer(static_cast<s32>(slot), std::addressof(buffer)) == | ||
| 315 | android::Status::NoError, | ||
| 316 | VI::ResultOperationFailed); | ||
| 317 | |||
| 318 | // Queue the buffer to the producer. | ||
| 319 | android::QueueBufferInput input{}; | ||
| 320 | android::QueueBufferOutput output{}; | ||
| 321 | input.crop = crop_region; | ||
| 322 | input.fence = fence; | ||
| 323 | input.transform = static_cast<android::NativeWindowTransform>(transform); | ||
| 324 | input.swap_interval = swap_interval; | ||
| 325 | R_UNLESS(producer.QueueBuffer(static_cast<s32>(slot), input, std::addressof(output)) == | ||
| 326 | android::Status::NoError, | ||
| 327 | VI::ResultOperationFailed); | ||
| 328 | |||
| 329 | // We succeeded. | ||
| 330 | R_SUCCEED(); | ||
| 331 | } | ||
| 332 | |||
| 333 | Result FbShareBufferManager::GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, | ||
| 334 | u64 layer_id) { | ||
| 335 | std::scoped_lock lk{m_guard}; | ||
| 336 | |||
| 337 | // Get the layer. | ||
| 338 | VI::Layer* layer; | ||
| 339 | R_TRY(this->GetLayerFromId(std::addressof(layer), layer_id)); | ||
| 340 | |||
| 341 | // Get the producer. | ||
| 342 | auto& producer = layer->GetBufferQueue(); | ||
| 343 | |||
| 344 | // Set the event. | ||
| 345 | *out_event = std::addressof(producer.GetNativeHandle()); | ||
| 346 | |||
| 347 | // We succeeded. | ||
| 348 | R_SUCCEED(); | ||
| 349 | } | ||
| 350 | |||
| 351 | } // namespace Service::Nvnflinger | ||
diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h new file mode 100644 index 000000000..c809c01b4 --- /dev/null +++ b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/math_util.h" | ||
| 7 | #include "core/hle/service/nvnflinger/nvnflinger.h" | ||
| 8 | #include "core/hle/service/nvnflinger/ui/fence.h" | ||
| 9 | |||
| 10 | namespace Kernel { | ||
| 11 | class KPageGroup; | ||
| 12 | } | ||
| 13 | |||
| 14 | namespace Service::Nvnflinger { | ||
| 15 | |||
| 16 | struct SharedMemorySlot { | ||
| 17 | u64 buffer_offset; | ||
| 18 | u64 size; | ||
| 19 | s32 width; | ||
| 20 | s32 height; | ||
| 21 | }; | ||
| 22 | static_assert(sizeof(SharedMemorySlot) == 0x18, "SharedMemorySlot has wrong size"); | ||
| 23 | |||
| 24 | struct SharedMemoryPoolLayout { | ||
| 25 | s32 num_slots; | ||
| 26 | std::array<SharedMemorySlot, 0x10> slots; | ||
| 27 | }; | ||
| 28 | static_assert(sizeof(SharedMemoryPoolLayout) == 0x188, "SharedMemoryPoolLayout has wrong size"); | ||
| 29 | |||
| 30 | class FbShareBufferManager final { | ||
| 31 | public: | ||
| 32 | explicit FbShareBufferManager(Core::System& system, Nvnflinger& flinger, | ||
| 33 | std::shared_ptr<Nvidia::Module> nvdrv); | ||
| 34 | ~FbShareBufferManager(); | ||
| 35 | |||
| 36 | Result Initialize(u64* out_buffer_id, u64* out_layer_handle, u64 display_id); | ||
| 37 | Result GetSharedBufferMemoryHandleId(u64* out_buffer_size, s32* out_nvmap_handle, | ||
| 38 | SharedMemoryPoolLayout* out_pool_layout, u64 buffer_id, | ||
| 39 | u64 applet_resource_user_id); | ||
| 40 | Result AcquireSharedFrameBuffer(android::Fence* out_fence, std::array<s32, 4>& out_slots, | ||
| 41 | s64* out_target_slot, u64 layer_id); | ||
| 42 | Result PresentSharedFrameBuffer(android::Fence fence, Common::Rectangle<s32> crop_region, | ||
| 43 | u32 transform, s32 swap_interval, u64 layer_id, s64 slot); | ||
| 44 | Result GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, u64 layer_id); | ||
| 45 | |||
| 46 | private: | ||
| 47 | Result GetLayerFromId(VI::Layer** out_layer, u64 layer_id); | ||
| 48 | |||
| 49 | private: | ||
| 50 | u64 m_next_buffer_id = 1; | ||
| 51 | u64 m_display_id = 0; | ||
| 52 | u64 m_buffer_id = 0; | ||
| 53 | u64 m_layer_id = 0; | ||
| 54 | u32 m_buffer_nvmap_handle = 0; | ||
| 55 | SharedMemoryPoolLayout m_pool_layout = {}; | ||
| 56 | |||
| 57 | std::unique_ptr<Kernel::KPageGroup> m_buffer_page_group; | ||
| 58 | |||
| 59 | std::mutex m_guard; | ||
| 60 | Core::System& m_system; | ||
| 61 | Nvnflinger& m_flinger; | ||
| 62 | std::shared_ptr<Nvidia::Module> m_nvdrv; | ||
| 63 | }; | ||
| 64 | |||
| 65 | } // namespace Service::Nvnflinger | ||
diff --git a/src/core/hle/service/nvnflinger/graphic_buffer_producer.h b/src/core/hle/service/nvnflinger/graphic_buffer_producer.h index 21d7b31f3..5d7cff7d3 100644 --- a/src/core/hle/service/nvnflinger/graphic_buffer_producer.h +++ b/src/core/hle/service/nvnflinger/graphic_buffer_producer.h | |||
| @@ -19,6 +19,7 @@ class InputParcel; | |||
| 19 | #pragma pack(push, 1) | 19 | #pragma pack(push, 1) |
| 20 | struct QueueBufferInput final { | 20 | struct QueueBufferInput final { |
| 21 | explicit QueueBufferInput(InputParcel& parcel); | 21 | explicit QueueBufferInput(InputParcel& parcel); |
| 22 | explicit QueueBufferInput() = default; | ||
| 22 | 23 | ||
| 23 | void Deflate(s64* timestamp_, bool* is_auto_timestamp_, Common::Rectangle<s32>* crop_, | 24 | void Deflate(s64* timestamp_, bool* is_auto_timestamp_, Common::Rectangle<s32>* crop_, |
| 24 | NativeWindowScalingMode* scaling_mode_, NativeWindowTransform* transform_, | 25 | NativeWindowScalingMode* scaling_mode_, NativeWindowTransform* transform_, |
| @@ -34,7 +35,6 @@ struct QueueBufferInput final { | |||
| 34 | *fence_ = fence; | 35 | *fence_ = fence; |
| 35 | } | 36 | } |
| 36 | 37 | ||
| 37 | private: | ||
| 38 | s64 timestamp{}; | 38 | s64 timestamp{}; |
| 39 | s32 is_auto_timestamp{}; | 39 | s32 is_auto_timestamp{}; |
| 40 | Common::Rectangle<s32> crop{}; | 40 | Common::Rectangle<s32> crop{}; |
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp index 21f31f7a0..a07c621d9 100644 --- a/src/core/hle/service/nvnflinger/nvnflinger.cpp +++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include "core/hle/service/nvdrv/nvdrv.h" | 17 | #include "core/hle/service/nvdrv/nvdrv.h" |
| 18 | #include "core/hle/service/nvnflinger/buffer_item_consumer.h" | 18 | #include "core/hle/service/nvnflinger/buffer_item_consumer.h" |
| 19 | #include "core/hle/service/nvnflinger/buffer_queue_core.h" | 19 | #include "core/hle/service/nvnflinger/buffer_queue_core.h" |
| 20 | #include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" | ||
| 20 | #include "core/hle/service/nvnflinger/hos_binder_driver_server.h" | 21 | #include "core/hle/service/nvnflinger/hos_binder_driver_server.h" |
| 21 | #include "core/hle/service/nvnflinger/nvnflinger.h" | 22 | #include "core/hle/service/nvnflinger/nvnflinger.h" |
| 22 | #include "core/hle/service/nvnflinger/ui/graphic_buffer.h" | 23 | #include "core/hle/service/nvnflinger/ui/graphic_buffer.h" |
| @@ -331,4 +332,14 @@ s64 Nvnflinger::GetNextTicks() const { | |||
| 331 | return static_cast<s64>(speed_scale * (1000000000.f / effective_fps)); | 332 | return static_cast<s64>(speed_scale * (1000000000.f / effective_fps)); |
| 332 | } | 333 | } |
| 333 | 334 | ||
| 335 | FbShareBufferManager& Nvnflinger::GetSystemBufferManager() { | ||
| 336 | const auto lock_guard = Lock(); | ||
| 337 | |||
| 338 | if (!system_buffer_manager) { | ||
| 339 | system_buffer_manager = std::make_unique<FbShareBufferManager>(system, *this, nvdrv); | ||
| 340 | } | ||
| 341 | |||
| 342 | return *system_buffer_manager; | ||
| 343 | } | ||
| 344 | |||
| 334 | } // namespace Service::Nvnflinger | 345 | } // namespace Service::Nvnflinger |
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.h b/src/core/hle/service/nvnflinger/nvnflinger.h index f478c2bc6..14c783582 100644 --- a/src/core/hle/service/nvnflinger/nvnflinger.h +++ b/src/core/hle/service/nvnflinger/nvnflinger.h | |||
| @@ -45,6 +45,9 @@ class BufferQueueProducer; | |||
| 45 | 45 | ||
| 46 | namespace Service::Nvnflinger { | 46 | namespace Service::Nvnflinger { |
| 47 | 47 | ||
| 48 | class FbShareBufferManager; | ||
| 49 | class HosBinderDriverServer; | ||
| 50 | |||
| 48 | class Nvnflinger final { | 51 | class Nvnflinger final { |
| 49 | public: | 52 | public: |
| 50 | explicit Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_); | 53 | explicit Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_); |
| @@ -90,12 +93,16 @@ public: | |||
| 90 | 93 | ||
| 91 | [[nodiscard]] s64 GetNextTicks() const; | 94 | [[nodiscard]] s64 GetNextTicks() const; |
| 92 | 95 | ||
| 96 | FbShareBufferManager& GetSystemBufferManager(); | ||
| 97 | |||
| 93 | private: | 98 | private: |
| 94 | struct Layer { | 99 | struct Layer { |
| 95 | std::unique_ptr<android::BufferQueueCore> core; | 100 | std::unique_ptr<android::BufferQueueCore> core; |
| 96 | std::unique_ptr<android::BufferQueueProducer> producer; | 101 | std::unique_ptr<android::BufferQueueProducer> producer; |
| 97 | }; | 102 | }; |
| 98 | 103 | ||
| 104 | friend class FbShareBufferManager; | ||
| 105 | |||
| 99 | private: | 106 | private: |
| 100 | [[nodiscard]] std::unique_lock<std::mutex> Lock() const { | 107 | [[nodiscard]] std::unique_lock<std::mutex> Lock() const { |
| 101 | return std::unique_lock{*guard}; | 108 | return std::unique_lock{*guard}; |
| @@ -140,6 +147,8 @@ private: | |||
| 140 | std::shared_ptr<Core::Timing::EventType> multi_composition_event; | 147 | std::shared_ptr<Core::Timing::EventType> multi_composition_event; |
| 141 | std::shared_ptr<Core::Timing::EventType> single_composition_event; | 148 | std::shared_ptr<Core::Timing::EventType> single_composition_event; |
| 142 | 149 | ||
| 150 | std::unique_ptr<FbShareBufferManager> system_buffer_manager; | ||
| 151 | |||
| 143 | std::shared_ptr<std::mutex> guard; | 152 | std::shared_ptr<std::mutex> guard; |
| 144 | 153 | ||
| 145 | Core::System& system; | 154 | Core::System& system; |
diff --git a/src/core/hle/service/nvnflinger/ui/fence.h b/src/core/hle/service/nvnflinger/ui/fence.h index 536e8156d..177aed758 100644 --- a/src/core/hle/service/nvnflinger/ui/fence.h +++ b/src/core/hle/service/nvnflinger/ui/fence.h | |||
| @@ -20,6 +20,9 @@ public: | |||
| 20 | static constexpr Fence NoFence() { | 20 | static constexpr Fence NoFence() { |
| 21 | Fence fence; | 21 | Fence fence; |
| 22 | fence.fences[0].id = -1; | 22 | fence.fences[0].id = -1; |
| 23 | fence.fences[1].id = -1; | ||
| 24 | fence.fences[2].id = -1; | ||
| 25 | fence.fences[3].id = -1; | ||
| 23 | return fence; | 26 | return fence; |
| 24 | } | 27 | } |
| 25 | 28 | ||
diff --git a/src/core/hle/service/nvnflinger/ui/graphic_buffer.h b/src/core/hle/service/nvnflinger/ui/graphic_buffer.h index 75d1705a8..3eac5cedd 100644 --- a/src/core/hle/service/nvnflinger/ui/graphic_buffer.h +++ b/src/core/hle/service/nvnflinger/ui/graphic_buffer.h | |||
| @@ -12,8 +12,7 @@ | |||
| 12 | 12 | ||
| 13 | namespace Service::android { | 13 | namespace Service::android { |
| 14 | 14 | ||
| 15 | class GraphicBuffer final { | 15 | struct GraphicBuffer final { |
| 16 | public: | ||
| 17 | constexpr GraphicBuffer() = default; | 16 | constexpr GraphicBuffer() = default; |
| 18 | 17 | ||
| 19 | constexpr GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_) | 18 | constexpr GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_) |
| @@ -77,7 +76,6 @@ public: | |||
| 77 | return false; | 76 | return false; |
| 78 | } | 77 | } |
| 79 | 78 | ||
| 80 | private: | ||
| 81 | u32 magic{}; | 79 | u32 magic{}; |
| 82 | s32 width{}; | 80 | s32 width{}; |
| 83 | s32 height{}; | 81 | s32 height{}; |
diff --git a/src/core/hle/service/pctl/pctl_module.cpp b/src/core/hle/service/pctl/pctl_module.cpp index 5db1703d1..938330dd0 100644 --- a/src/core/hle/service/pctl/pctl_module.cpp +++ b/src/core/hle/service/pctl/pctl_module.cpp | |||
| @@ -33,7 +33,7 @@ public: | |||
| 33 | {1001, &IParentalControlService::CheckFreeCommunicationPermission, "CheckFreeCommunicationPermission"}, | 33 | {1001, &IParentalControlService::CheckFreeCommunicationPermission, "CheckFreeCommunicationPermission"}, |
| 34 | {1002, nullptr, "ConfirmLaunchApplicationPermission"}, | 34 | {1002, nullptr, "ConfirmLaunchApplicationPermission"}, |
| 35 | {1003, nullptr, "ConfirmResumeApplicationPermission"}, | 35 | {1003, nullptr, "ConfirmResumeApplicationPermission"}, |
| 36 | {1004, nullptr, "ConfirmSnsPostPermission"}, | 36 | {1004, &IParentalControlService::ConfirmSnsPostPermission, "ConfirmSnsPostPermission"}, |
| 37 | {1005, nullptr, "ConfirmSystemSettingsPermission"}, | 37 | {1005, nullptr, "ConfirmSystemSettingsPermission"}, |
| 38 | {1006, &IParentalControlService::IsRestrictionTemporaryUnlocked, "IsRestrictionTemporaryUnlocked"}, | 38 | {1006, &IParentalControlService::IsRestrictionTemporaryUnlocked, "IsRestrictionTemporaryUnlocked"}, |
| 39 | {1007, nullptr, "RevertRestrictionTemporaryUnlocked"}, | 39 | {1007, nullptr, "RevertRestrictionTemporaryUnlocked"}, |
| @@ -236,6 +236,13 @@ private: | |||
| 236 | states.free_communication = true; | 236 | states.free_communication = true; |
| 237 | } | 237 | } |
| 238 | 238 | ||
| 239 | void ConfirmSnsPostPermission(HLERequestContext& ctx) { | ||
| 240 | LOG_WARNING(Service_PCTL, "(STUBBED) called"); | ||
| 241 | |||
| 242 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 243 | rb.Push(Error::ResultNoFreeCommunication); | ||
| 244 | } | ||
| 245 | |||
| 239 | void IsRestrictionTemporaryUnlocked(HLERequestContext& ctx) { | 246 | void IsRestrictionTemporaryUnlocked(HLERequestContext& ctx) { |
| 240 | const bool is_temporary_unlocked = false; | 247 | const bool is_temporary_unlocked = false; |
| 241 | 248 | ||
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 2eb978379..b1bfb9898 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp | |||
| @@ -20,9 +20,12 @@ | |||
| 20 | #include "core/hle/kernel/k_readable_event.h" | 20 | #include "core/hle/kernel/k_readable_event.h" |
| 21 | #include "core/hle/kernel/k_thread.h" | 21 | #include "core/hle/kernel/k_thread.h" |
| 22 | #include "core/hle/service/ipc_helpers.h" | 22 | #include "core/hle/service/ipc_helpers.h" |
| 23 | #include "core/hle/service/nvdrv/devices/nvmap.h" | ||
| 23 | #include "core/hle/service/nvdrv/nvdata.h" | 24 | #include "core/hle/service/nvdrv/nvdata.h" |
| 25 | #include "core/hle/service/nvdrv/nvdrv.h" | ||
| 24 | #include "core/hle/service/nvnflinger/binder.h" | 26 | #include "core/hle/service/nvnflinger/binder.h" |
| 25 | #include "core/hle/service/nvnflinger/buffer_queue_producer.h" | 27 | #include "core/hle/service/nvnflinger/buffer_queue_producer.h" |
| 28 | #include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" | ||
| 26 | #include "core/hle/service/nvnflinger/hos_binder_driver_server.h" | 29 | #include "core/hle/service/nvnflinger/hos_binder_driver_server.h" |
| 27 | #include "core/hle/service/nvnflinger/nvnflinger.h" | 30 | #include "core/hle/service/nvnflinger/nvnflinger.h" |
| 28 | #include "core/hle/service/nvnflinger/parcel.h" | 31 | #include "core/hle/service/nvnflinger/parcel.h" |
| @@ -131,8 +134,9 @@ private: | |||
| 131 | 134 | ||
| 132 | class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> { | 135 | class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> { |
| 133 | public: | 136 | public: |
| 134 | explicit ISystemDisplayService(Core::System& system_) | 137 | explicit ISystemDisplayService(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger_) |
| 135 | : ServiceFramework{system_, "ISystemDisplayService"} { | 138 | : ServiceFramework{system_, "ISystemDisplayService"}, nvnflinger{nvnflinger_} { |
| 139 | // clang-format off | ||
| 136 | static const FunctionInfo functions[] = { | 140 | static const FunctionInfo functions[] = { |
| 137 | {1200, nullptr, "GetZOrderCountMin"}, | 141 | {1200, nullptr, "GetZOrderCountMin"}, |
| 138 | {1202, nullptr, "GetZOrderCountMax"}, | 142 | {1202, nullptr, "GetZOrderCountMax"}, |
| @@ -170,22 +174,126 @@ public: | |||
| 170 | {3217, nullptr, "SetDisplayCmuLuma"}, | 174 | {3217, nullptr, "SetDisplayCmuLuma"}, |
| 171 | {3218, nullptr, "SetDisplayCrcMode"}, | 175 | {3218, nullptr, "SetDisplayCrcMode"}, |
| 172 | {6013, nullptr, "GetLayerPresentationSubmissionTimestamps"}, | 176 | {6013, nullptr, "GetLayerPresentationSubmissionTimestamps"}, |
| 173 | {8225, nullptr, "GetSharedBufferMemoryHandleId"}, | 177 | {8225, &ISystemDisplayService::GetSharedBufferMemoryHandleId, "GetSharedBufferMemoryHandleId"}, |
| 174 | {8250, nullptr, "OpenSharedLayer"}, | 178 | {8250, &ISystemDisplayService::OpenSharedLayer, "OpenSharedLayer"}, |
| 175 | {8251, nullptr, "CloseSharedLayer"}, | 179 | {8251, nullptr, "CloseSharedLayer"}, |
| 176 | {8252, nullptr, "ConnectSharedLayer"}, | 180 | {8252, &ISystemDisplayService::ConnectSharedLayer, "ConnectSharedLayer"}, |
| 177 | {8253, nullptr, "DisconnectSharedLayer"}, | 181 | {8253, nullptr, "DisconnectSharedLayer"}, |
| 178 | {8254, nullptr, "AcquireSharedFrameBuffer"}, | 182 | {8254, &ISystemDisplayService::AcquireSharedFrameBuffer, "AcquireSharedFrameBuffer"}, |
| 179 | {8255, nullptr, "PresentSharedFrameBuffer"}, | 183 | {8255, &ISystemDisplayService::PresentSharedFrameBuffer, "PresentSharedFrameBuffer"}, |
| 180 | {8256, nullptr, "GetSharedFrameBufferAcquirableEvent"}, | 184 | {8256, &ISystemDisplayService::GetSharedFrameBufferAcquirableEvent, "GetSharedFrameBufferAcquirableEvent"}, |
| 181 | {8257, nullptr, "FillSharedFrameBufferColor"}, | 185 | {8257, nullptr, "FillSharedFrameBufferColor"}, |
| 182 | {8258, nullptr, "CancelSharedFrameBuffer"}, | 186 | {8258, nullptr, "CancelSharedFrameBuffer"}, |
| 183 | {9000, nullptr, "GetDp2hdmiController"}, | 187 | {9000, nullptr, "GetDp2hdmiController"}, |
| 184 | }; | 188 | }; |
| 189 | // clang-format on | ||
| 185 | RegisterHandlers(functions); | 190 | RegisterHandlers(functions); |
| 186 | } | 191 | } |
| 187 | 192 | ||
| 188 | private: | 193 | private: |
| 194 | void GetSharedBufferMemoryHandleId(HLERequestContext& ctx) { | ||
| 195 | IPC::RequestParser rp{ctx}; | ||
| 196 | const u64 buffer_id = rp.PopRaw<u64>(); | ||
| 197 | |||
| 198 | LOG_INFO(Service_VI, "called. buffer_id={:#x}", buffer_id); | ||
| 199 | |||
| 200 | struct OutputParameters { | ||
| 201 | s32 nvmap_handle; | ||
| 202 | u64 size; | ||
| 203 | }; | ||
| 204 | |||
| 205 | OutputParameters out{}; | ||
| 206 | Nvnflinger::SharedMemoryPoolLayout layout{}; | ||
| 207 | const auto result = nvnflinger.GetSystemBufferManager().GetSharedBufferMemoryHandleId( | ||
| 208 | &out.size, &out.nvmap_handle, &layout, buffer_id, 0); | ||
| 209 | |||
| 210 | ctx.WriteBuffer(&layout, sizeof(layout)); | ||
| 211 | |||
| 212 | IPC::ResponseBuilder rb{ctx, 6}; | ||
| 213 | rb.Push(result); | ||
| 214 | rb.PushRaw(out); | ||
| 215 | } | ||
| 216 | |||
| 217 | void OpenSharedLayer(HLERequestContext& ctx) { | ||
| 218 | IPC::RequestParser rp{ctx}; | ||
| 219 | const u64 layer_id = rp.PopRaw<u64>(); | ||
| 220 | |||
| 221 | LOG_INFO(Service_VI, "(STUBBED) called. layer_id={:#x}", layer_id); | ||
| 222 | |||
| 223 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 224 | rb.Push(ResultSuccess); | ||
| 225 | } | ||
| 226 | |||
| 227 | void ConnectSharedLayer(HLERequestContext& ctx) { | ||
| 228 | IPC::RequestParser rp{ctx}; | ||
| 229 | const u64 layer_id = rp.PopRaw<u64>(); | ||
| 230 | |||
| 231 | LOG_INFO(Service_VI, "(STUBBED) called. layer_id={:#x}", layer_id); | ||
| 232 | |||
| 233 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 234 | rb.Push(ResultSuccess); | ||
| 235 | } | ||
| 236 | |||
| 237 | void GetSharedFrameBufferAcquirableEvent(HLERequestContext& ctx) { | ||
| 238 | LOG_DEBUG(Service_VI, "called"); | ||
| 239 | |||
| 240 | IPC::RequestParser rp{ctx}; | ||
| 241 | const u64 layer_id = rp.PopRaw<u64>(); | ||
| 242 | |||
| 243 | Kernel::KReadableEvent* event{}; | ||
| 244 | const auto result = nvnflinger.GetSystemBufferManager().GetSharedFrameBufferAcquirableEvent( | ||
| 245 | &event, layer_id); | ||
| 246 | |||
| 247 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 248 | rb.Push(result); | ||
| 249 | rb.PushCopyObjects(event); | ||
| 250 | } | ||
| 251 | |||
| 252 | void AcquireSharedFrameBuffer(HLERequestContext& ctx) { | ||
| 253 | LOG_DEBUG(Service_VI, "called"); | ||
| 254 | |||
| 255 | IPC::RequestParser rp{ctx}; | ||
| 256 | const u64 layer_id = rp.PopRaw<u64>(); | ||
| 257 | |||
| 258 | struct OutputParameters { | ||
| 259 | android::Fence fence; | ||
| 260 | std::array<s32, 4> slots; | ||
| 261 | s64 target_slot; | ||
| 262 | }; | ||
| 263 | static_assert(sizeof(OutputParameters) == 0x40, "OutputParameters has wrong size"); | ||
| 264 | |||
| 265 | OutputParameters out{}; | ||
| 266 | const auto result = nvnflinger.GetSystemBufferManager().AcquireSharedFrameBuffer( | ||
| 267 | &out.fence, out.slots, &out.target_slot, layer_id); | ||
| 268 | |||
| 269 | IPC::ResponseBuilder rb{ctx, 18}; | ||
| 270 | rb.Push(result); | ||
| 271 | rb.PushRaw(out); | ||
| 272 | } | ||
| 273 | |||
| 274 | void PresentSharedFrameBuffer(HLERequestContext& ctx) { | ||
| 275 | LOG_DEBUG(Service_VI, "called"); | ||
| 276 | |||
| 277 | struct InputParameters { | ||
| 278 | android::Fence fence; | ||
| 279 | Common::Rectangle<s32> crop_region; | ||
| 280 | u32 window_transform; | ||
| 281 | s32 swap_interval; | ||
| 282 | u64 layer_id; | ||
| 283 | s64 surface_id; | ||
| 284 | }; | ||
| 285 | static_assert(sizeof(InputParameters) == 0x50, "InputParameters has wrong size"); | ||
| 286 | |||
| 287 | IPC::RequestParser rp{ctx}; | ||
| 288 | auto input = rp.PopRaw<InputParameters>(); | ||
| 289 | |||
| 290 | const auto result = nvnflinger.GetSystemBufferManager().PresentSharedFrameBuffer( | ||
| 291 | input.fence, input.crop_region, input.window_transform, input.swap_interval, | ||
| 292 | input.layer_id, input.surface_id); | ||
| 293 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 294 | rb.Push(result); | ||
| 295 | } | ||
| 296 | |||
| 189 | void SetLayerZ(HLERequestContext& ctx) { | 297 | void SetLayerZ(HLERequestContext& ctx) { |
| 190 | IPC::RequestParser rp{ctx}; | 298 | IPC::RequestParser rp{ctx}; |
| 191 | const u64 layer_id = rp.Pop<u64>(); | 299 | const u64 layer_id = rp.Pop<u64>(); |
| @@ -228,6 +336,9 @@ private: | |||
| 228 | rb.PushRaw<float>(60.0f); // This wouldn't seem to be correct for 30 fps games. | 336 | rb.PushRaw<float>(60.0f); // This wouldn't seem to be correct for 30 fps games. |
| 229 | rb.Push<u32>(0); | 337 | rb.Push<u32>(0); |
| 230 | } | 338 | } |
| 339 | |||
| 340 | private: | ||
| 341 | Nvnflinger::Nvnflinger& nvnflinger; | ||
| 231 | }; | 342 | }; |
| 232 | 343 | ||
| 233 | class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> { | 344 | class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> { |
| @@ -453,7 +564,7 @@ private: | |||
| 453 | 564 | ||
| 454 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 565 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 455 | rb.Push(ResultSuccess); | 566 | rb.Push(ResultSuccess); |
| 456 | rb.PushIpcInterface<ISystemDisplayService>(system); | 567 | rb.PushIpcInterface<ISystemDisplayService>(system, nv_flinger); |
| 457 | } | 568 | } |
| 458 | 569 | ||
| 459 | void GetManagerDisplayService(HLERequestContext& ctx) { | 570 | void GetManagerDisplayService(HLERequestContext& ctx) { |
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 5a42dea48..5c36b71e5 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp | |||
| @@ -118,7 +118,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect | |||
| 118 | return {ResultStatus::ErrorMissingNPDM, {}}; | 118 | return {ResultStatus::ErrorMissingNPDM, {}}; |
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | const ResultStatus result2 = metadata.Load(npdm); | 121 | const ResultStatus result2 = metadata.Reload(npdm); |
| 122 | if (result2 != ResultStatus::Success) { | 122 | if (result2 != ResultStatus::Success) { |
| 123 | return {result2, {}}; | 123 | return {result2, {}}; |
| 124 | } | 124 | } |
diff --git a/src/input_common/drivers/virtual_gamepad.h b/src/input_common/drivers/virtual_gamepad.h index dfbc45a28..3a40e3fd3 100644 --- a/src/input_common/drivers/virtual_gamepad.h +++ b/src/input_common/drivers/virtual_gamepad.h | |||
| @@ -67,7 +67,7 @@ public: | |||
| 67 | * @param player_index the player number that will take this action | 67 | * @param player_index the player number that will take this action |
| 68 | * @param delta_timestamp time passed since last reading | 68 | * @param delta_timestamp time passed since last reading |
| 69 | * @param gyro_x,gyro_y,gyro_z the gyro sensor readings | 69 | * @param gyro_x,gyro_y,gyro_z the gyro sensor readings |
| 70 | * @param accel_x,accel_y,accel_z the acelerometer reading | 70 | * @param accel_x,accel_y,accel_z the accelerometer reading |
| 71 | */ | 71 | */ |
| 72 | void SetMotionState(std::size_t player_index, u64 delta_timestamp, float gyro_x, float gyro_y, | 72 | void SetMotionState(std::size_t player_index, u64 delta_timestamp, float gyro_x, float gyro_y, |
| 73 | float gyro_z, float accel_x, float accel_y, float accel_z); | 73 | float gyro_z, float accel_x, float accel_y, float accel_z); |
diff --git a/src/input_common/helpers/joycon_protocol/generic_functions.h b/src/input_common/helpers/joycon_protocol/generic_functions.h index 90fcd17f6..b94567f82 100644 --- a/src/input_common/helpers/joycon_protocol/generic_functions.h +++ b/src/input_common/helpers/joycon_protocol/generic_functions.h | |||
| @@ -55,7 +55,7 @@ public: | |||
| 55 | 55 | ||
| 56 | /** | 56 | /** |
| 57 | * Configures the motion sensor with the specified parameters | 57 | * Configures the motion sensor with the specified parameters |
| 58 | * @param gsen gyroscope sensor sensitvity in degrees per second | 58 | * @param gsen gyroscope sensor sensitivity in degrees per second |
| 59 | * @param gfrec gyroscope sensor frequency in hertz | 59 | * @param gfrec gyroscope sensor frequency in hertz |
| 60 | * @param asen accelerometer sensitivity in G force | 60 | * @param asen accelerometer sensitivity in G force |
| 61 | * @param afrec accelerometer frequency in hertz | 61 | * @param afrec accelerometer frequency in hertz |
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_composite.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_composite.cpp index d508ee567..4e8ba4ae6 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_composite.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_composite.cpp | |||
| @@ -55,7 +55,7 @@ void CompositeInsert(EmitContext& ctx, IR::Inst& inst, Register composite, Objec | |||
| 55 | "MOV.{} {}.{},{};", | 55 | "MOV.{} {}.{},{};", |
| 56 | type, ret, composite, type, ret, swizzle, object); | 56 | type, ret, composite, type, ret, swizzle, object); |
| 57 | } else { | 57 | } else { |
| 58 | // The return value is alised so we can just insert the object, it doesn't matter if it's | 58 | // The return value is aliased so we can just insert the object, it doesn't matter if it's |
| 59 | // aliased | 59 | // aliased |
| 60 | ctx.Add("MOV.{} {}.{},{};", type, ret, swizzle, object); | 60 | ctx.Add("MOV.{} {}.{},{};", type, ret, swizzle, object); |
| 61 | } | 61 | } |
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 9e90c587c..9b2698fad 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h | |||
| @@ -544,7 +544,7 @@ void BufferCache<P>::CommitAsyncFlushesHigh() { | |||
| 544 | it++; | 544 | it++; |
| 545 | } | 545 | } |
| 546 | 546 | ||
| 547 | boost::container::small_vector<std::pair<BufferCopy, BufferId>, 1> downloads; | 547 | boost::container::small_vector<std::pair<BufferCopy, BufferId>, 16> downloads; |
| 548 | u64 total_size_bytes = 0; | 548 | u64 total_size_bytes = 0; |
| 549 | u64 largest_copy = 0; | 549 | u64 largest_copy = 0; |
| 550 | for (const IntervalSet& intervals : committed_ranges) { | 550 | for (const IntervalSet& intervals : committed_ranges) { |
| @@ -914,6 +914,11 @@ void BufferCache<P>::BindHostGraphicsStorageBuffers(size_t stage) { | |||
| 914 | 914 | ||
| 915 | const u32 offset = buffer.Offset(binding.cpu_addr); | 915 | const u32 offset = buffer.Offset(binding.cpu_addr); |
| 916 | const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0; | 916 | const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0; |
| 917 | |||
| 918 | if (is_written) { | ||
| 919 | MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, size); | ||
| 920 | } | ||
| 921 | |||
| 917 | if constexpr (NEEDS_BIND_STORAGE_INDEX) { | 922 | if constexpr (NEEDS_BIND_STORAGE_INDEX) { |
| 918 | runtime.BindStorageBuffer(stage, binding_index, buffer, offset, size, is_written); | 923 | runtime.BindStorageBuffer(stage, binding_index, buffer, offset, size, is_written); |
| 919 | ++binding_index; | 924 | ++binding_index; |
| @@ -931,6 +936,11 @@ void BufferCache<P>::BindHostGraphicsTextureBuffers(size_t stage) { | |||
| 931 | const u32 size = binding.size; | 936 | const u32 size = binding.size; |
| 932 | SynchronizeBuffer(buffer, binding.cpu_addr, size); | 937 | SynchronizeBuffer(buffer, binding.cpu_addr, size); |
| 933 | 938 | ||
| 939 | const bool is_written = ((channel_state->written_texture_buffers[stage] >> index) & 1) != 0; | ||
| 940 | if (is_written) { | ||
| 941 | MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, size); | ||
| 942 | } | ||
| 943 | |||
| 934 | const u32 offset = buffer.Offset(binding.cpu_addr); | 944 | const u32 offset = buffer.Offset(binding.cpu_addr); |
| 935 | const PixelFormat format = binding.format; | 945 | const PixelFormat format = binding.format; |
| 936 | if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) { | 946 | if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) { |
| @@ -962,6 +972,8 @@ void BufferCache<P>::BindHostTransformFeedbackBuffers() { | |||
| 962 | const u32 size = binding.size; | 972 | const u32 size = binding.size; |
| 963 | SynchronizeBuffer(buffer, binding.cpu_addr, size); | 973 | SynchronizeBuffer(buffer, binding.cpu_addr, size); |
| 964 | 974 | ||
| 975 | MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, size); | ||
| 976 | |||
| 965 | const u32 offset = buffer.Offset(binding.cpu_addr); | 977 | const u32 offset = buffer.Offset(binding.cpu_addr); |
| 966 | host_bindings.buffers.push_back(&buffer); | 978 | host_bindings.buffers.push_back(&buffer); |
| 967 | host_bindings.offsets.push_back(offset); | 979 | host_bindings.offsets.push_back(offset); |
| @@ -1011,6 +1023,11 @@ void BufferCache<P>::BindHostComputeStorageBuffers() { | |||
| 1011 | const u32 offset = buffer.Offset(binding.cpu_addr); | 1023 | const u32 offset = buffer.Offset(binding.cpu_addr); |
| 1012 | const bool is_written = | 1024 | const bool is_written = |
| 1013 | ((channel_state->written_compute_storage_buffers >> index) & 1) != 0; | 1025 | ((channel_state->written_compute_storage_buffers >> index) & 1) != 0; |
| 1026 | |||
| 1027 | if (is_written) { | ||
| 1028 | MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, size); | ||
| 1029 | } | ||
| 1030 | |||
| 1014 | if constexpr (NEEDS_BIND_STORAGE_INDEX) { | 1031 | if constexpr (NEEDS_BIND_STORAGE_INDEX) { |
| 1015 | runtime.BindComputeStorageBuffer(binding_index, buffer, offset, size, is_written); | 1032 | runtime.BindComputeStorageBuffer(binding_index, buffer, offset, size, is_written); |
| 1016 | ++binding_index; | 1033 | ++binding_index; |
| @@ -1028,6 +1045,12 @@ void BufferCache<P>::BindHostComputeTextureBuffers() { | |||
| 1028 | const u32 size = binding.size; | 1045 | const u32 size = binding.size; |
| 1029 | SynchronizeBuffer(buffer, binding.cpu_addr, size); | 1046 | SynchronizeBuffer(buffer, binding.cpu_addr, size); |
| 1030 | 1047 | ||
| 1048 | const bool is_written = | ||
| 1049 | ((channel_state->written_compute_texture_buffers >> index) & 1) != 0; | ||
| 1050 | if (is_written) { | ||
| 1051 | MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, size); | ||
| 1052 | } | ||
| 1053 | |||
| 1031 | const u32 offset = buffer.Offset(binding.cpu_addr); | 1054 | const u32 offset = buffer.Offset(binding.cpu_addr); |
| 1032 | const PixelFormat format = binding.format; | 1055 | const PixelFormat format = binding.format; |
| 1033 | if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) { | 1056 | if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) { |
| @@ -1201,16 +1224,11 @@ void BufferCache<P>::UpdateUniformBuffers(size_t stage) { | |||
| 1201 | 1224 | ||
| 1202 | template <class P> | 1225 | template <class P> |
| 1203 | void BufferCache<P>::UpdateStorageBuffers(size_t stage) { | 1226 | void BufferCache<P>::UpdateStorageBuffers(size_t stage) { |
| 1204 | const u32 written_mask = channel_state->written_storage_buffers[stage]; | ||
| 1205 | ForEachEnabledBit(channel_state->enabled_storage_buffers[stage], [&](u32 index) { | 1227 | ForEachEnabledBit(channel_state->enabled_storage_buffers[stage], [&](u32 index) { |
| 1206 | // Resolve buffer | 1228 | // Resolve buffer |
| 1207 | Binding& binding = channel_state->storage_buffers[stage][index]; | 1229 | Binding& binding = channel_state->storage_buffers[stage][index]; |
| 1208 | const BufferId buffer_id = FindBuffer(binding.cpu_addr, binding.size); | 1230 | const BufferId buffer_id = FindBuffer(binding.cpu_addr, binding.size); |
| 1209 | binding.buffer_id = buffer_id; | 1231 | binding.buffer_id = buffer_id; |
| 1210 | // Mark buffer as written if needed | ||
| 1211 | if (((written_mask >> index) & 1) != 0) { | ||
| 1212 | MarkWrittenBuffer(buffer_id, binding.cpu_addr, binding.size); | ||
| 1213 | } | ||
| 1214 | }); | 1232 | }); |
| 1215 | } | 1233 | } |
| 1216 | 1234 | ||
| @@ -1219,10 +1237,6 @@ void BufferCache<P>::UpdateTextureBuffers(size_t stage) { | |||
| 1219 | ForEachEnabledBit(channel_state->enabled_texture_buffers[stage], [&](u32 index) { | 1237 | ForEachEnabledBit(channel_state->enabled_texture_buffers[stage], [&](u32 index) { |
| 1220 | Binding& binding = channel_state->texture_buffers[stage][index]; | 1238 | Binding& binding = channel_state->texture_buffers[stage][index]; |
| 1221 | binding.buffer_id = FindBuffer(binding.cpu_addr, binding.size); | 1239 | binding.buffer_id = FindBuffer(binding.cpu_addr, binding.size); |
| 1222 | // Mark buffer as written if needed | ||
| 1223 | if (((channel_state->written_texture_buffers[stage] >> index) & 1) != 0) { | ||
| 1224 | MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, binding.size); | ||
| 1225 | } | ||
| 1226 | }); | 1240 | }); |
| 1227 | } | 1241 | } |
| 1228 | 1242 | ||
| @@ -1252,7 +1266,6 @@ void BufferCache<P>::UpdateTransformFeedbackBuffer(u32 index) { | |||
| 1252 | .size = size, | 1266 | .size = size, |
| 1253 | .buffer_id = buffer_id, | 1267 | .buffer_id = buffer_id, |
| 1254 | }; | 1268 | }; |
| 1255 | MarkWrittenBuffer(buffer_id, *cpu_addr, size); | ||
| 1256 | } | 1269 | } |
| 1257 | 1270 | ||
| 1258 | template <class P> | 1271 | template <class P> |
| @@ -1279,10 +1292,6 @@ void BufferCache<P>::UpdateComputeStorageBuffers() { | |||
| 1279 | // Resolve buffer | 1292 | // Resolve buffer |
| 1280 | Binding& binding = channel_state->compute_storage_buffers[index]; | 1293 | Binding& binding = channel_state->compute_storage_buffers[index]; |
| 1281 | binding.buffer_id = FindBuffer(binding.cpu_addr, binding.size); | 1294 | binding.buffer_id = FindBuffer(binding.cpu_addr, binding.size); |
| 1282 | // Mark as written if needed | ||
| 1283 | if (((channel_state->written_compute_storage_buffers >> index) & 1) != 0) { | ||
| 1284 | MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, binding.size); | ||
| 1285 | } | ||
| 1286 | }); | 1295 | }); |
| 1287 | } | 1296 | } |
| 1288 | 1297 | ||
| @@ -1291,18 +1300,11 @@ void BufferCache<P>::UpdateComputeTextureBuffers() { | |||
| 1291 | ForEachEnabledBit(channel_state->enabled_compute_texture_buffers, [&](u32 index) { | 1300 | ForEachEnabledBit(channel_state->enabled_compute_texture_buffers, [&](u32 index) { |
| 1292 | Binding& binding = channel_state->compute_texture_buffers[index]; | 1301 | Binding& binding = channel_state->compute_texture_buffers[index]; |
| 1293 | binding.buffer_id = FindBuffer(binding.cpu_addr, binding.size); | 1302 | binding.buffer_id = FindBuffer(binding.cpu_addr, binding.size); |
| 1294 | // Mark as written if needed | ||
| 1295 | if (((channel_state->written_compute_texture_buffers >> index) & 1) != 0) { | ||
| 1296 | MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, binding.size); | ||
| 1297 | } | ||
| 1298 | }); | 1303 | }); |
| 1299 | } | 1304 | } |
| 1300 | 1305 | ||
| 1301 | template <class P> | 1306 | template <class P> |
| 1302 | void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, VAddr cpu_addr, u32 size) { | 1307 | void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, VAddr cpu_addr, u32 size) { |
| 1303 | if (memory_tracker.IsRegionCpuModified(cpu_addr, size)) { | ||
| 1304 | SynchronizeBuffer(slot_buffers[buffer_id], cpu_addr, size); | ||
| 1305 | } | ||
| 1306 | memory_tracker.MarkRegionAsGpuModified(cpu_addr, size); | 1308 | memory_tracker.MarkRegionAsGpuModified(cpu_addr, size); |
| 1307 | 1309 | ||
| 1308 | const IntervalType base_interval{cpu_addr, cpu_addr + size}; | 1310 | const IntervalType base_interval{cpu_addr, cpu_addr + size}; |
diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h index c9fab2d90..e46a8fa5c 100644 --- a/src/video_core/dma_pusher.h +++ b/src/video_core/dma_pusher.h | |||
| @@ -161,7 +161,7 @@ private: | |||
| 161 | u32 method_count; ///< Current method count | 161 | u32 method_count; ///< Current method count |
| 162 | u32 length_pending; ///< Large NI command length pending | 162 | u32 length_pending; ///< Large NI command length pending |
| 163 | GPUVAddr dma_get; ///< Currently read segment | 163 | GPUVAddr dma_get; ///< Currently read segment |
| 164 | u64 dma_word_offset; ///< Current word ofset from address | 164 | u64 dma_word_offset; ///< Current word offset from address |
| 165 | bool non_incrementing; ///< Current command's NI flag | 165 | bool non_incrementing; ///< Current command's NI flag |
| 166 | bool is_last_call; | 166 | bool is_last_call; |
| 167 | }; | 167 | }; |
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index 6b912027f..8bb429578 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt | |||
| @@ -19,6 +19,7 @@ set(SHADER_FILES | |||
| 19 | block_linear_unswizzle_2d.comp | 19 | block_linear_unswizzle_2d.comp |
| 20 | block_linear_unswizzle_3d.comp | 20 | block_linear_unswizzle_3d.comp |
| 21 | convert_abgr8_to_d24s8.frag | 21 | convert_abgr8_to_d24s8.frag |
| 22 | convert_d32f_to_abgr8.frag | ||
| 22 | convert_d24s8_to_abgr8.frag | 23 | convert_d24s8_to_abgr8.frag |
| 23 | convert_depth_to_float.frag | 24 | convert_depth_to_float.frag |
| 24 | convert_float_to_depth.frag | 25 | convert_float_to_depth.frag |
diff --git a/src/video_core/host_shaders/convert_d32f_to_abgr8.frag b/src/video_core/host_shaders/convert_d32f_to_abgr8.frag new file mode 100644 index 000000000..04cfef8b5 --- /dev/null +++ b/src/video_core/host_shaders/convert_d32f_to_abgr8.frag | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #version 450 | ||
| 5 | |||
| 6 | layout(binding = 0) uniform sampler2D depth_tex; | ||
| 7 | |||
| 8 | layout(location = 0) out vec4 color; | ||
| 9 | |||
| 10 | void main() { | ||
| 11 | ivec2 coord = ivec2(gl_FragCoord.xy); | ||
| 12 | float depth = textureLod(depth_tex, coord, 0).r; | ||
| 13 | color = vec4(depth, depth, depth, 1.0); | ||
| 14 | } | ||
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index 3e12a8813..78ea5208b 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h | |||
| @@ -89,9 +89,6 @@ public: | |||
| 89 | void RequestScreenshot(void* data, std::function<void(bool)> callback, | 89 | void RequestScreenshot(void* data, std::function<void(bool)> callback, |
| 90 | const Layout::FramebufferLayout& layout); | 90 | const Layout::FramebufferLayout& layout); |
| 91 | 91 | ||
| 92 | /// This is called to notify the rendering backend of a surface change | ||
| 93 | virtual void NotifySurfaceChanged() {} | ||
| 94 | |||
| 95 | protected: | 92 | protected: |
| 96 | Core::Frontend::EmuWindow& render_window; ///< Reference to the render window handle. | 93 | Core::Frontend::EmuWindow& render_window; ///< Reference to the render window handle. |
| 97 | std::unique_ptr<Core::Frontend::GraphicsContext> context; | 94 | std::unique_ptr<Core::Frontend::GraphicsContext> context; |
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index 9cafd2983..512eef575 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp | |||
| @@ -1048,6 +1048,10 @@ void Image::Scale(bool up_scale) { | |||
| 1048 | } | 1048 | } |
| 1049 | 1049 | ||
| 1050 | bool Image::ScaleUp(bool ignore) { | 1050 | bool Image::ScaleUp(bool ignore) { |
| 1051 | const auto& resolution = runtime->resolution; | ||
| 1052 | if (!resolution.active) { | ||
| 1053 | return false; | ||
| 1054 | } | ||
| 1051 | if (True(flags & ImageFlagBits::Rescaled)) { | 1055 | if (True(flags & ImageFlagBits::Rescaled)) { |
| 1052 | return false; | 1056 | return false; |
| 1053 | } | 1057 | } |
| @@ -1060,9 +1064,6 @@ bool Image::ScaleUp(bool ignore) { | |||
| 1060 | return false; | 1064 | return false; |
| 1061 | } | 1065 | } |
| 1062 | flags |= ImageFlagBits::Rescaled; | 1066 | flags |= ImageFlagBits::Rescaled; |
| 1063 | if (!runtime->resolution.active) { | ||
| 1064 | return false; | ||
| 1065 | } | ||
| 1066 | has_scaled = true; | 1067 | has_scaled = true; |
| 1067 | if (ignore) { | 1068 | if (ignore) { |
| 1068 | current_texture = upscaled_backup.handle; | 1069 | current_texture = upscaled_backup.handle; |
| @@ -1073,13 +1074,14 @@ bool Image::ScaleUp(bool ignore) { | |||
| 1073 | } | 1074 | } |
| 1074 | 1075 | ||
| 1075 | bool Image::ScaleDown(bool ignore) { | 1076 | bool Image::ScaleDown(bool ignore) { |
| 1076 | if (False(flags & ImageFlagBits::Rescaled)) { | 1077 | const auto& resolution = runtime->resolution; |
| 1078 | if (!resolution.active) { | ||
| 1077 | return false; | 1079 | return false; |
| 1078 | } | 1080 | } |
| 1079 | flags &= ~ImageFlagBits::Rescaled; | 1081 | if (False(flags & ImageFlagBits::Rescaled)) { |
| 1080 | if (!runtime->resolution.active) { | ||
| 1081 | return false; | 1082 | return false; |
| 1082 | } | 1083 | } |
| 1084 | flags &= ~ImageFlagBits::Rescaled; | ||
| 1083 | if (ignore) { | 1085 | if (ignore) { |
| 1084 | current_texture = texture.handle; | 1086 | current_texture = texture.handle; |
| 1085 | return true; | 1087 | return true; |
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h index 3676eaaa9..e71b87e99 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.h +++ b/src/video_core/renderer_opengl/gl_texture_cache.h | |||
| @@ -118,6 +118,8 @@ public: | |||
| 118 | 118 | ||
| 119 | void InsertUploadMemoryBarrier(); | 119 | void InsertUploadMemoryBarrier(); |
| 120 | 120 | ||
| 121 | void TransitionImageLayout(Image& image) {} | ||
| 122 | |||
| 121 | FormatProperties FormatInfo(VideoCommon::ImageType type, GLenum internal_format) const; | 123 | FormatProperties FormatInfo(VideoCommon::ImageType type, GLenum internal_format) const; |
| 122 | 124 | ||
| 123 | bool HasNativeBgr() const noexcept { | 125 | bool HasNativeBgr() const noexcept { |
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index c7dc7e0a1..5ea9e2378 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h | |||
| @@ -116,6 +116,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> FORMAT_TAB | |||
| 116 | {GL_RGB9_E5, GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV}, // E5B9G9R9_FLOAT | 116 | {GL_RGB9_E5, GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV}, // E5B9G9R9_FLOAT |
| 117 | {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT}, // D32_FLOAT | 117 | {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT}, // D32_FLOAT |
| 118 | {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // D16_UNORM | 118 | {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // D16_UNORM |
| 119 | {GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT_24_8}, // X8_D24_UNORM | ||
| 119 | {GL_STENCIL_INDEX8, GL_STENCIL, GL_UNSIGNED_BYTE}, // S8_UINT | 120 | {GL_STENCIL_INDEX8, GL_STENCIL, GL_UNSIGNED_BYTE}, // S8_UINT |
| 120 | {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // D24_UNORM_S8_UINT | 121 | {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // D24_UNORM_S8_UINT |
| 121 | {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // S8_UINT_D24_UNORM | 122 | {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // S8_UINT_D24_UNORM |
diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index 1032c9d12..f01d2394e 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "video_core/host_shaders/blit_color_float_frag_spv.h" | 9 | #include "video_core/host_shaders/blit_color_float_frag_spv.h" |
| 10 | #include "video_core/host_shaders/convert_abgr8_to_d24s8_frag_spv.h" | 10 | #include "video_core/host_shaders/convert_abgr8_to_d24s8_frag_spv.h" |
| 11 | #include "video_core/host_shaders/convert_d24s8_to_abgr8_frag_spv.h" | 11 | #include "video_core/host_shaders/convert_d24s8_to_abgr8_frag_spv.h" |
| 12 | #include "video_core/host_shaders/convert_d32f_to_abgr8_frag_spv.h" | ||
| 12 | #include "video_core/host_shaders/convert_depth_to_float_frag_spv.h" | 13 | #include "video_core/host_shaders/convert_depth_to_float_frag_spv.h" |
| 13 | #include "video_core/host_shaders/convert_float_to_depth_frag_spv.h" | 14 | #include "video_core/host_shaders/convert_float_to_depth_frag_spv.h" |
| 14 | #include "video_core/host_shaders/convert_s8d24_to_abgr8_frag_spv.h" | 15 | #include "video_core/host_shaders/convert_s8d24_to_abgr8_frag_spv.h" |
| @@ -433,6 +434,7 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_, | |||
| 433 | convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)), | 434 | convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)), |
| 434 | convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)), | 435 | convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)), |
| 435 | convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)), | 436 | convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)), |
| 437 | convert_d32f_to_abgr8_frag(BuildShader(device, CONVERT_D32F_TO_ABGR8_FRAG_SPV)), | ||
| 436 | convert_d24s8_to_abgr8_frag(BuildShader(device, CONVERT_D24S8_TO_ABGR8_FRAG_SPV)), | 438 | convert_d24s8_to_abgr8_frag(BuildShader(device, CONVERT_D24S8_TO_ABGR8_FRAG_SPV)), |
| 437 | convert_s8d24_to_abgr8_frag(BuildShader(device, CONVERT_S8D24_TO_ABGR8_FRAG_SPV)), | 439 | convert_s8d24_to_abgr8_frag(BuildShader(device, CONVERT_S8D24_TO_ABGR8_FRAG_SPV)), |
| 438 | linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)), | 440 | linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)), |
| @@ -557,6 +559,13 @@ void BlitImageHelper::ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer, | |||
| 557 | Convert(*convert_abgr8_to_d24s8_pipeline, dst_framebuffer, src_image_view); | 559 | Convert(*convert_abgr8_to_d24s8_pipeline, dst_framebuffer, src_image_view); |
| 558 | } | 560 | } |
| 559 | 561 | ||
| 562 | void BlitImageHelper::ConvertD32FToABGR8(const Framebuffer* dst_framebuffer, | ||
| 563 | ImageView& src_image_view) { | ||
| 564 | ConvertPipelineColorTargetEx(convert_d32f_to_abgr8_pipeline, dst_framebuffer->RenderPass(), | ||
| 565 | convert_d32f_to_abgr8_frag); | ||
| 566 | ConvertDepthStencil(*convert_d32f_to_abgr8_pipeline, dst_framebuffer, src_image_view); | ||
| 567 | } | ||
| 568 | |||
| 560 | void BlitImageHelper::ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer, | 569 | void BlitImageHelper::ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer, |
| 561 | ImageView& src_image_view) { | 570 | ImageView& src_image_view) { |
| 562 | ConvertPipelineColorTargetEx(convert_d24s8_to_abgr8_pipeline, dst_framebuffer->RenderPass(), | 571 | ConvertPipelineColorTargetEx(convert_d24s8_to_abgr8_pipeline, dst_framebuffer->RenderPass(), |
| @@ -609,6 +618,8 @@ void BlitImageHelper::ClearDepthStencil(const Framebuffer* dst_framebuffer, bool | |||
| 609 | const VkPipelineLayout layout = *clear_color_pipeline_layout; | 618 | const VkPipelineLayout layout = *clear_color_pipeline_layout; |
| 610 | scheduler.RequestRenderpass(dst_framebuffer); | 619 | scheduler.RequestRenderpass(dst_framebuffer); |
| 611 | scheduler.Record([pipeline, layout, clear_depth, dst_region](vk::CommandBuffer cmdbuf) { | 620 | scheduler.Record([pipeline, layout, clear_depth, dst_region](vk::CommandBuffer cmdbuf) { |
| 621 | constexpr std::array blend_constants{0.0f, 0.0f, 0.0f, 0.0f}; | ||
| 622 | cmdbuf.SetBlendConstants(blend_constants.data()); | ||
| 612 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); | 623 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); |
| 613 | BindBlitState(cmdbuf, dst_region); | 624 | BindBlitState(cmdbuf, dst_region); |
| 614 | cmdbuf.PushConstants(layout, VK_SHADER_STAGE_FRAGMENT_BIT, clear_depth); | 625 | cmdbuf.PushConstants(layout, VK_SHADER_STAGE_FRAGMENT_BIT, clear_depth); |
| @@ -865,7 +876,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline( | |||
| 865 | .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, | 876 | .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, |
| 866 | .pNext = nullptr, | 877 | .pNext = nullptr, |
| 867 | .flags = 0, | 878 | .flags = 0, |
| 868 | .depthTestEnable = VK_FALSE, | 879 | .depthTestEnable = key.depth_clear, |
| 869 | .depthWriteEnable = key.depth_clear, | 880 | .depthWriteEnable = key.depth_clear, |
| 870 | .depthCompareOp = VK_COMPARE_OP_ALWAYS, | 881 | .depthCompareOp = VK_COMPARE_OP_ALWAYS, |
| 871 | .depthBoundsTestEnable = VK_FALSE, | 882 | .depthBoundsTestEnable = VK_FALSE, |
diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h index dcfe217aa..a032c71fb 100644 --- a/src/video_core/renderer_vulkan/blit_image.h +++ b/src/video_core/renderer_vulkan/blit_image.h | |||
| @@ -67,6 +67,8 @@ public: | |||
| 67 | 67 | ||
| 68 | void ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer, const ImageView& src_image_view); | 68 | void ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer, const ImageView& src_image_view); |
| 69 | 69 | ||
| 70 | void ConvertD32FToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view); | ||
| 71 | |||
| 70 | void ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view); | 72 | void ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view); |
| 71 | 73 | ||
| 72 | void ConvertS8D24ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view); | 74 | void ConvertS8D24ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view); |
| @@ -128,6 +130,7 @@ private: | |||
| 128 | vk::ShaderModule convert_depth_to_float_frag; | 130 | vk::ShaderModule convert_depth_to_float_frag; |
| 129 | vk::ShaderModule convert_float_to_depth_frag; | 131 | vk::ShaderModule convert_float_to_depth_frag; |
| 130 | vk::ShaderModule convert_abgr8_to_d24s8_frag; | 132 | vk::ShaderModule convert_abgr8_to_d24s8_frag; |
| 133 | vk::ShaderModule convert_d32f_to_abgr8_frag; | ||
| 131 | vk::ShaderModule convert_d24s8_to_abgr8_frag; | 134 | vk::ShaderModule convert_d24s8_to_abgr8_frag; |
| 132 | vk::ShaderModule convert_s8d24_to_abgr8_frag; | 135 | vk::ShaderModule convert_s8d24_to_abgr8_frag; |
| 133 | vk::Sampler linear_sampler; | 136 | vk::Sampler linear_sampler; |
| @@ -146,6 +149,7 @@ private: | |||
| 146 | vk::Pipeline convert_d16_to_r16_pipeline; | 149 | vk::Pipeline convert_d16_to_r16_pipeline; |
| 147 | vk::Pipeline convert_r16_to_d16_pipeline; | 150 | vk::Pipeline convert_r16_to_d16_pipeline; |
| 148 | vk::Pipeline convert_abgr8_to_d24s8_pipeline; | 151 | vk::Pipeline convert_abgr8_to_d24s8_pipeline; |
| 152 | vk::Pipeline convert_d32f_to_abgr8_pipeline; | ||
| 149 | vk::Pipeline convert_d24s8_to_abgr8_pipeline; | 153 | vk::Pipeline convert_d24s8_to_abgr8_pipeline; |
| 150 | vk::Pipeline convert_s8d24_to_abgr8_pipeline; | 154 | vk::Pipeline convert_s8d24_to_abgr8_pipeline; |
| 151 | }; | 155 | }; |
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index 208e88533..a08f2f67f 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp | |||
| @@ -214,8 +214,9 @@ struct FormatTuple { | |||
| 214 | {VK_FORMAT_E5B9G9R9_UFLOAT_PACK32}, // E5B9G9R9_FLOAT | 214 | {VK_FORMAT_E5B9G9R9_UFLOAT_PACK32}, // E5B9G9R9_FLOAT |
| 215 | 215 | ||
| 216 | // Depth formats | 216 | // Depth formats |
| 217 | {VK_FORMAT_D32_SFLOAT, Attachable}, // D32_FLOAT | 217 | {VK_FORMAT_D32_SFLOAT, Attachable}, // D32_FLOAT |
| 218 | {VK_FORMAT_D16_UNORM, Attachable}, // D16_UNORM | 218 | {VK_FORMAT_D16_UNORM, Attachable}, // D16_UNORM |
| 219 | {VK_FORMAT_X8_D24_UNORM_PACK32, Attachable}, // X8_D24_UNORM | ||
| 219 | 220 | ||
| 220 | // Stencil formats | 221 | // Stencil formats |
| 221 | {VK_FORMAT_S8_UINT, Attachable}, // S8_UINT | 222 | {VK_FORMAT_S8_UINT, Attachable}, // S8_UINT |
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h index 590bc1c64..14e257cf7 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.h | |||
| @@ -56,10 +56,6 @@ public: | |||
| 56 | return device.GetDriverName(); | 56 | return device.GetDriverName(); |
| 57 | } | 57 | } |
| 58 | 58 | ||
| 59 | void NotifySurfaceChanged() override { | ||
| 60 | present_manager.NotifySurfaceChanged(); | ||
| 61 | } | ||
| 62 | |||
| 63 | private: | 59 | private: |
| 64 | void Report() const; | 60 | void Report() const; |
| 65 | 61 | ||
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index 31928bb94..52fc142d1 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp | |||
| @@ -96,6 +96,7 @@ std::size_t GetSizeInBytes(const Tegra::FramebufferConfig& framebuffer) { | |||
| 96 | VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) { | 96 | VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) { |
| 97 | switch (framebuffer.pixel_format) { | 97 | switch (framebuffer.pixel_format) { |
| 98 | case Service::android::PixelFormat::Rgba8888: | 98 | case Service::android::PixelFormat::Rgba8888: |
| 99 | case Service::android::PixelFormat::Rgbx8888: | ||
| 99 | return VK_FORMAT_A8B8G8R8_UNORM_PACK32; | 100 | return VK_FORMAT_A8B8G8R8_UNORM_PACK32; |
| 100 | case Service::android::PixelFormat::Rgb565: | 101 | case Service::android::PixelFormat::Rgb565: |
| 101 | return VK_FORMAT_R5G6B5_UNORM_PACK16; | 102 | return VK_FORMAT_R5G6B5_UNORM_PACK16; |
diff --git a/src/video_core/renderer_vulkan/vk_present_manager.cpp b/src/video_core/renderer_vulkan/vk_present_manager.cpp index d681bd22a..2ef36583b 100644 --- a/src/video_core/renderer_vulkan/vk_present_manager.cpp +++ b/src/video_core/renderer_vulkan/vk_present_manager.cpp | |||
| @@ -103,8 +103,7 @@ PresentManager::PresentManager(const vk::Instance& instance_, | |||
| 103 | surface{surface_}, blit_supported{CanBlitToSwapchain(device.GetPhysical(), | 103 | surface{surface_}, blit_supported{CanBlitToSwapchain(device.GetPhysical(), |
| 104 | swapchain.GetImageViewFormat())}, | 104 | swapchain.GetImageViewFormat())}, |
| 105 | use_present_thread{Settings::values.async_presentation.GetValue()}, | 105 | use_present_thread{Settings::values.async_presentation.GetValue()}, |
| 106 | image_count{swapchain.GetImageCount()}, last_render_surface{ | 106 | image_count{swapchain.GetImageCount()} { |
| 107 | render_window_.GetWindowInfo().render_surface} { | ||
| 108 | 107 | ||
| 109 | auto& dld = device.GetLogical(); | 108 | auto& dld = device.GetLogical(); |
| 110 | cmdpool = dld.CreateCommandPool({ | 109 | cmdpool = dld.CreateCommandPool({ |
| @@ -289,44 +288,36 @@ void PresentManager::PresentThread(std::stop_token token) { | |||
| 289 | } | 288 | } |
| 290 | } | 289 | } |
| 291 | 290 | ||
| 292 | void PresentManager::NotifySurfaceChanged() { | 291 | void PresentManager::RecreateSwapchain(Frame* frame) { |
| 293 | #ifdef ANDROID | 292 | swapchain.Create(*surface, frame->width, frame->height, frame->is_srgb); |
| 294 | std::scoped_lock lock{recreate_surface_mutex}; | 293 | image_count = swapchain.GetImageCount(); |
| 295 | recreate_surface_cv.notify_one(); | ||
| 296 | #endif | ||
| 297 | } | 294 | } |
| 298 | 295 | ||
| 299 | void PresentManager::CopyToSwapchain(Frame* frame) { | 296 | void PresentManager::CopyToSwapchain(Frame* frame) { |
| 300 | MICROPROFILE_SCOPE(Vulkan_CopyToSwapchain); | 297 | bool requires_recreation = false; |
| 301 | 298 | ||
| 302 | const auto recreate_swapchain = [&] { | 299 | while (true) { |
| 303 | swapchain.Create(*surface, frame->width, frame->height, frame->is_srgb); | 300 | try { |
| 304 | image_count = swapchain.GetImageCount(); | 301 | // Recreate surface and swapchain if needed. |
| 305 | }; | 302 | if (requires_recreation) { |
| 306 | 303 | surface = CreateSurface(instance, render_window.GetWindowInfo()); | |
| 307 | #ifdef ANDROID | 304 | RecreateSwapchain(frame); |
| 308 | std::unique_lock lock{recreate_surface_mutex}; | 305 | } |
| 309 | 306 | ||
| 310 | const auto needs_recreation = [&] { | 307 | // Draw to swapchain. |
| 311 | if (last_render_surface != render_window.GetWindowInfo().render_surface) { | 308 | return CopyToSwapchainImpl(frame); |
| 312 | return true; | 309 | } catch (const vk::Exception& except) { |
| 313 | } | 310 | if (except.GetResult() != VK_ERROR_SURFACE_LOST_KHR) { |
| 314 | if (swapchain.NeedsRecreation(frame->is_srgb)) { | 311 | throw; |
| 315 | return true; | 312 | } |
| 313 | |||
| 314 | requires_recreation = true; | ||
| 316 | } | 315 | } |
| 317 | return false; | ||
| 318 | }; | ||
| 319 | |||
| 320 | recreate_surface_cv.wait_for(lock, std::chrono::milliseconds(400), | ||
| 321 | [&]() { return !needs_recreation(); }); | ||
| 322 | |||
| 323 | // If the frontend recreated the surface, recreate the renderer surface and swapchain. | ||
| 324 | if (last_render_surface != render_window.GetWindowInfo().render_surface) { | ||
| 325 | last_render_surface = render_window.GetWindowInfo().render_surface; | ||
| 326 | surface = CreateSurface(instance, render_window.GetWindowInfo()); | ||
| 327 | recreate_swapchain(); | ||
| 328 | } | 316 | } |
| 329 | #endif | 317 | } |
| 318 | |||
| 319 | void PresentManager::CopyToSwapchainImpl(Frame* frame) { | ||
| 320 | MICROPROFILE_SCOPE(Vulkan_CopyToSwapchain); | ||
| 330 | 321 | ||
| 331 | // If the size or colorspace of the incoming frames has changed, recreate the swapchain | 322 | // If the size or colorspace of the incoming frames has changed, recreate the swapchain |
| 332 | // to account for that. | 323 | // to account for that. |
| @@ -334,11 +325,11 @@ void PresentManager::CopyToSwapchain(Frame* frame) { | |||
| 334 | const bool size_changed = | 325 | const bool size_changed = |
| 335 | swapchain.GetWidth() != frame->width || swapchain.GetHeight() != frame->height; | 326 | swapchain.GetWidth() != frame->width || swapchain.GetHeight() != frame->height; |
| 336 | if (srgb_changed || size_changed) { | 327 | if (srgb_changed || size_changed) { |
| 337 | recreate_swapchain(); | 328 | RecreateSwapchain(frame); |
| 338 | } | 329 | } |
| 339 | 330 | ||
| 340 | while (swapchain.AcquireNextImage()) { | 331 | while (swapchain.AcquireNextImage()) { |
| 341 | recreate_swapchain(); | 332 | RecreateSwapchain(frame); |
| 342 | } | 333 | } |
| 343 | 334 | ||
| 344 | const vk::CommandBuffer cmdbuf{frame->cmdbuf}; | 335 | const vk::CommandBuffer cmdbuf{frame->cmdbuf}; |
| @@ -488,4 +479,4 @@ void PresentManager::CopyToSwapchain(Frame* frame) { | |||
| 488 | swapchain.Present(render_semaphore); | 479 | swapchain.Present(render_semaphore); |
| 489 | } | 480 | } |
| 490 | 481 | ||
| 491 | } // namespace Vulkan \ No newline at end of file | 482 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/vk_present_manager.h b/src/video_core/renderer_vulkan/vk_present_manager.h index 83e859416..a3d825fe6 100644 --- a/src/video_core/renderer_vulkan/vk_present_manager.h +++ b/src/video_core/renderer_vulkan/vk_present_manager.h | |||
| @@ -54,14 +54,15 @@ public: | |||
| 54 | /// Waits for the present thread to finish presenting all queued frames. | 54 | /// Waits for the present thread to finish presenting all queued frames. |
| 55 | void WaitPresent(); | 55 | void WaitPresent(); |
| 56 | 56 | ||
| 57 | /// This is called to notify the rendering backend of a surface change | ||
| 58 | void NotifySurfaceChanged(); | ||
| 59 | |||
| 60 | private: | 57 | private: |
| 61 | void PresentThread(std::stop_token token); | 58 | void PresentThread(std::stop_token token); |
| 62 | 59 | ||
| 63 | void CopyToSwapchain(Frame* frame); | 60 | void CopyToSwapchain(Frame* frame); |
| 64 | 61 | ||
| 62 | void CopyToSwapchainImpl(Frame* frame); | ||
| 63 | |||
| 64 | void RecreateSwapchain(Frame* frame); | ||
| 65 | |||
| 65 | private: | 66 | private: |
| 66 | const vk::Instance& instance; | 67 | const vk::Instance& instance; |
| 67 | Core::Frontend::EmuWindow& render_window; | 68 | Core::Frontend::EmuWindow& render_window; |
| @@ -76,16 +77,13 @@ private: | |||
| 76 | std::queue<Frame*> free_queue; | 77 | std::queue<Frame*> free_queue; |
| 77 | std::condition_variable_any frame_cv; | 78 | std::condition_variable_any frame_cv; |
| 78 | std::condition_variable free_cv; | 79 | std::condition_variable free_cv; |
| 79 | std::condition_variable recreate_surface_cv; | ||
| 80 | std::mutex swapchain_mutex; | 80 | std::mutex swapchain_mutex; |
| 81 | std::mutex recreate_surface_mutex; | ||
| 82 | std::mutex queue_mutex; | 81 | std::mutex queue_mutex; |
| 83 | std::mutex free_mutex; | 82 | std::mutex free_mutex; |
| 84 | std::jthread present_thread; | 83 | std::jthread present_thread; |
| 85 | bool blit_supported; | 84 | bool blit_supported; |
| 86 | bool use_present_thread; | 85 | bool use_present_thread; |
| 87 | std::size_t image_count{}; | 86 | std::size_t image_count{}; |
| 88 | void* last_render_surface{}; | ||
| 89 | }; | 87 | }; |
| 90 | 88 | ||
| 91 | } // namespace Vulkan | 89 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 1628d76d6..83f2b6045 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp | |||
| @@ -422,7 +422,8 @@ void RasterizerVulkan::Clear(u32 layer_count) { | |||
| 422 | return; | 422 | return; |
| 423 | } | 423 | } |
| 424 | 424 | ||
| 425 | if (use_stencil && regs.stencil_front_mask != 0xFF && regs.stencil_front_mask != 0) { | 425 | if (use_stencil && framebuffer->HasAspectStencilBit() && regs.stencil_front_mask != 0xFF && |
| 426 | regs.stencil_front_mask != 0) { | ||
| 426 | Region2D dst_region = { | 427 | Region2D dst_region = { |
| 427 | Offset2D{.x = clear_rect.rect.offset.x, .y = clear_rect.rect.offset.y}, | 428 | Offset2D{.x = clear_rect.rect.offset.x, .y = clear_rect.rect.offset.y}, |
| 428 | Offset2D{.x = clear_rect.rect.offset.x + static_cast<s32>(clear_rect.rect.extent.width), | 429 | Offset2D{.x = clear_rect.rect.offset.x + static_cast<s32>(clear_rect.rect.extent.width), |
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp index ce92f66ab..b278614e6 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp | |||
| @@ -24,25 +24,38 @@ using namespace Common::Literals; | |||
| 24 | 24 | ||
| 25 | // Maximum potential alignment of a Vulkan buffer | 25 | // Maximum potential alignment of a Vulkan buffer |
| 26 | constexpr VkDeviceSize MAX_ALIGNMENT = 256; | 26 | constexpr VkDeviceSize MAX_ALIGNMENT = 256; |
| 27 | // Maximum size to put elements in the stream buffer | ||
| 28 | constexpr VkDeviceSize MAX_STREAM_BUFFER_REQUEST_SIZE = 8_MiB; | ||
| 29 | // Stream buffer size in bytes | 27 | // Stream buffer size in bytes |
| 30 | constexpr VkDeviceSize STREAM_BUFFER_SIZE = 128_MiB; | 28 | constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 128_MiB; |
| 31 | constexpr VkDeviceSize REGION_SIZE = STREAM_BUFFER_SIZE / StagingBufferPool::NUM_SYNCS; | ||
| 32 | 29 | ||
| 33 | size_t Region(size_t iterator) noexcept { | 30 | size_t GetStreamBufferSize(const Device& device) { |
| 34 | return iterator / REGION_SIZE; | 31 | VkDeviceSize size{0}; |
| 32 | if (device.HasDebuggingToolAttached()) { | ||
| 33 | ForEachDeviceLocalHostVisibleHeap(device, [&size](size_t index, VkMemoryHeap& heap) { | ||
| 34 | size = std::max(size, heap.size); | ||
| 35 | }); | ||
| 36 | // If rebar is not supported, cut the max heap size to 40%. This will allow 2 captures to be | ||
| 37 | // loaded at the same time in RenderDoc. If rebar is supported, this shouldn't be an issue | ||
| 38 | // as the heap will be much larger. | ||
| 39 | if (size <= 256_MiB) { | ||
| 40 | size = size * 40 / 100; | ||
| 41 | } | ||
| 42 | } else { | ||
| 43 | size = MAX_STREAM_BUFFER_SIZE; | ||
| 44 | } | ||
| 45 | return std::min(Common::AlignUp(size, MAX_ALIGNMENT), MAX_STREAM_BUFFER_SIZE); | ||
| 35 | } | 46 | } |
| 36 | } // Anonymous namespace | 47 | } // Anonymous namespace |
| 37 | 48 | ||
| 38 | StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& memory_allocator_, | 49 | StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& memory_allocator_, |
| 39 | Scheduler& scheduler_) | 50 | Scheduler& scheduler_) |
| 40 | : device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_} { | 51 | : device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_}, |
| 52 | stream_buffer_size{GetStreamBufferSize(device)}, region_size{stream_buffer_size / | ||
| 53 | StagingBufferPool::NUM_SYNCS} { | ||
| 41 | VkBufferCreateInfo stream_ci = { | 54 | VkBufferCreateInfo stream_ci = { |
| 42 | .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, | 55 | .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, |
| 43 | .pNext = nullptr, | 56 | .pNext = nullptr, |
| 44 | .flags = 0, | 57 | .flags = 0, |
| 45 | .size = STREAM_BUFFER_SIZE, | 58 | .size = stream_buffer_size, |
| 46 | .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | | 59 | .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | |
| 47 | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, | 60 | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, |
| 48 | .sharingMode = VK_SHARING_MODE_EXCLUSIVE, | 61 | .sharingMode = VK_SHARING_MODE_EXCLUSIVE, |
| @@ -63,7 +76,7 @@ StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& mem | |||
| 63 | StagingBufferPool::~StagingBufferPool() = default; | 76 | StagingBufferPool::~StagingBufferPool() = default; |
| 64 | 77 | ||
| 65 | StagingBufferRef StagingBufferPool::Request(size_t size, MemoryUsage usage, bool deferred) { | 78 | StagingBufferRef StagingBufferPool::Request(size_t size, MemoryUsage usage, bool deferred) { |
| 66 | if (!deferred && usage == MemoryUsage::Upload && size <= MAX_STREAM_BUFFER_REQUEST_SIZE) { | 79 | if (!deferred && usage == MemoryUsage::Upload && size <= region_size) { |
| 67 | return GetStreamBuffer(size); | 80 | return GetStreamBuffer(size); |
| 68 | } | 81 | } |
| 69 | return GetStagingBuffer(size, usage, deferred); | 82 | return GetStagingBuffer(size, usage, deferred); |
| @@ -101,7 +114,7 @@ StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) { | |||
| 101 | used_iterator = iterator; | 114 | used_iterator = iterator; |
| 102 | free_iterator = std::max(free_iterator, iterator + size); | 115 | free_iterator = std::max(free_iterator, iterator + size); |
| 103 | 116 | ||
| 104 | if (iterator + size >= STREAM_BUFFER_SIZE) { | 117 | if (iterator + size >= stream_buffer_size) { |
| 105 | std::fill(sync_ticks.begin() + Region(used_iterator), sync_ticks.begin() + NUM_SYNCS, | 118 | std::fill(sync_ticks.begin() + Region(used_iterator), sync_ticks.begin() + NUM_SYNCS, |
| 106 | current_tick); | 119 | current_tick); |
| 107 | used_iterator = 0; | 120 | used_iterator = 0; |
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h index 5f69f08b1..d3deb9072 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h | |||
| @@ -90,6 +90,9 @@ private: | |||
| 90 | void ReleaseCache(MemoryUsage usage); | 90 | void ReleaseCache(MemoryUsage usage); |
| 91 | 91 | ||
| 92 | void ReleaseLevel(StagingBuffersCache& cache, size_t log2); | 92 | void ReleaseLevel(StagingBuffersCache& cache, size_t log2); |
| 93 | size_t Region(size_t iter) const noexcept { | ||
| 94 | return iter / region_size; | ||
| 95 | } | ||
| 93 | 96 | ||
| 94 | const Device& device; | 97 | const Device& device; |
| 95 | MemoryAllocator& memory_allocator; | 98 | MemoryAllocator& memory_allocator; |
| @@ -97,6 +100,8 @@ private: | |||
| 97 | 100 | ||
| 98 | vk::Buffer stream_buffer; | 101 | vk::Buffer stream_buffer; |
| 99 | std::span<u8> stream_pointer; | 102 | std::span<u8> stream_pointer; |
| 103 | VkDeviceSize stream_buffer_size; | ||
| 104 | VkDeviceSize region_size; | ||
| 100 | 105 | ||
| 101 | size_t iterator = 0; | 106 | size_t iterator = 0; |
| 102 | size_t used_iterator = 0; | 107 | size_t used_iterator = 0; |
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 71fdec809..93773a69f 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp | |||
| @@ -238,6 +238,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) { | |||
| 238 | return any_r ? VK_IMAGE_ASPECT_STENCIL_BIT : VK_IMAGE_ASPECT_DEPTH_BIT; | 238 | return any_r ? VK_IMAGE_ASPECT_STENCIL_BIT : VK_IMAGE_ASPECT_DEPTH_BIT; |
| 239 | case PixelFormat::D16_UNORM: | 239 | case PixelFormat::D16_UNORM: |
| 240 | case PixelFormat::D32_FLOAT: | 240 | case PixelFormat::D32_FLOAT: |
| 241 | case PixelFormat::X8_D24_UNORM: | ||
| 241 | return VK_IMAGE_ASPECT_DEPTH_BIT; | 242 | return VK_IMAGE_ASPECT_DEPTH_BIT; |
| 242 | case PixelFormat::S8_UINT: | 243 | case PixelFormat::S8_UINT: |
| 243 | return VK_IMAGE_ASPECT_STENCIL_BIT; | 244 | return VK_IMAGE_ASPECT_STENCIL_BIT; |
| @@ -1200,6 +1201,9 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im | |||
| 1200 | if (src_view.format == PixelFormat::D24_UNORM_S8_UINT) { | 1201 | if (src_view.format == PixelFormat::D24_UNORM_S8_UINT) { |
| 1201 | return blit_image_helper.ConvertS8D24ToABGR8(dst, src_view); | 1202 | return blit_image_helper.ConvertS8D24ToABGR8(dst, src_view); |
| 1202 | } | 1203 | } |
| 1204 | if (src_view.format == PixelFormat::D32_FLOAT) { | ||
| 1205 | return blit_image_helper.ConvertD32FToABGR8(dst, src_view); | ||
| 1206 | } | ||
| 1203 | break; | 1207 | break; |
| 1204 | case PixelFormat::R32_FLOAT: | 1208 | case PixelFormat::R32_FLOAT: |
| 1205 | if (src_view.format == PixelFormat::D32_FLOAT) { | 1209 | if (src_view.format == PixelFormat::D32_FLOAT) { |
| @@ -1526,15 +1530,15 @@ bool Image::IsRescaled() const noexcept { | |||
| 1526 | } | 1530 | } |
| 1527 | 1531 | ||
| 1528 | bool Image::ScaleUp(bool ignore) { | 1532 | bool Image::ScaleUp(bool ignore) { |
| 1533 | const auto& resolution = runtime->resolution; | ||
| 1534 | if (!resolution.active) { | ||
| 1535 | return false; | ||
| 1536 | } | ||
| 1529 | if (True(flags & ImageFlagBits::Rescaled)) { | 1537 | if (True(flags & ImageFlagBits::Rescaled)) { |
| 1530 | return false; | 1538 | return false; |
| 1531 | } | 1539 | } |
| 1532 | ASSERT(info.type != ImageType::Linear); | 1540 | ASSERT(info.type != ImageType::Linear); |
| 1533 | flags |= ImageFlagBits::Rescaled; | 1541 | flags |= ImageFlagBits::Rescaled; |
| 1534 | const auto& resolution = runtime->resolution; | ||
| 1535 | if (!resolution.active) { | ||
| 1536 | return false; | ||
| 1537 | } | ||
| 1538 | has_scaled = true; | 1542 | has_scaled = true; |
| 1539 | if (!scaled_image) { | 1543 | if (!scaled_image) { |
| 1540 | const bool is_2d = info.type == ImageType::e2D; | 1544 | const bool is_2d = info.type == ImageType::e2D; |
| @@ -1563,15 +1567,15 @@ bool Image::ScaleUp(bool ignore) { | |||
| 1563 | } | 1567 | } |
| 1564 | 1568 | ||
| 1565 | bool Image::ScaleDown(bool ignore) { | 1569 | bool Image::ScaleDown(bool ignore) { |
| 1570 | const auto& resolution = runtime->resolution; | ||
| 1571 | if (!resolution.active) { | ||
| 1572 | return false; | ||
| 1573 | } | ||
| 1566 | if (False(flags & ImageFlagBits::Rescaled)) { | 1574 | if (False(flags & ImageFlagBits::Rescaled)) { |
| 1567 | return false; | 1575 | return false; |
| 1568 | } | 1576 | } |
| 1569 | ASSERT(info.type != ImageType::Linear); | 1577 | ASSERT(info.type != ImageType::Linear); |
| 1570 | flags &= ~ImageFlagBits::Rescaled; | 1578 | flags &= ~ImageFlagBits::Rescaled; |
| 1571 | const auto& resolution = runtime->resolution; | ||
| 1572 | if (!resolution.active) { | ||
| 1573 | return false; | ||
| 1574 | } | ||
| 1575 | current_image = *original_image; | 1579 | current_image = *original_image; |
| 1576 | if (ignore) { | 1580 | if (ignore) { |
| 1577 | return true; | 1581 | return true; |
| @@ -2009,4 +2013,32 @@ void TextureCacheRuntime::AccelerateImageUpload( | |||
| 2009 | ASSERT(false); | 2013 | ASSERT(false); |
| 2010 | } | 2014 | } |
| 2011 | 2015 | ||
| 2016 | void TextureCacheRuntime::TransitionImageLayout(Image& image) { | ||
| 2017 | if (!image.ExchangeInitialization()) { | ||
| 2018 | VkImageMemoryBarrier barrier{ | ||
| 2019 | .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | ||
| 2020 | .pNext = nullptr, | ||
| 2021 | .srcAccessMask = VK_ACCESS_NONE, | ||
| 2022 | .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, | ||
| 2023 | .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, | ||
| 2024 | .newLayout = VK_IMAGE_LAYOUT_GENERAL, | ||
| 2025 | .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 2026 | .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 2027 | .image = image.Handle(), | ||
| 2028 | .subresourceRange{ | ||
| 2029 | .aspectMask = image.AspectMask(), | ||
| 2030 | .baseMipLevel = 0, | ||
| 2031 | .levelCount = VK_REMAINING_MIP_LEVELS, | ||
| 2032 | .baseArrayLayer = 0, | ||
| 2033 | .layerCount = VK_REMAINING_ARRAY_LAYERS, | ||
| 2034 | }, | ||
| 2035 | }; | ||
| 2036 | scheduler.RequestOutsideRenderPassOperationContext(); | ||
| 2037 | scheduler.Record([barrier = barrier](vk::CommandBuffer cmdbuf) { | ||
| 2038 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, | ||
| 2039 | VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, barrier); | ||
| 2040 | }); | ||
| 2041 | } | ||
| 2042 | } | ||
| 2043 | |||
| 2012 | } // namespace Vulkan | 2044 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index d6c5a15cc..7a0807709 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h | |||
| @@ -92,6 +92,8 @@ public: | |||
| 92 | 92 | ||
| 93 | void InsertUploadMemoryBarrier() {} | 93 | void InsertUploadMemoryBarrier() {} |
| 94 | 94 | ||
| 95 | void TransitionImageLayout(Image& image); | ||
| 96 | |||
| 95 | bool HasBrokenTextureViewFormats() const noexcept { | 97 | bool HasBrokenTextureViewFormats() const noexcept { |
| 96 | // No known Vulkan driver has broken image views | 98 | // No known Vulkan driver has broken image views |
| 97 | return false; | 99 | return false; |
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp index e16cd5e73..5b3c7aa5a 100644 --- a/src/video_core/surface.cpp +++ b/src/video_core/surface.cpp | |||
| @@ -85,6 +85,8 @@ PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format) { | |||
| 85 | return PixelFormat::S8_UINT; | 85 | return PixelFormat::S8_UINT; |
| 86 | case Tegra::DepthFormat::Z32_FLOAT_X24S8_UINT: | 86 | case Tegra::DepthFormat::Z32_FLOAT_X24S8_UINT: |
| 87 | return PixelFormat::D32_FLOAT_S8_UINT; | 87 | return PixelFormat::D32_FLOAT_S8_UINT; |
| 88 | case Tegra::DepthFormat::X8Z24_UNORM: | ||
| 89 | return PixelFormat::X8_D24_UNORM; | ||
| 88 | default: | 90 | default: |
| 89 | UNIMPLEMENTED_MSG("Unimplemented format={}", format); | 91 | UNIMPLEMENTED_MSG("Unimplemented format={}", format); |
| 90 | return PixelFormat::S8_UINT_D24_UNORM; | 92 | return PixelFormat::S8_UINT_D24_UNORM; |
| @@ -202,6 +204,7 @@ PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) | |||
| 202 | PixelFormat PixelFormatFromGPUPixelFormat(Service::android::PixelFormat format) { | 204 | PixelFormat PixelFormatFromGPUPixelFormat(Service::android::PixelFormat format) { |
| 203 | switch (format) { | 205 | switch (format) { |
| 204 | case Service::android::PixelFormat::Rgba8888: | 206 | case Service::android::PixelFormat::Rgba8888: |
| 207 | case Service::android::PixelFormat::Rgbx8888: | ||
| 205 | return PixelFormat::A8B8G8R8_UNORM; | 208 | return PixelFormat::A8B8G8R8_UNORM; |
| 206 | case Service::android::PixelFormat::Rgb565: | 209 | case Service::android::PixelFormat::Rgb565: |
| 207 | return PixelFormat::R5G6B5_UNORM; | 210 | return PixelFormat::R5G6B5_UNORM; |
diff --git a/src/video_core/surface.h b/src/video_core/surface.h index 9b9c4d9bc..a5e8e2f62 100644 --- a/src/video_core/surface.h +++ b/src/video_core/surface.h | |||
| @@ -115,6 +115,7 @@ enum class PixelFormat { | |||
| 115 | // Depth formats | 115 | // Depth formats |
| 116 | D32_FLOAT = MaxColorFormat, | 116 | D32_FLOAT = MaxColorFormat, |
| 117 | D16_UNORM, | 117 | D16_UNORM, |
| 118 | X8_D24_UNORM, | ||
| 118 | 119 | ||
| 119 | MaxDepthFormat, | 120 | MaxDepthFormat, |
| 120 | 121 | ||
| @@ -251,6 +252,7 @@ constexpr std::array<u8, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{ | |||
| 251 | 1, // E5B9G9R9_FLOAT | 252 | 1, // E5B9G9R9_FLOAT |
| 252 | 1, // D32_FLOAT | 253 | 1, // D32_FLOAT |
| 253 | 1, // D16_UNORM | 254 | 1, // D16_UNORM |
| 255 | 1, // X8_D24_UNORM | ||
| 254 | 1, // S8_UINT | 256 | 1, // S8_UINT |
| 255 | 1, // D24_UNORM_S8_UINT | 257 | 1, // D24_UNORM_S8_UINT |
| 256 | 1, // S8_UINT_D24_UNORM | 258 | 1, // S8_UINT_D24_UNORM |
| @@ -360,6 +362,7 @@ constexpr std::array<u8, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{ | |||
| 360 | 1, // E5B9G9R9_FLOAT | 362 | 1, // E5B9G9R9_FLOAT |
| 361 | 1, // D32_FLOAT | 363 | 1, // D32_FLOAT |
| 362 | 1, // D16_UNORM | 364 | 1, // D16_UNORM |
| 365 | 1, // X8_D24_UNORM | ||
| 363 | 1, // S8_UINT | 366 | 1, // S8_UINT |
| 364 | 1, // D24_UNORM_S8_UINT | 367 | 1, // D24_UNORM_S8_UINT |
| 365 | 1, // S8_UINT_D24_UNORM | 368 | 1, // S8_UINT_D24_UNORM |
| @@ -469,6 +472,7 @@ constexpr std::array<u8, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{ | |||
| 469 | 32, // E5B9G9R9_FLOAT | 472 | 32, // E5B9G9R9_FLOAT |
| 470 | 32, // D32_FLOAT | 473 | 32, // D32_FLOAT |
| 471 | 16, // D16_UNORM | 474 | 16, // D16_UNORM |
| 475 | 32, // X8_D24_UNORM | ||
| 472 | 8, // S8_UINT | 476 | 8, // S8_UINT |
| 473 | 32, // D24_UNORM_S8_UINT | 477 | 32, // D24_UNORM_S8_UINT |
| 474 | 32, // S8_UINT_D24_UNORM | 478 | 32, // S8_UINT_D24_UNORM |
diff --git a/src/video_core/texture_cache/format_lookup_table.cpp b/src/video_core/texture_cache/format_lookup_table.cpp index 56307d030..8c774f512 100644 --- a/src/video_core/texture_cache/format_lookup_table.cpp +++ b/src/video_core/texture_cache/format_lookup_table.cpp | |||
| @@ -138,10 +138,16 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red, | |||
| 138 | return PixelFormat::E5B9G9R9_FLOAT; | 138 | return PixelFormat::E5B9G9R9_FLOAT; |
| 139 | case Hash(TextureFormat::Z32, FLOAT): | 139 | case Hash(TextureFormat::Z32, FLOAT): |
| 140 | return PixelFormat::D32_FLOAT; | 140 | return PixelFormat::D32_FLOAT; |
| 141 | case Hash(TextureFormat::Z32, FLOAT, UINT, UINT, UINT, LINEAR): | ||
| 142 | return PixelFormat::D32_FLOAT; | ||
| 141 | case Hash(TextureFormat::Z16, UNORM): | 143 | case Hash(TextureFormat::Z16, UNORM): |
| 142 | return PixelFormat::D16_UNORM; | 144 | return PixelFormat::D16_UNORM; |
| 143 | case Hash(TextureFormat::Z16, UNORM, UINT, UINT, UINT, LINEAR): | 145 | case Hash(TextureFormat::Z16, UNORM, UINT, UINT, UINT, LINEAR): |
| 144 | return PixelFormat::D16_UNORM; | 146 | return PixelFormat::D16_UNORM; |
| 147 | case Hash(TextureFormat::X8Z24, UNORM): | ||
| 148 | return PixelFormat::X8_D24_UNORM; | ||
| 149 | case Hash(TextureFormat::X8Z24, UNORM, UINT, UINT, UINT, LINEAR): | ||
| 150 | return PixelFormat::X8_D24_UNORM; | ||
| 145 | case Hash(TextureFormat::Z24S8, UINT, UNORM, UNORM, UNORM, LINEAR): | 151 | case Hash(TextureFormat::Z24S8, UINT, UNORM, UNORM, UNORM, LINEAR): |
| 146 | return PixelFormat::S8_UINT_D24_UNORM; | 152 | return PixelFormat::S8_UINT_D24_UNORM; |
| 147 | case Hash(TextureFormat::Z24S8, UINT, UNORM, UINT, UINT, LINEAR): | 153 | case Hash(TextureFormat::Z24S8, UINT, UNORM, UINT, UINT, LINEAR): |
diff --git a/src/video_core/texture_cache/formatter.h b/src/video_core/texture_cache/formatter.h index 9ee57a076..cabbfcb2d 100644 --- a/src/video_core/texture_cache/formatter.h +++ b/src/video_core/texture_cache/formatter.h | |||
| @@ -211,6 +211,8 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str | |||
| 211 | return "D32_FLOAT"; | 211 | return "D32_FLOAT"; |
| 212 | case PixelFormat::D16_UNORM: | 212 | case PixelFormat::D16_UNORM: |
| 213 | return "D16_UNORM"; | 213 | return "D16_UNORM"; |
| 214 | case PixelFormat::X8_D24_UNORM: | ||
| 215 | return "X8_D24_UNORM"; | ||
| 214 | case PixelFormat::S8_UINT: | 216 | case PixelFormat::S8_UINT: |
| 215 | return "S8_UINT"; | 217 | return "S8_UINT"; |
| 216 | case PixelFormat::D24_UNORM_S8_UINT: | 218 | case PixelFormat::D24_UNORM_S8_UINT: |
diff --git a/src/video_core/texture_cache/image_base.h b/src/video_core/texture_cache/image_base.h index 55d49d017..0587d7b72 100644 --- a/src/video_core/texture_cache/image_base.h +++ b/src/video_core/texture_cache/image_base.h | |||
| @@ -41,7 +41,7 @@ enum class ImageFlagBits : u32 { | |||
| 41 | IsRescalable = 1 << 15, | 41 | IsRescalable = 1 << 15, |
| 42 | 42 | ||
| 43 | AsynchronousDecode = 1 << 16, | 43 | AsynchronousDecode = 1 << 16, |
| 44 | IsDecoding = 1 << 17, ///< Is currently being decoded asynchornously. | 44 | IsDecoding = 1 << 17, ///< Is currently being decoded asynchronously. |
| 45 | }; | 45 | }; |
| 46 | DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits) | 46 | DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits) |
| 47 | 47 | ||
diff --git a/src/video_core/texture_cache/image_view_base.cpp b/src/video_core/texture_cache/image_view_base.cpp index 0c5f4450d..18b9250f9 100644 --- a/src/video_core/texture_cache/image_view_base.cpp +++ b/src/video_core/texture_cache/image_view_base.cpp | |||
| @@ -85,6 +85,7 @@ bool ImageViewBase::SupportsAnisotropy() const noexcept { | |||
| 85 | // Depth formats | 85 | // Depth formats |
| 86 | case PixelFormat::D32_FLOAT: | 86 | case PixelFormat::D32_FLOAT: |
| 87 | case PixelFormat::D16_UNORM: | 87 | case PixelFormat::D16_UNORM: |
| 88 | case PixelFormat::X8_D24_UNORM: | ||
| 88 | // Stencil formats | 89 | // Stencil formats |
| 89 | case PixelFormat::S8_UINT: | 90 | case PixelFormat::S8_UINT: |
| 90 | // DepthStencil formats | 91 | // DepthStencil formats |
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 1bdb0def5..d575c57ca 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h | |||
| @@ -1016,6 +1016,7 @@ void TextureCache<P>::RefreshContents(Image& image, ImageId image_id) { | |||
| 1016 | 1016 | ||
| 1017 | if (image.info.num_samples > 1 && !runtime.CanUploadMSAA()) { | 1017 | if (image.info.num_samples > 1 && !runtime.CanUploadMSAA()) { |
| 1018 | LOG_WARNING(HW_GPU, "MSAA image uploads are not implemented"); | 1018 | LOG_WARNING(HW_GPU, "MSAA image uploads are not implemented"); |
| 1019 | runtime.TransitionImageLayout(image); | ||
| 1019 | return; | 1020 | return; |
| 1020 | } | 1021 | } |
| 1021 | if (True(image.flags & ImageFlagBits::AsynchronousDecode)) { | 1022 | if (True(image.flags & ImageFlagBits::AsynchronousDecode)) { |
diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp index a83f5d41c..8151cabf0 100644 --- a/src/video_core/texture_cache/util.cpp +++ b/src/video_core/texture_cache/util.cpp | |||
| @@ -68,6 +68,7 @@ struct LevelInfo { | |||
| 68 | Extent2D tile_size; | 68 | Extent2D tile_size; |
| 69 | u32 bpp_log2; | 69 | u32 bpp_log2; |
| 70 | u32 tile_width_spacing; | 70 | u32 tile_width_spacing; |
| 71 | u32 num_levels; | ||
| 71 | }; | 72 | }; |
| 72 | 73 | ||
| 73 | [[nodiscard]] constexpr u32 AdjustTileSize(u32 shift, u32 unit_factor, u32 dimension) { | 74 | [[nodiscard]] constexpr u32 AdjustTileSize(u32 shift, u32 unit_factor, u32 dimension) { |
| @@ -118,11 +119,11 @@ template <u32 GOB_EXTENT> | |||
| 118 | } | 119 | } |
| 119 | 120 | ||
| 120 | [[nodiscard]] constexpr Extent3D AdjustMipBlockSize(Extent3D num_tiles, Extent3D block_size, | 121 | [[nodiscard]] constexpr Extent3D AdjustMipBlockSize(Extent3D num_tiles, Extent3D block_size, |
| 121 | u32 level) { | 122 | u32 level, u32 num_levels) { |
| 122 | return { | 123 | return { |
| 123 | .width = AdjustMipBlockSize<GOB_SIZE_X>(num_tiles.width, block_size.width, level), | 124 | .width = AdjustMipBlockSize<GOB_SIZE_X>(num_tiles.width, block_size.width, level), |
| 124 | .height = AdjustMipBlockSize<GOB_SIZE_Y>(num_tiles.height, block_size.height, level), | 125 | .height = AdjustMipBlockSize<GOB_SIZE_Y>(num_tiles.height, block_size.height, level), |
| 125 | .depth = level == 0 | 126 | .depth = level == 0 && num_levels == 1 |
| 126 | ? block_size.depth | 127 | ? block_size.depth |
| 127 | : AdjustMipBlockSize<GOB_SIZE_Z>(num_tiles.depth, block_size.depth, level), | 128 | : AdjustMipBlockSize<GOB_SIZE_Z>(num_tiles.depth, block_size.depth, level), |
| 128 | }; | 129 | }; |
| @@ -166,13 +167,6 @@ template <u32 GOB_EXTENT> | |||
| 166 | } | 167 | } |
| 167 | 168 | ||
| 168 | [[nodiscard]] constexpr Extent3D TileShift(const LevelInfo& info, u32 level) { | 169 | [[nodiscard]] constexpr Extent3D TileShift(const LevelInfo& info, u32 level) { |
| 169 | if (level == 0) { | ||
| 170 | return Extent3D{ | ||
| 171 | .width = info.block.width, | ||
| 172 | .height = info.block.height, | ||
| 173 | .depth = info.block.depth, | ||
| 174 | }; | ||
| 175 | } | ||
| 176 | const Extent3D blocks = NumLevelBlocks(info, level); | 170 | const Extent3D blocks = NumLevelBlocks(info, level); |
| 177 | return Extent3D{ | 171 | return Extent3D{ |
| 178 | .width = AdjustTileSize(info.block.width, GOB_SIZE_X, blocks.width), | 172 | .width = AdjustTileSize(info.block.width, GOB_SIZE_X, blocks.width), |
| @@ -257,7 +251,7 @@ template <u32 GOB_EXTENT> | |||
| 257 | } | 251 | } |
| 258 | 252 | ||
| 259 | [[nodiscard]] constexpr LevelInfo MakeLevelInfo(PixelFormat format, Extent3D size, Extent3D block, | 253 | [[nodiscard]] constexpr LevelInfo MakeLevelInfo(PixelFormat format, Extent3D size, Extent3D block, |
| 260 | u32 tile_width_spacing) { | 254 | u32 tile_width_spacing, u32 num_levels) { |
| 261 | const u32 bytes_per_block = BytesPerBlock(format); | 255 | const u32 bytes_per_block = BytesPerBlock(format); |
| 262 | return { | 256 | return { |
| 263 | .size = | 257 | .size = |
| @@ -270,16 +264,18 @@ template <u32 GOB_EXTENT> | |||
| 270 | .tile_size = DefaultBlockSize(format), | 264 | .tile_size = DefaultBlockSize(format), |
| 271 | .bpp_log2 = BytesPerBlockLog2(bytes_per_block), | 265 | .bpp_log2 = BytesPerBlockLog2(bytes_per_block), |
| 272 | .tile_width_spacing = tile_width_spacing, | 266 | .tile_width_spacing = tile_width_spacing, |
| 267 | .num_levels = num_levels, | ||
| 273 | }; | 268 | }; |
| 274 | } | 269 | } |
| 275 | 270 | ||
| 276 | [[nodiscard]] constexpr LevelInfo MakeLevelInfo(const ImageInfo& info) { | 271 | [[nodiscard]] constexpr LevelInfo MakeLevelInfo(const ImageInfo& info) { |
| 277 | return MakeLevelInfo(info.format, info.size, info.block, info.tile_width_spacing); | 272 | return MakeLevelInfo(info.format, info.size, info.block, info.tile_width_spacing, |
| 273 | info.resources.levels); | ||
| 278 | } | 274 | } |
| 279 | 275 | ||
| 280 | [[nodiscard]] constexpr u32 CalculateLevelOffset(PixelFormat format, Extent3D size, Extent3D block, | 276 | [[nodiscard]] constexpr u32 CalculateLevelOffset(PixelFormat format, Extent3D size, Extent3D block, |
| 281 | u32 tile_width_spacing, u32 level) { | 277 | u32 tile_width_spacing, u32 level) { |
| 282 | const LevelInfo info = MakeLevelInfo(format, size, block, tile_width_spacing); | 278 | const LevelInfo info = MakeLevelInfo(format, size, block, tile_width_spacing, level); |
| 283 | u32 offset = 0; | 279 | u32 offset = 0; |
| 284 | for (u32 current_level = 0; current_level < level; ++current_level) { | 280 | for (u32 current_level = 0; current_level < level; ++current_level) { |
| 285 | offset += CalculateLevelSize(info, current_level); | 281 | offset += CalculateLevelSize(info, current_level); |
| @@ -466,7 +462,7 @@ template <u32 GOB_EXTENT> | |||
| 466 | }; | 462 | }; |
| 467 | const u32 bpp_log2 = BytesPerBlockLog2(info.format); | 463 | const u32 bpp_log2 = BytesPerBlockLog2(info.format); |
| 468 | const u32 alignment = StrideAlignment(num_tiles, info.block, bpp_log2, info.tile_width_spacing); | 464 | const u32 alignment = StrideAlignment(num_tiles, info.block, bpp_log2, info.tile_width_spacing); |
| 469 | const Extent3D mip_block = AdjustMipBlockSize(num_tiles, info.block, 0); | 465 | const Extent3D mip_block = AdjustMipBlockSize(num_tiles, info.block, 0, info.resources.levels); |
| 470 | return Extent3D{ | 466 | return Extent3D{ |
| 471 | .width = Common::AlignUpLog2(num_tiles.width, alignment), | 467 | .width = Common::AlignUpLog2(num_tiles.width, alignment), |
| 472 | .height = Common::AlignUpLog2(num_tiles.height, GOB_SIZE_Y_SHIFT + mip_block.height), | 468 | .height = Common::AlignUpLog2(num_tiles.height, GOB_SIZE_Y_SHIFT + mip_block.height), |
| @@ -533,7 +529,8 @@ void SwizzleBlockLinearImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr | |||
| 533 | UNIMPLEMENTED_IF(copy.image_extent != level_size); | 529 | UNIMPLEMENTED_IF(copy.image_extent != level_size); |
| 534 | 530 | ||
| 535 | const Extent3D num_tiles = AdjustTileSize(level_size, tile_size); | 531 | const Extent3D num_tiles = AdjustTileSize(level_size, tile_size); |
| 536 | const Extent3D block = AdjustMipBlockSize(num_tiles, level_info.block, level); | 532 | const Extent3D block = |
| 533 | AdjustMipBlockSize(num_tiles, level_info.block, level, level_info.num_levels); | ||
| 537 | 534 | ||
| 538 | size_t host_offset = copy.buffer_offset; | 535 | size_t host_offset = copy.buffer_offset; |
| 539 | 536 | ||
| @@ -698,7 +695,7 @@ u32 CalculateLevelStrideAlignment(const ImageInfo& info, u32 level) { | |||
| 698 | const Extent2D tile_size = DefaultBlockSize(info.format); | 695 | const Extent2D tile_size = DefaultBlockSize(info.format); |
| 699 | const Extent3D level_size = AdjustMipSize(info.size, level); | 696 | const Extent3D level_size = AdjustMipSize(info.size, level); |
| 700 | const Extent3D num_tiles = AdjustTileSize(level_size, tile_size); | 697 | const Extent3D num_tiles = AdjustTileSize(level_size, tile_size); |
| 701 | const Extent3D block = AdjustMipBlockSize(num_tiles, info.block, level); | 698 | const Extent3D block = AdjustMipBlockSize(num_tiles, info.block, level, info.resources.levels); |
| 702 | const u32 bpp_log2 = BytesPerBlockLog2(info.format); | 699 | const u32 bpp_log2 = BytesPerBlockLog2(info.format); |
| 703 | return StrideAlignment(num_tiles, block, bpp_log2, info.tile_width_spacing); | 700 | return StrideAlignment(num_tiles, block, bpp_log2, info.tile_width_spacing); |
| 704 | } | 701 | } |
| @@ -887,7 +884,8 @@ boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(Tegra::Memory | |||
| 887 | .image_extent = level_size, | 884 | .image_extent = level_size, |
| 888 | }; | 885 | }; |
| 889 | const Extent3D num_tiles = AdjustTileSize(level_size, tile_size); | 886 | const Extent3D num_tiles = AdjustTileSize(level_size, tile_size); |
| 890 | const Extent3D block = AdjustMipBlockSize(num_tiles, level_info.block, level); | 887 | const Extent3D block = |
| 888 | AdjustMipBlockSize(num_tiles, level_info.block, level, level_info.num_levels); | ||
| 891 | const u32 stride_alignment = StrideAlignment(num_tiles, info.block, gob, bpp_log2); | 889 | const u32 stride_alignment = StrideAlignment(num_tiles, info.block, gob, bpp_log2); |
| 892 | size_t guest_layer_offset = 0; | 890 | size_t guest_layer_offset = 0; |
| 893 | 891 | ||
| @@ -1041,7 +1039,7 @@ Extent3D MipBlockSize(const ImageInfo& info, u32 level) { | |||
| 1041 | const Extent2D tile_size = DefaultBlockSize(info.format); | 1039 | const Extent2D tile_size = DefaultBlockSize(info.format); |
| 1042 | const Extent3D level_size = AdjustMipSize(info.size, level); | 1040 | const Extent3D level_size = AdjustMipSize(info.size, level); |
| 1043 | const Extent3D num_tiles = AdjustTileSize(level_size, tile_size); | 1041 | const Extent3D num_tiles = AdjustTileSize(level_size, tile_size); |
| 1044 | return AdjustMipBlockSize(num_tiles, level_info.block, level); | 1042 | return AdjustMipBlockSize(num_tiles, level_info.block, level, level_info.num_levels); |
| 1045 | } | 1043 | } |
| 1046 | 1044 | ||
| 1047 | boost::container::small_vector<SwizzleParameters, 16> FullUploadSwizzles(const ImageInfo& info) { | 1045 | boost::container::small_vector<SwizzleParameters, 16> FullUploadSwizzles(const ImageInfo& info) { |
| @@ -1063,7 +1061,8 @@ boost::container::small_vector<SwizzleParameters, 16> FullUploadSwizzles(const I | |||
| 1063 | for (s32 level = 0; level < num_levels; ++level) { | 1061 | for (s32 level = 0; level < num_levels; ++level) { |
| 1064 | const Extent3D level_size = AdjustMipSize(size, level); | 1062 | const Extent3D level_size = AdjustMipSize(size, level); |
| 1065 | const Extent3D num_tiles = AdjustTileSize(level_size, tile_size); | 1063 | const Extent3D num_tiles = AdjustTileSize(level_size, tile_size); |
| 1066 | const Extent3D block = AdjustMipBlockSize(num_tiles, level_info.block, level); | 1064 | const Extent3D block = |
| 1065 | AdjustMipBlockSize(num_tiles, level_info.block, level, level_info.num_levels); | ||
| 1067 | params[level] = SwizzleParameters{ | 1066 | params[level] = SwizzleParameters{ |
| 1068 | .num_tiles = num_tiles, | 1067 | .num_tiles = num_tiles, |
| 1069 | .block = block, | 1068 | .block = block, |
| @@ -1195,7 +1194,7 @@ std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, const | |||
| 1195 | return std::nullopt; | 1194 | return std::nullopt; |
| 1196 | } | 1195 | } |
| 1197 | } else { | 1196 | } else { |
| 1198 | // Format comaptibility is not relaxed, ensure we are creating a view on a compatible format | 1197 | // Format compatibility is not relaxed, ensure we are creating a view on a compatible format |
| 1199 | if (!IsViewCompatible(existing.format, candidate.format, broken_views, native_bgr)) { | 1198 | if (!IsViewCompatible(existing.format, candidate.format, broken_views, native_bgr)) { |
| 1200 | return std::nullopt; | 1199 | return std::nullopt; |
| 1201 | } | 1200 | } |
| @@ -1292,11 +1291,11 @@ u32 MapSizeBytes(const ImageBase& image) { | |||
| 1292 | } | 1291 | } |
| 1293 | } | 1292 | } |
| 1294 | 1293 | ||
| 1295 | static_assert(CalculateLevelSize(LevelInfo{{1920, 1080, 1}, {0, 2, 0}, {1, 1}, 2, 0}, 0) == | 1294 | static_assert(CalculateLevelSize(LevelInfo{{1920, 1080, 1}, {0, 2, 0}, {1, 1}, 2, 0, 1}, 0) == |
| 1296 | 0x7f8000); | 1295 | 0x7f8000); |
| 1297 | static_assert(CalculateLevelSize(LevelInfo{{32, 32, 1}, {0, 0, 4}, {1, 1}, 4, 0}, 0) == 0x40000); | 1296 | static_assert(CalculateLevelSize(LevelInfo{{32, 32, 1}, {0, 0, 4}, {1, 1}, 4, 0, 1}, 0) == 0x4000); |
| 1298 | 1297 | ||
| 1299 | static_assert(CalculateLevelSize(LevelInfo{{128, 8, 1}, {0, 4, 0}, {1, 1}, 4, 0}, 0) == 0x40000); | 1298 | static_assert(CalculateLevelSize(LevelInfo{{128, 8, 1}, {0, 4, 0}, {1, 1}, 4, 0, 1}, 0) == 0x4000); |
| 1300 | 1299 | ||
| 1301 | static_assert(CalculateLevelOffset(PixelFormat::R8_SINT, {1920, 1080, 1}, {0, 2, 0}, 0, 7) == | 1300 | static_assert(CalculateLevelOffset(PixelFormat::R8_SINT, {1920, 1080, 1}, {0, 2, 0}, 0, 7) == |
| 1302 | 0x2afc00); | 1301 | 0x2afc00); |
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 3960b135a..876cec2e8 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp | |||
| @@ -84,9 +84,12 @@ constexpr std::array VK_FORMAT_A4B4G4R4_UNORM_PACK16{ | |||
| 84 | } // namespace Alternatives | 84 | } // namespace Alternatives |
| 85 | 85 | ||
| 86 | enum class NvidiaArchitecture { | 86 | enum class NvidiaArchitecture { |
| 87 | AmpereOrNewer, | 87 | KeplerOrOlder, |
| 88 | Maxwell, | ||
| 89 | Pascal, | ||
| 90 | Volta, | ||
| 88 | Turing, | 91 | Turing, |
| 89 | VoltaOrOlder, | 92 | AmpereOrNewer, |
| 90 | }; | 93 | }; |
| 91 | 94 | ||
| 92 | template <typename T> | 95 | template <typename T> |
| @@ -200,6 +203,7 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::Physica | |||
| 200 | VK_FORMAT_BC7_UNORM_BLOCK, | 203 | VK_FORMAT_BC7_UNORM_BLOCK, |
| 201 | VK_FORMAT_D16_UNORM, | 204 | VK_FORMAT_D16_UNORM, |
| 202 | VK_FORMAT_D16_UNORM_S8_UINT, | 205 | VK_FORMAT_D16_UNORM_S8_UINT, |
| 206 | VK_FORMAT_X8_D24_UNORM_PACK32, | ||
| 203 | VK_FORMAT_D24_UNORM_S8_UINT, | 207 | VK_FORMAT_D24_UNORM_S8_UINT, |
| 204 | VK_FORMAT_D32_SFLOAT, | 208 | VK_FORMAT_D32_SFLOAT, |
| 205 | VK_FORMAT_D32_SFLOAT_S8_UINT, | 209 | VK_FORMAT_D32_SFLOAT_S8_UINT, |
| @@ -321,13 +325,38 @@ NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical, | |||
| 321 | physical.GetProperties2(physical_properties); | 325 | physical.GetProperties2(physical_properties); |
| 322 | if (shading_rate_props.primitiveFragmentShadingRateWithMultipleViewports) { | 326 | if (shading_rate_props.primitiveFragmentShadingRateWithMultipleViewports) { |
| 323 | // Only Ampere and newer support this feature | 327 | // Only Ampere and newer support this feature |
| 328 | // TODO: Find a way to differentiate Ampere and Ada | ||
| 324 | return NvidiaArchitecture::AmpereOrNewer; | 329 | return NvidiaArchitecture::AmpereOrNewer; |
| 325 | } | 330 | } |
| 326 | } | ||
| 327 | if (exts.contains(VK_NV_SHADING_RATE_IMAGE_EXTENSION_NAME)) { | ||
| 328 | return NvidiaArchitecture::Turing; | 331 | return NvidiaArchitecture::Turing; |
| 329 | } | 332 | } |
| 330 | return NvidiaArchitecture::VoltaOrOlder; | 333 | |
| 334 | if (exts.contains(VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME)) { | ||
| 335 | VkPhysicalDeviceBlendOperationAdvancedPropertiesEXT advanced_blending_props{}; | ||
| 336 | advanced_blending_props.sType = | ||
| 337 | VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_PROPERTIES_EXT; | ||
| 338 | VkPhysicalDeviceProperties2 physical_properties{}; | ||
| 339 | physical_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; | ||
| 340 | physical_properties.pNext = &advanced_blending_props; | ||
| 341 | physical.GetProperties2(physical_properties); | ||
| 342 | if (advanced_blending_props.advancedBlendMaxColorAttachments == 1) { | ||
| 343 | return NvidiaArchitecture::Maxwell; | ||
| 344 | } | ||
| 345 | |||
| 346 | if (exts.contains(VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME)) { | ||
| 347 | VkPhysicalDeviceConservativeRasterizationPropertiesEXT conservative_raster_props{}; | ||
| 348 | conservative_raster_props.sType = | ||
| 349 | VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CONSERVATIVE_RASTERIZATION_PROPERTIES_EXT; | ||
| 350 | physical_properties.pNext = &conservative_raster_props; | ||
| 351 | physical.GetProperties2(physical_properties); | ||
| 352 | if (conservative_raster_props.degenerateLinesRasterized) { | ||
| 353 | return NvidiaArchitecture::Volta; | ||
| 354 | } | ||
| 355 | return NvidiaArchitecture::Pascal; | ||
| 356 | } | ||
| 357 | } | ||
| 358 | |||
| 359 | return NvidiaArchitecture::KeplerOrOlder; | ||
| 331 | } | 360 | } |
| 332 | 361 | ||
| 333 | std::vector<const char*> ExtensionListForVulkan( | 362 | std::vector<const char*> ExtensionListForVulkan( |
| @@ -504,19 +533,14 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR | |||
| 504 | if (is_nvidia) { | 533 | if (is_nvidia) { |
| 505 | const u32 nv_major_version = (properties.properties.driverVersion >> 22) & 0x3ff; | 534 | const u32 nv_major_version = (properties.properties.driverVersion >> 22) & 0x3ff; |
| 506 | const auto arch = GetNvidiaArchitecture(physical, supported_extensions); | 535 | const auto arch = GetNvidiaArchitecture(physical, supported_extensions); |
| 507 | switch (arch) { | 536 | if (arch >= NvidiaArchitecture::AmpereOrNewer) { |
| 508 | case NvidiaArchitecture::AmpereOrNewer: | ||
| 509 | LOG_WARNING(Render_Vulkan, "Ampere and newer have broken float16 math"); | 537 | LOG_WARNING(Render_Vulkan, "Ampere and newer have broken float16 math"); |
| 510 | features.shader_float16_int8.shaderFloat16 = false; | 538 | features.shader_float16_int8.shaderFloat16 = false; |
| 511 | break; | 539 | } else if (arch <= NvidiaArchitecture::Volta) { |
| 512 | case NvidiaArchitecture::Turing: | ||
| 513 | break; | ||
| 514 | case NvidiaArchitecture::VoltaOrOlder: | ||
| 515 | if (nv_major_version < 527) { | 540 | if (nv_major_version < 527) { |
| 516 | LOG_WARNING(Render_Vulkan, "Volta and older have broken VK_KHR_push_descriptor"); | 541 | LOG_WARNING(Render_Vulkan, "Volta and older have broken VK_KHR_push_descriptor"); |
| 517 | RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME); | 542 | RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME); |
| 518 | } | 543 | } |
| 519 | break; | ||
| 520 | } | 544 | } |
| 521 | if (nv_major_version >= 510) { | 545 | if (nv_major_version >= 510) { |
| 522 | LOG_WARNING(Render_Vulkan, "NVIDIA Drivers >= 510 do not support MSAA image blits"); | 546 | LOG_WARNING(Render_Vulkan, "NVIDIA Drivers >= 510 do not support MSAA image blits"); |
| @@ -661,7 +685,15 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR | |||
| 661 | "ANV drivers 22.3.0 to 23.1.0 have broken VK_KHR_push_descriptor"); | 685 | "ANV drivers 22.3.0 to 23.1.0 have broken VK_KHR_push_descriptor"); |
| 662 | RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME); | 686 | RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME); |
| 663 | } | 687 | } |
| 688 | } else if (extensions.push_descriptor && is_nvidia) { | ||
| 689 | const auto arch = GetNvidiaArchitecture(physical, supported_extensions); | ||
| 690 | if (arch <= NvidiaArchitecture::Pascal) { | ||
| 691 | LOG_WARNING(Render_Vulkan, | ||
| 692 | "Pascal and older architectures have broken VK_KHR_push_descriptor"); | ||
| 693 | RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME); | ||
| 694 | } | ||
| 664 | } | 695 | } |
| 696 | |||
| 665 | if (is_mvk) { | 697 | if (is_mvk) { |
| 666 | LOG_WARNING(Render_Vulkan, | 698 | LOG_WARNING(Render_Vulkan, |
| 667 | "MVK driver breaks when using more than 16 vertex attributes/bindings"); | 699 | "MVK driver breaks when using more than 16 vertex attributes/bindings"); |
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 9be612392..282a2925d 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h | |||
| @@ -314,7 +314,7 @@ public: | |||
| 314 | return GetDriverID() != VK_DRIVER_ID_QUALCOMM_PROPRIETARY; | 314 | return GetDriverID() != VK_DRIVER_ID_QUALCOMM_PROPRIETARY; |
| 315 | } | 315 | } |
| 316 | 316 | ||
| 317 | /// Returns true if the device suppors float64 natively. | 317 | /// Returns true if the device supports float64 natively. |
| 318 | bool IsFloat64Supported() const { | 318 | bool IsFloat64Supported() const { |
| 319 | return features.features.shaderFloat64; | 319 | return features.features.shaderFloat64; |
| 320 | } | 320 | } |
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp index 3ef381a38..8dd1667f3 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "common/alignment.h" | 9 | #include "common/alignment.h" |
| 10 | #include "common/assert.h" | 10 | #include "common/assert.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "common/literals.h" | ||
| 12 | #include "common/logging/log.h" | 13 | #include "common/logging/log.h" |
| 13 | #include "common/polyfill_ranges.h" | 14 | #include "common/polyfill_ranges.h" |
| 14 | #include "video_core/vulkan_common/vma.h" | 15 | #include "video_core/vulkan_common/vma.h" |
| @@ -65,12 +66,12 @@ struct Range { | |||
| 65 | switch (usage) { | 66 | switch (usage) { |
| 66 | case MemoryUsage::Upload: | 67 | case MemoryUsage::Upload: |
| 67 | case MemoryUsage::Stream: | 68 | case MemoryUsage::Stream: |
| 68 | return VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; | 69 | return VMA_ALLOCATION_CREATE_MAPPED_BIT | |
| 70 | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; | ||
| 69 | case MemoryUsage::Download: | 71 | case MemoryUsage::Download: |
| 70 | return VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT; | 72 | return VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT; |
| 71 | case MemoryUsage::DeviceLocal: | 73 | case MemoryUsage::DeviceLocal: |
| 72 | return VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | | 74 | return {}; |
| 73 | VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT; | ||
| 74 | } | 75 | } |
| 75 | return {}; | 76 | return {}; |
| 76 | } | 77 | } |
| @@ -212,7 +213,20 @@ MemoryAllocator::MemoryAllocator(const Device& device_) | |||
| 212 | : device{device_}, allocator{device.GetAllocator()}, | 213 | : device{device_}, allocator{device.GetAllocator()}, |
| 213 | properties{device_.GetPhysical().GetMemoryProperties().memoryProperties}, | 214 | properties{device_.GetPhysical().GetMemoryProperties().memoryProperties}, |
| 214 | buffer_image_granularity{ | 215 | buffer_image_granularity{ |
| 215 | device_.GetPhysical().GetProperties().limits.bufferImageGranularity} {} | 216 | device_.GetPhysical().GetProperties().limits.bufferImageGranularity} { |
| 217 | // GPUs not supporting rebar may only have a region with less than 256MB host visible/device | ||
| 218 | // local memory. In that case, opening 2 RenderDoc captures side-by-side is not possible due to | ||
| 219 | // the heap running out of memory. With RenderDoc attached and only a small host/device region, | ||
| 220 | // only allow the stream buffer in this memory heap. | ||
| 221 | if (device.HasDebuggingToolAttached()) { | ||
| 222 | using namespace Common::Literals; | ||
| 223 | ForEachDeviceLocalHostVisibleHeap(device, [this](size_t index, VkMemoryHeap& heap) { | ||
| 224 | if (heap.size <= 256_MiB) { | ||
| 225 | valid_memory_types &= ~(1u << index); | ||
| 226 | } | ||
| 227 | }); | ||
| 228 | } | ||
| 229 | } | ||
| 216 | 230 | ||
| 217 | MemoryAllocator::~MemoryAllocator() = default; | 231 | MemoryAllocator::~MemoryAllocator() = default; |
| 218 | 232 | ||
| @@ -239,12 +253,11 @@ vk::Image MemoryAllocator::CreateImage(const VkImageCreateInfo& ci) const { | |||
| 239 | 253 | ||
| 240 | vk::Buffer MemoryAllocator::CreateBuffer(const VkBufferCreateInfo& ci, MemoryUsage usage) const { | 254 | vk::Buffer MemoryAllocator::CreateBuffer(const VkBufferCreateInfo& ci, MemoryUsage usage) const { |
| 241 | const VmaAllocationCreateInfo alloc_ci = { | 255 | const VmaAllocationCreateInfo alloc_ci = { |
| 242 | .flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT | | 256 | .flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | MemoryUsageVmaFlags(usage), |
| 243 | MemoryUsageVmaFlags(usage), | ||
| 244 | .usage = MemoryUsageVma(usage), | 257 | .usage = MemoryUsageVma(usage), |
| 245 | .requiredFlags = 0, | 258 | .requiredFlags = 0, |
| 246 | .preferredFlags = MemoryUsagePreferedVmaFlags(usage), | 259 | .preferredFlags = MemoryUsagePreferedVmaFlags(usage), |
| 247 | .memoryTypeBits = 0, | 260 | .memoryTypeBits = usage == MemoryUsage::Stream ? 0u : valid_memory_types, |
| 248 | .pool = VK_NULL_HANDLE, | 261 | .pool = VK_NULL_HANDLE, |
| 249 | .pUserData = nullptr, | 262 | .pUserData = nullptr, |
| 250 | .priority = 0.f, | 263 | .priority = 0.f, |
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.h b/src/video_core/vulkan_common/vulkan_memory_allocator.h index f449bc8d0..38a182bcb 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.h +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <span> | 7 | #include <span> |
| 8 | #include <vector> | 8 | #include <vector> |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "video_core/vulkan_common/vulkan_device.h" | ||
| 10 | #include "video_core/vulkan_common/vulkan_wrapper.h" | 11 | #include "video_core/vulkan_common/vulkan_wrapper.h" |
| 11 | 12 | ||
| 12 | VK_DEFINE_HANDLE(VmaAllocator) | 13 | VK_DEFINE_HANDLE(VmaAllocator) |
| @@ -26,6 +27,18 @@ enum class MemoryUsage { | |||
| 26 | Stream, ///< Requests device local host visible buffer, falling back host memory. | 27 | Stream, ///< Requests device local host visible buffer, falling back host memory. |
| 27 | }; | 28 | }; |
| 28 | 29 | ||
| 30 | template <typename F> | ||
| 31 | void ForEachDeviceLocalHostVisibleHeap(const Device& device, F&& f) { | ||
| 32 | auto memory_props = device.GetPhysical().GetMemoryProperties().memoryProperties; | ||
| 33 | for (size_t i = 0; i < memory_props.memoryTypeCount; i++) { | ||
| 34 | auto& memory_type = memory_props.memoryTypes[i]; | ||
| 35 | if ((memory_type.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) && | ||
| 36 | (memory_type.propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)) { | ||
| 37 | f(memory_type.heapIndex, memory_props.memoryHeaps[memory_type.heapIndex]); | ||
| 38 | } | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 29 | /// Ownership handle of a memory commitment. | 42 | /// Ownership handle of a memory commitment. |
| 30 | /// Points to a subregion of a memory allocation. | 43 | /// Points to a subregion of a memory allocation. |
| 31 | class MemoryCommit { | 44 | class MemoryCommit { |
| @@ -124,6 +137,7 @@ private: | |||
| 124 | std::vector<std::unique_ptr<MemoryAllocation>> allocations; ///< Current allocations. | 137 | std::vector<std::unique_ptr<MemoryAllocation>> allocations; ///< Current allocations. |
| 125 | VkDeviceSize buffer_image_granularity; // The granularity for adjacent offsets between buffers | 138 | VkDeviceSize buffer_image_granularity; // The granularity for adjacent offsets between buffers |
| 126 | // and optimal images | 139 | // and optimal images |
| 140 | u32 valid_memory_types{~0u}; | ||
| 127 | }; | 141 | }; |
| 128 | 142 | ||
| 129 | } // namespace Vulkan | 143 | } // namespace Vulkan |
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index 1e3c0fa64..0487cd3b6 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h | |||
| @@ -117,6 +117,9 @@ public: | |||
| 117 | virtual ~Exception() = default; | 117 | virtual ~Exception() = default; |
| 118 | 118 | ||
| 119 | const char* what() const noexcept override; | 119 | const char* what() const noexcept override; |
| 120 | VkResult GetResult() const noexcept { | ||
| 121 | return result; | ||
| 122 | } | ||
| 120 | 123 | ||
| 121 | private: | 124 | private: |
| 122 | VkResult result; | 125 | VkResult result; |
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 8f86a1553..9ebece907 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -195,6 +195,8 @@ add_executable(yuzu | |||
| 195 | multiplayer/state.cpp | 195 | multiplayer/state.cpp |
| 196 | multiplayer/state.h | 196 | multiplayer/state.h |
| 197 | multiplayer/validation.h | 197 | multiplayer/validation.h |
| 198 | play_time_manager.cpp | ||
| 199 | play_time_manager.h | ||
| 198 | precompiled_headers.h | 200 | precompiled_headers.h |
| 199 | qt_common.cpp | 201 | qt_common.cpp |
| 200 | qt_common.h | 202 | qt_common.h |
diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp index d15559518..ca0e14fad 100644 --- a/src/yuzu/applets/qt_controller.cpp +++ b/src/yuzu/applets/qt_controller.cpp | |||
| @@ -23,6 +23,7 @@ | |||
| 23 | #include "yuzu/configuration/configure_vibration.h" | 23 | #include "yuzu/configuration/configure_vibration.h" |
| 24 | #include "yuzu/configuration/input_profiles.h" | 24 | #include "yuzu/configuration/input_profiles.h" |
| 25 | #include "yuzu/main.h" | 25 | #include "yuzu/main.h" |
| 26 | #include "yuzu/util/controller_navigation.h" | ||
| 26 | 27 | ||
| 27 | namespace { | 28 | namespace { |
| 28 | 29 | ||
| @@ -132,6 +133,8 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( | |||
| 132 | ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected, | 133 | ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected, |
| 133 | }; | 134 | }; |
| 134 | 135 | ||
| 136 | ui->labelError->setVisible(false); | ||
| 137 | |||
| 135 | // Setup/load everything prior to setting up connections. | 138 | // Setup/load everything prior to setting up connections. |
| 136 | // This avoids unintentionally changing the states of elements while loading them in. | 139 | // This avoids unintentionally changing the states of elements while loading them in. |
| 137 | SetSupportedControllers(); | 140 | SetSupportedControllers(); |
| @@ -143,6 +146,8 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( | |||
| 143 | 146 | ||
| 144 | LoadConfiguration(); | 147 | LoadConfiguration(); |
| 145 | 148 | ||
| 149 | controller_navigation = new ControllerNavigation(system.HIDCore(), this); | ||
| 150 | |||
| 146 | for (std::size_t i = 0; i < NUM_PLAYERS; ++i) { | 151 | for (std::size_t i = 0; i < NUM_PLAYERS; ++i) { |
| 147 | SetExplainText(i); | 152 | SetExplainText(i); |
| 148 | UpdateControllerIcon(i); | 153 | UpdateControllerIcon(i); |
| @@ -151,6 +156,8 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( | |||
| 151 | 156 | ||
| 152 | connect(player_groupboxes[i], &QGroupBox::toggled, [this, i](bool checked) { | 157 | connect(player_groupboxes[i], &QGroupBox::toggled, [this, i](bool checked) { |
| 153 | if (checked) { | 158 | if (checked) { |
| 159 | // Hide eventual error message about number of controllers | ||
| 160 | ui->labelError->setVisible(false); | ||
| 154 | for (std::size_t index = 0; index <= i; ++index) { | 161 | for (std::size_t index = 0; index <= i; ++index) { |
| 155 | connected_controller_checkboxes[index]->setChecked(checked); | 162 | connected_controller_checkboxes[index]->setChecked(checked); |
| 156 | } | 163 | } |
| @@ -199,6 +206,12 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( | |||
| 199 | connect(ui->buttonBox, &QDialogButtonBox::accepted, this, | 206 | connect(ui->buttonBox, &QDialogButtonBox::accepted, this, |
| 200 | &QtControllerSelectorDialog::ApplyConfiguration); | 207 | &QtControllerSelectorDialog::ApplyConfiguration); |
| 201 | 208 | ||
| 209 | connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent, | ||
| 210 | [this](Qt::Key key) { | ||
| 211 | QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier); | ||
| 212 | QCoreApplication::postEvent(this, event); | ||
| 213 | }); | ||
| 214 | |||
| 202 | // Enhancement: Check if the parameters have already been met before disconnecting controllers. | 215 | // Enhancement: Check if the parameters have already been met before disconnecting controllers. |
| 203 | // If all the parameters are met AND only allows a single player, | 216 | // If all the parameters are met AND only allows a single player, |
| 204 | // stop the constructor here as we do not need to continue. | 217 | // stop the constructor here as we do not need to continue. |
| @@ -217,6 +230,7 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( | |||
| 217 | } | 230 | } |
| 218 | 231 | ||
| 219 | QtControllerSelectorDialog::~QtControllerSelectorDialog() { | 232 | QtControllerSelectorDialog::~QtControllerSelectorDialog() { |
| 233 | controller_navigation->UnloadController(); | ||
| 220 | system.HIDCore().DisableAllControllerConfiguration(); | 234 | system.HIDCore().DisableAllControllerConfiguration(); |
| 221 | } | 235 | } |
| 222 | 236 | ||
| @@ -291,6 +305,31 @@ void QtControllerSelectorDialog::CallConfigureInputProfileDialog() { | |||
| 291 | dialog.exec(); | 305 | dialog.exec(); |
| 292 | } | 306 | } |
| 293 | 307 | ||
| 308 | void QtControllerSelectorDialog::keyPressEvent(QKeyEvent* evt) { | ||
| 309 | const auto num_connected_players = static_cast<int>( | ||
| 310 | std::count_if(player_groupboxes.begin(), player_groupboxes.end(), | ||
| 311 | [](const QGroupBox* player) { return player->isChecked(); })); | ||
| 312 | |||
| 313 | const auto min_supported_players = parameters.enable_single_mode ? 1 : parameters.min_players; | ||
| 314 | const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players; | ||
| 315 | |||
| 316 | if ((evt->key() == Qt::Key_Enter || evt->key() == Qt::Key_Return) && !parameters_met) { | ||
| 317 | // Display error message when trying to validate using "Enter" and "OK" button is disabled | ||
| 318 | ui->labelError->setVisible(true); | ||
| 319 | return; | ||
| 320 | } else if (evt->key() == Qt::Key_Left && num_connected_players > min_supported_players) { | ||
| 321 | // Remove a player if possible | ||
| 322 | connected_controller_checkboxes[num_connected_players - 1]->setChecked(false); | ||
| 323 | return; | ||
| 324 | } else if (evt->key() == Qt::Key_Right && num_connected_players < max_supported_players) { | ||
| 325 | // Add a player, if possible | ||
| 326 | ui->labelError->setVisible(false); | ||
| 327 | connected_controller_checkboxes[num_connected_players]->setChecked(true); | ||
| 328 | return; | ||
| 329 | } | ||
| 330 | QDialog::keyPressEvent(evt); | ||
| 331 | } | ||
| 332 | |||
| 294 | bool QtControllerSelectorDialog::CheckIfParametersMet() { | 333 | bool QtControllerSelectorDialog::CheckIfParametersMet() { |
| 295 | // Here, we check and validate the current configuration against all applicable parameters. | 334 | // Here, we check and validate the current configuration against all applicable parameters. |
| 296 | const auto num_connected_players = static_cast<int>( | 335 | const auto num_connected_players = static_cast<int>( |
diff --git a/src/yuzu/applets/qt_controller.h b/src/yuzu/applets/qt_controller.h index 2fdc35857..7f0673d06 100644 --- a/src/yuzu/applets/qt_controller.h +++ b/src/yuzu/applets/qt_controller.h | |||
| @@ -34,6 +34,8 @@ class HIDCore; | |||
| 34 | enum class NpadStyleIndex : u8; | 34 | enum class NpadStyleIndex : u8; |
| 35 | } // namespace Core::HID | 35 | } // namespace Core::HID |
| 36 | 36 | ||
| 37 | class ControllerNavigation; | ||
| 38 | |||
| 37 | class QtControllerSelectorDialog final : public QDialog { | 39 | class QtControllerSelectorDialog final : public QDialog { |
| 38 | Q_OBJECT | 40 | Q_OBJECT |
| 39 | 41 | ||
| @@ -46,6 +48,8 @@ public: | |||
| 46 | 48 | ||
| 47 | int exec() override; | 49 | int exec() override; |
| 48 | 50 | ||
| 51 | void keyPressEvent(QKeyEvent* evt) override; | ||
| 52 | |||
| 49 | private: | 53 | private: |
| 50 | // Applies the current configuration. | 54 | // Applies the current configuration. |
| 51 | void ApplyConfiguration(); | 55 | void ApplyConfiguration(); |
| @@ -110,6 +114,8 @@ private: | |||
| 110 | 114 | ||
| 111 | Core::System& system; | 115 | Core::System& system; |
| 112 | 116 | ||
| 117 | ControllerNavigation* controller_navigation = nullptr; | ||
| 118 | |||
| 113 | // This is true if and only if all parameters are met. Otherwise, this is false. | 119 | // This is true if and only if all parameters are met. Otherwise, this is false. |
| 114 | // This determines whether the "OK" button can be clicked to exit the applet. | 120 | // This determines whether the "OK" button can be clicked to exit the applet. |
| 115 | bool parameters_met{false}; | 121 | bool parameters_met{false}; |
diff --git a/src/yuzu/applets/qt_controller.ui b/src/yuzu/applets/qt_controller.ui index 729e921ee..6f7cb3c13 100644 --- a/src/yuzu/applets/qt_controller.ui +++ b/src/yuzu/applets/qt_controller.ui | |||
| @@ -2624,13 +2624,53 @@ | |||
| 2624 | </spacer> | 2624 | </spacer> |
| 2625 | </item> | 2625 | </item> |
| 2626 | <item alignment="Qt::AlignBottom"> | 2626 | <item alignment="Qt::AlignBottom"> |
| 2627 | <widget class="QDialogButtonBox" name="buttonBox"> | 2627 | <widget class="QWidget" name="closeButtons" native="true"> |
| 2628 | <property name="enabled"> | 2628 | <layout class="QVBoxLayout" name="verticalLayout_46"> |
| 2629 | <bool>true</bool> | 2629 | <property name="spacing"> |
| 2630 | </property> | 2630 | <number>7</number> |
| 2631 | <property name="standardButtons"> | 2631 | </property> |
| 2632 | <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> | 2632 | <property name="leftMargin"> |
| 2633 | </property> | 2633 | <number>0</number> |
| 2634 | </property> | ||
| 2635 | <property name="topMargin"> | ||
| 2636 | <number>0</number> | ||
| 2637 | </property> | ||
| 2638 | <property name="rightMargin"> | ||
| 2639 | <number>0</number> | ||
| 2640 | </property> | ||
| 2641 | <property name="bottomMargin"> | ||
| 2642 | <number>0</number> | ||
| 2643 | </property> | ||
| 2644 | <item> | ||
| 2645 | <widget class="QLabel" name="labelError"> | ||
| 2646 | <property name="enabled"> | ||
| 2647 | <bool>true</bool> | ||
| 2648 | </property> | ||
| 2649 | <property name="styleSheet"> | ||
| 2650 | <string notr="true">QLabel { color : red; }</string> | ||
| 2651 | </property> | ||
| 2652 | <property name="text"> | ||
| 2653 | <string>Not enough controllers</string> | ||
| 2654 | </property> | ||
| 2655 | <property name="alignment"> | ||
| 2656 | <set>Qt::AlignCenter</set> | ||
| 2657 | </property> | ||
| 2658 | <property name="margin"> | ||
| 2659 | <number>0</number> | ||
| 2660 | </property> | ||
| 2661 | </widget> | ||
| 2662 | </item> | ||
| 2663 | <item> | ||
| 2664 | <widget class="QDialogButtonBox" name="buttonBox"> | ||
| 2665 | <property name="enabled"> | ||
| 2666 | <bool>true</bool> | ||
| 2667 | </property> | ||
| 2668 | <property name="standardButtons"> | ||
| 2669 | <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> | ||
| 2670 | </property> | ||
| 2671 | </widget> | ||
| 2672 | </item> | ||
| 2673 | </layout> | ||
| 2634 | </widget> | 2674 | </widget> |
| 2635 | </item> | 2675 | </item> |
| 2636 | </layout> | 2676 | </layout> |
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp index 9ccfb2435..81dd51ad3 100644 --- a/src/yuzu/configuration/configure_audio.cpp +++ b/src/yuzu/configuration/configure_audio.cpp | |||
| @@ -42,6 +42,9 @@ void ConfigureAudio::Setup(const ConfigurationShared::Builder& builder) { | |||
| 42 | for (auto* setting : Settings::values.linkage.by_category[category]) { | 42 | for (auto* setting : Settings::values.linkage.by_category[category]) { |
| 43 | settings.push_back(setting); | 43 | settings.push_back(setting); |
| 44 | } | 44 | } |
| 45 | for (auto* setting : UISettings::values.linkage.by_category[category]) { | ||
| 46 | settings.push_back(setting); | ||
| 47 | } | ||
| 45 | }; | 48 | }; |
| 46 | 49 | ||
| 47 | push(Settings::Category::Audio); | 50 | push(Settings::Category::Audio); |
diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp index a9fde9f4f..82f3b6e78 100644 --- a/src/yuzu/configuration/configure_ui.cpp +++ b/src/yuzu/configuration/configure_ui.cpp | |||
| @@ -123,6 +123,8 @@ ConfigureUi::ConfigureUi(Core::System& system_, QWidget* parent) | |||
| 123 | connect(ui->show_compat, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate); | 123 | connect(ui->show_compat, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate); |
| 124 | connect(ui->show_size, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate); | 124 | connect(ui->show_size, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate); |
| 125 | connect(ui->show_types, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate); | 125 | connect(ui->show_types, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate); |
| 126 | connect(ui->show_play_time, &QCheckBox::stateChanged, this, | ||
| 127 | &ConfigureUi::RequestGameListUpdate); | ||
| 126 | connect(ui->game_icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, | 128 | connect(ui->game_icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, |
| 127 | &ConfigureUi::RequestGameListUpdate); | 129 | &ConfigureUi::RequestGameListUpdate); |
| 128 | connect(ui->folder_icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), | 130 | connect(ui->folder_icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), |
| @@ -167,6 +169,7 @@ void ConfigureUi::ApplyConfiguration() { | |||
| 167 | UISettings::values.show_compat = ui->show_compat->isChecked(); | 169 | UISettings::values.show_compat = ui->show_compat->isChecked(); |
| 168 | UISettings::values.show_size = ui->show_size->isChecked(); | 170 | UISettings::values.show_size = ui->show_size->isChecked(); |
| 169 | UISettings::values.show_types = ui->show_types->isChecked(); | 171 | UISettings::values.show_types = ui->show_types->isChecked(); |
| 172 | UISettings::values.show_play_time = ui->show_play_time->isChecked(); | ||
| 170 | UISettings::values.game_icon_size = ui->game_icon_size_combobox->currentData().toUInt(); | 173 | UISettings::values.game_icon_size = ui->game_icon_size_combobox->currentData().toUInt(); |
| 171 | UISettings::values.folder_icon_size = ui->folder_icon_size_combobox->currentData().toUInt(); | 174 | UISettings::values.folder_icon_size = ui->folder_icon_size_combobox->currentData().toUInt(); |
| 172 | UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt(); | 175 | UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt(); |
| @@ -179,6 +182,7 @@ void ConfigureUi::ApplyConfiguration() { | |||
| 179 | const u32 height = ScreenshotDimensionToInt(ui->screenshot_height->currentText()); | 182 | const u32 height = ScreenshotDimensionToInt(ui->screenshot_height->currentText()); |
| 180 | UISettings::values.screenshot_height.SetValue(height); | 183 | UISettings::values.screenshot_height.SetValue(height); |
| 181 | 184 | ||
| 185 | RequestGameListUpdate(); | ||
| 182 | system.ApplySettings(); | 186 | system.ApplySettings(); |
| 183 | } | 187 | } |
| 184 | 188 | ||
| @@ -194,6 +198,7 @@ void ConfigureUi::SetConfiguration() { | |||
| 194 | ui->show_compat->setChecked(UISettings::values.show_compat.GetValue()); | 198 | ui->show_compat->setChecked(UISettings::values.show_compat.GetValue()); |
| 195 | ui->show_size->setChecked(UISettings::values.show_size.GetValue()); | 199 | ui->show_size->setChecked(UISettings::values.show_size.GetValue()); |
| 196 | ui->show_types->setChecked(UISettings::values.show_types.GetValue()); | 200 | ui->show_types->setChecked(UISettings::values.show_types.GetValue()); |
| 201 | ui->show_play_time->setChecked(UISettings::values.show_play_time.GetValue()); | ||
| 197 | ui->game_icon_size_combobox->setCurrentIndex( | 202 | ui->game_icon_size_combobox->setCurrentIndex( |
| 198 | ui->game_icon_size_combobox->findData(UISettings::values.game_icon_size.GetValue())); | 203 | ui->game_icon_size_combobox->findData(UISettings::values.game_icon_size.GetValue())); |
| 199 | ui->folder_icon_size_combobox->setCurrentIndex( | 204 | ui->folder_icon_size_combobox->setCurrentIndex( |
diff --git a/src/yuzu/configuration/configure_ui.ui b/src/yuzu/configuration/configure_ui.ui index cb66ef104..b8e648381 100644 --- a/src/yuzu/configuration/configure_ui.ui +++ b/src/yuzu/configuration/configure_ui.ui | |||
| @@ -105,6 +105,13 @@ | |||
| 105 | </widget> | 105 | </widget> |
| 106 | </item> | 106 | </item> |
| 107 | <item> | 107 | <item> |
| 108 | <widget class="QCheckBox" name="show_play_time"> | ||
| 109 | <property name="text"> | ||
| 110 | <string>Show Play Time Column</string> | ||
| 111 | </property> | ||
| 112 | </widget> | ||
| 113 | </item> | ||
| 114 | <item> | ||
| 108 | <layout class="QHBoxLayout" name="game_icon_size_qhbox_layout_2"> | 115 | <layout class="QHBoxLayout" name="game_icon_size_qhbox_layout_2"> |
| 109 | <item> | 116 | <item> |
| 110 | <widget class="QLabel" name="game_icon_size_label"> | 117 | <widget class="QLabel" name="game_icon_size_label"> |
diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp index 276bdbaba..a4e8af1b4 100644 --- a/src/yuzu/configuration/shared_translation.cpp +++ b/src/yuzu/configuration/shared_translation.cpp | |||
| @@ -29,9 +29,10 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) { | |||
| 29 | INSERT(Settings, sink_id, "Output Engine:", ""); | 29 | INSERT(Settings, sink_id, "Output Engine:", ""); |
| 30 | INSERT(Settings, audio_output_device_id, "Output Device:", ""); | 30 | INSERT(Settings, audio_output_device_id, "Output Device:", ""); |
| 31 | INSERT(Settings, audio_input_device_id, "Input Device:", ""); | 31 | INSERT(Settings, audio_input_device_id, "Input Device:", ""); |
| 32 | INSERT(Settings, audio_muted, "Mute audio when in background", ""); | 32 | INSERT(Settings, audio_muted, "Mute audio", ""); |
| 33 | INSERT(Settings, volume, "Volume:", ""); | 33 | INSERT(Settings, volume, "Volume:", ""); |
| 34 | INSERT(Settings, dump_audio_commands, "", ""); | 34 | INSERT(Settings, dump_audio_commands, "", ""); |
| 35 | INSERT(UISettings, mute_when_in_background, "Mute audio when in background", ""); | ||
| 35 | 36 | ||
| 36 | // Core | 37 | // Core |
| 37 | INSERT(Settings, use_multi_core, "Multicore CPU Emulation", ""); | 38 | INSERT(Settings, use_multi_core, "Multicore CPU Emulation", ""); |
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index f254c1e1c..74f48031a 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp | |||
| @@ -312,8 +312,10 @@ void GameList::OnFilterCloseClicked() { | |||
| 312 | } | 312 | } |
| 313 | 313 | ||
| 314 | GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvider* provider_, | 314 | GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvider* provider_, |
| 315 | Core::System& system_, GMainWindow* parent) | 315 | PlayTime::PlayTimeManager& play_time_manager_, Core::System& system_, |
| 316 | : QWidget{parent}, vfs{std::move(vfs_)}, provider{provider_}, system{system_} { | 316 | GMainWindow* parent) |
| 317 | : QWidget{parent}, vfs{std::move(vfs_)}, provider{provider_}, | ||
| 318 | play_time_manager{play_time_manager_}, system{system_} { | ||
| 317 | watcher = new QFileSystemWatcher(this); | 319 | watcher = new QFileSystemWatcher(this); |
| 318 | connect(watcher, &QFileSystemWatcher::directoryChanged, this, &GameList::RefreshGameDirectory); | 320 | connect(watcher, &QFileSystemWatcher::directoryChanged, this, &GameList::RefreshGameDirectory); |
| 319 | 321 | ||
| @@ -340,6 +342,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvid | |||
| 340 | 342 | ||
| 341 | tree_view->setColumnHidden(COLUMN_ADD_ONS, !UISettings::values.show_add_ons); | 343 | tree_view->setColumnHidden(COLUMN_ADD_ONS, !UISettings::values.show_add_ons); |
| 342 | tree_view->setColumnHidden(COLUMN_COMPATIBILITY, !UISettings::values.show_compat); | 344 | tree_view->setColumnHidden(COLUMN_COMPATIBILITY, !UISettings::values.show_compat); |
| 345 | tree_view->setColumnHidden(COLUMN_PLAY_TIME, !UISettings::values.show_play_time); | ||
| 343 | item_model->setSortRole(GameListItemPath::SortRole); | 346 | item_model->setSortRole(GameListItemPath::SortRole); |
| 344 | 347 | ||
| 345 | connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::OnUpdateThemedIcons); | 348 | connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::OnUpdateThemedIcons); |
| @@ -548,6 +551,7 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri | |||
| 548 | QAction* remove_update = remove_menu->addAction(tr("Remove Installed Update")); | 551 | QAction* remove_update = remove_menu->addAction(tr("Remove Installed Update")); |
| 549 | QAction* remove_dlc = remove_menu->addAction(tr("Remove All Installed DLC")); | 552 | QAction* remove_dlc = remove_menu->addAction(tr("Remove All Installed DLC")); |
| 550 | QAction* remove_custom_config = remove_menu->addAction(tr("Remove Custom Configuration")); | 553 | QAction* remove_custom_config = remove_menu->addAction(tr("Remove Custom Configuration")); |
| 554 | QAction* remove_play_time_data = remove_menu->addAction(tr("Remove Play Time Data")); | ||
| 551 | QAction* remove_cache_storage = remove_menu->addAction(tr("Remove Cache Storage")); | 555 | QAction* remove_cache_storage = remove_menu->addAction(tr("Remove Cache Storage")); |
| 552 | QAction* remove_gl_shader_cache = remove_menu->addAction(tr("Remove OpenGL Pipeline Cache")); | 556 | QAction* remove_gl_shader_cache = remove_menu->addAction(tr("Remove OpenGL Pipeline Cache")); |
| 553 | QAction* remove_vk_shader_cache = remove_menu->addAction(tr("Remove Vulkan Pipeline Cache")); | 557 | QAction* remove_vk_shader_cache = remove_menu->addAction(tr("Remove Vulkan Pipeline Cache")); |
| @@ -560,9 +564,9 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri | |||
| 560 | QAction* verify_integrity = context_menu.addAction(tr("Verify Integrity")); | 564 | QAction* verify_integrity = context_menu.addAction(tr("Verify Integrity")); |
| 561 | QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); | 565 | QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); |
| 562 | QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); | 566 | QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); |
| 563 | #ifndef WIN32 | ||
| 564 | QMenu* shortcut_menu = context_menu.addMenu(tr("Create Shortcut")); | 567 | QMenu* shortcut_menu = context_menu.addMenu(tr("Create Shortcut")); |
| 565 | QAction* create_desktop_shortcut = shortcut_menu->addAction(tr("Add to Desktop")); | 568 | QAction* create_desktop_shortcut = shortcut_menu->addAction(tr("Add to Desktop")); |
| 569 | #ifndef WIN32 | ||
| 566 | QAction* create_applications_menu_shortcut = | 570 | QAction* create_applications_menu_shortcut = |
| 567 | shortcut_menu->addAction(tr("Add to Applications Menu")); | 571 | shortcut_menu->addAction(tr("Add to Applications Menu")); |
| 568 | #endif | 572 | #endif |
| @@ -622,6 +626,8 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri | |||
| 622 | connect(remove_custom_config, &QAction::triggered, [this, program_id, path]() { | 626 | connect(remove_custom_config, &QAction::triggered, [this, program_id, path]() { |
| 623 | emit RemoveFileRequested(program_id, GameListRemoveTarget::CustomConfiguration, path); | 627 | emit RemoveFileRequested(program_id, GameListRemoveTarget::CustomConfiguration, path); |
| 624 | }); | 628 | }); |
| 629 | connect(remove_play_time_data, &QAction::triggered, | ||
| 630 | [this, program_id]() { emit RemovePlayTimeRequested(program_id); }); | ||
| 625 | connect(remove_cache_storage, &QAction::triggered, [this, program_id, path] { | 631 | connect(remove_cache_storage, &QAction::triggered, [this, program_id, path] { |
| 626 | emit RemoveFileRequested(program_id, GameListRemoveTarget::CacheStorage, path); | 632 | emit RemoveFileRequested(program_id, GameListRemoveTarget::CacheStorage, path); |
| 627 | }); | 633 | }); |
| @@ -638,10 +644,10 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri | |||
| 638 | connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() { | 644 | connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() { |
| 639 | emit NavigateToGamedbEntryRequested(program_id, compatibility_list); | 645 | emit NavigateToGamedbEntryRequested(program_id, compatibility_list); |
| 640 | }); | 646 | }); |
| 641 | #ifndef WIN32 | ||
| 642 | connect(create_desktop_shortcut, &QAction::triggered, [this, program_id, path]() { | 647 | connect(create_desktop_shortcut, &QAction::triggered, [this, program_id, path]() { |
| 643 | emit CreateShortcut(program_id, path, GameListShortcutTarget::Desktop); | 648 | emit CreateShortcut(program_id, path, GameListShortcutTarget::Desktop); |
| 644 | }); | 649 | }); |
| 650 | #ifndef WIN32 | ||
| 645 | connect(create_applications_menu_shortcut, &QAction::triggered, [this, program_id, path]() { | 651 | connect(create_applications_menu_shortcut, &QAction::triggered, [this, program_id, path]() { |
| 646 | emit CreateShortcut(program_id, path, GameListShortcutTarget::Applications); | 652 | emit CreateShortcut(program_id, path, GameListShortcutTarget::Applications); |
| 647 | }); | 653 | }); |
| @@ -790,6 +796,7 @@ void GameList::RetranslateUI() { | |||
| 790 | item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons")); | 796 | item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons")); |
| 791 | item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type")); | 797 | item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type")); |
| 792 | item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size")); | 798 | item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size")); |
| 799 | item_model->setHeaderData(COLUMN_PLAY_TIME, Qt::Horizontal, tr("Play time")); | ||
| 793 | } | 800 | } |
| 794 | 801 | ||
| 795 | void GameListSearchField::changeEvent(QEvent* event) { | 802 | void GameListSearchField::changeEvent(QEvent* event) { |
| @@ -817,6 +824,7 @@ void GameList::PopulateAsync(QVector<UISettings::GameDir>& game_dirs) { | |||
| 817 | tree_view->setColumnHidden(COLUMN_COMPATIBILITY, !UISettings::values.show_compat); | 824 | tree_view->setColumnHidden(COLUMN_COMPATIBILITY, !UISettings::values.show_compat); |
| 818 | tree_view->setColumnHidden(COLUMN_FILE_TYPE, !UISettings::values.show_types); | 825 | tree_view->setColumnHidden(COLUMN_FILE_TYPE, !UISettings::values.show_types); |
| 819 | tree_view->setColumnHidden(COLUMN_SIZE, !UISettings::values.show_size); | 826 | tree_view->setColumnHidden(COLUMN_SIZE, !UISettings::values.show_size); |
| 827 | tree_view->setColumnHidden(COLUMN_PLAY_TIME, !UISettings::values.show_play_time); | ||
| 820 | 828 | ||
| 821 | // Delete any rows that might already exist if we're repopulating | 829 | // Delete any rows that might already exist if we're repopulating |
| 822 | item_model->removeRows(0, item_model->rowCount()); | 830 | item_model->removeRows(0, item_model->rowCount()); |
| @@ -825,7 +833,7 @@ void GameList::PopulateAsync(QVector<UISettings::GameDir>& game_dirs) { | |||
| 825 | emit ShouldCancelWorker(); | 833 | emit ShouldCancelWorker(); |
| 826 | 834 | ||
| 827 | GameListWorker* worker = | 835 | GameListWorker* worker = |
| 828 | new GameListWorker(vfs, provider, game_dirs, compatibility_list, system); | 836 | new GameListWorker(vfs, provider, game_dirs, compatibility_list, play_time_manager, system); |
| 829 | 837 | ||
| 830 | connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection); | 838 | connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection); |
| 831 | connect(worker, &GameListWorker::DirEntryReady, this, &GameList::AddDirEntry, | 839 | connect(worker, &GameListWorker::DirEntryReady, this, &GameList::AddDirEntry, |
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index 1fcbbf0ba..712570cea 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | #include "core/core.h" | 18 | #include "core/core.h" |
| 19 | #include "uisettings.h" | 19 | #include "uisettings.h" |
| 20 | #include "yuzu/compatibility_list.h" | 20 | #include "yuzu/compatibility_list.h" |
| 21 | #include "yuzu/play_time_manager.h" | ||
| 21 | 22 | ||
| 22 | namespace Core { | 23 | namespace Core { |
| 23 | class System; | 24 | class System; |
| @@ -75,11 +76,13 @@ public: | |||
| 75 | COLUMN_ADD_ONS, | 76 | COLUMN_ADD_ONS, |
| 76 | COLUMN_FILE_TYPE, | 77 | COLUMN_FILE_TYPE, |
| 77 | COLUMN_SIZE, | 78 | COLUMN_SIZE, |
| 79 | COLUMN_PLAY_TIME, | ||
| 78 | COLUMN_COUNT, // Number of columns | 80 | COLUMN_COUNT, // Number of columns |
| 79 | }; | 81 | }; |
| 80 | 82 | ||
| 81 | explicit GameList(std::shared_ptr<FileSys::VfsFilesystem> vfs_, | 83 | explicit GameList(std::shared_ptr<FileSys::VfsFilesystem> vfs_, |
| 82 | FileSys::ManualContentProvider* provider_, Core::System& system_, | 84 | FileSys::ManualContentProvider* provider_, |
| 85 | PlayTime::PlayTimeManager& play_time_manager_, Core::System& system_, | ||
| 83 | GMainWindow* parent = nullptr); | 86 | GMainWindow* parent = nullptr); |
| 84 | ~GameList() override; | 87 | ~GameList() override; |
| 85 | 88 | ||
| @@ -113,6 +116,7 @@ signals: | |||
| 113 | void RemoveInstalledEntryRequested(u64 program_id, InstalledEntryType type); | 116 | void RemoveInstalledEntryRequested(u64 program_id, InstalledEntryType type); |
| 114 | void RemoveFileRequested(u64 program_id, GameListRemoveTarget target, | 117 | void RemoveFileRequested(u64 program_id, GameListRemoveTarget target, |
| 115 | const std::string& game_path); | 118 | const std::string& game_path); |
| 119 | void RemovePlayTimeRequested(u64 program_id); | ||
| 116 | void DumpRomFSRequested(u64 program_id, const std::string& game_path, DumpRomFSTarget target); | 120 | void DumpRomFSRequested(u64 program_id, const std::string& game_path, DumpRomFSTarget target); |
| 117 | void VerifyIntegrityRequested(const std::string& game_path); | 121 | void VerifyIntegrityRequested(const std::string& game_path); |
| 118 | void CopyTIDRequested(u64 program_id); | 122 | void CopyTIDRequested(u64 program_id); |
| @@ -168,6 +172,7 @@ private: | |||
| 168 | 172 | ||
| 169 | friend class GameListSearchField; | 173 | friend class GameListSearchField; |
| 170 | 174 | ||
| 175 | const PlayTime::PlayTimeManager& play_time_manager; | ||
| 171 | Core::System& system; | 176 | Core::System& system; |
| 172 | }; | 177 | }; |
| 173 | 178 | ||
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index 1800f090f..86a0c41d9 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | #include "common/common_types.h" | 18 | #include "common/common_types.h" |
| 19 | #include "common/logging/log.h" | 19 | #include "common/logging/log.h" |
| 20 | #include "common/string_util.h" | 20 | #include "common/string_util.h" |
| 21 | #include "yuzu/play_time_manager.h" | ||
| 21 | #include "yuzu/uisettings.h" | 22 | #include "yuzu/uisettings.h" |
| 22 | #include "yuzu/util/util.h" | 23 | #include "yuzu/util/util.h" |
| 23 | 24 | ||
| @@ -221,6 +222,31 @@ public: | |||
| 221 | } | 222 | } |
| 222 | }; | 223 | }; |
| 223 | 224 | ||
| 225 | /** | ||
| 226 | * GameListItem for Play Time values. | ||
| 227 | * This object stores the play time of a game in seconds, and its readable | ||
| 228 | * representation in minutes/hours | ||
| 229 | */ | ||
| 230 | class GameListItemPlayTime : public GameListItem { | ||
| 231 | public: | ||
| 232 | static constexpr int PlayTimeRole = SortRole; | ||
| 233 | |||
| 234 | GameListItemPlayTime() = default; | ||
| 235 | explicit GameListItemPlayTime(const qulonglong time_seconds) { | ||
| 236 | setData(time_seconds, PlayTimeRole); | ||
| 237 | } | ||
| 238 | |||
| 239 | void setData(const QVariant& value, int role) override { | ||
| 240 | qulonglong time_seconds = value.toULongLong(); | ||
| 241 | GameListItem::setData(PlayTime::ReadablePlayTime(time_seconds), Qt::DisplayRole); | ||
| 242 | GameListItem::setData(value, PlayTimeRole); | ||
| 243 | } | ||
| 244 | |||
| 245 | bool operator<(const QStandardItem& other) const override { | ||
| 246 | return data(PlayTimeRole).toULongLong() < other.data(PlayTimeRole).toULongLong(); | ||
| 247 | } | ||
| 248 | }; | ||
| 249 | |||
| 224 | class GameListDir : public GameListItem { | 250 | class GameListDir : public GameListItem { |
| 225 | public: | 251 | public: |
| 226 | static constexpr int GameDirRole = Qt::UserRole + 2; | 252 | static constexpr int GameDirRole = Qt::UserRole + 2; |
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index e7fb8a282..588f1dd6e 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp | |||
| @@ -194,6 +194,7 @@ QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::stri | |||
| 194 | const std::size_t size, const std::vector<u8>& icon, | 194 | const std::size_t size, const std::vector<u8>& icon, |
| 195 | Loader::AppLoader& loader, u64 program_id, | 195 | Loader::AppLoader& loader, u64 program_id, |
| 196 | const CompatibilityList& compatibility_list, | 196 | const CompatibilityList& compatibility_list, |
| 197 | const PlayTime::PlayTimeManager& play_time_manager, | ||
| 197 | const FileSys::PatchManager& patch) { | 198 | const FileSys::PatchManager& patch) { |
| 198 | const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); | 199 | const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); |
| 199 | 200 | ||
| @@ -212,6 +213,7 @@ QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::stri | |||
| 212 | new GameListItemCompat(compatibility), | 213 | new GameListItemCompat(compatibility), |
| 213 | new GameListItem(file_type_string), | 214 | new GameListItem(file_type_string), |
| 214 | new GameListItemSize(size), | 215 | new GameListItemSize(size), |
| 216 | new GameListItemPlayTime(play_time_manager.GetPlayTime(program_id)), | ||
| 215 | }; | 217 | }; |
| 216 | 218 | ||
| 217 | const auto patch_versions = GetGameListCachedObject( | 219 | const auto patch_versions = GetGameListCachedObject( |
| @@ -227,9 +229,12 @@ QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::stri | |||
| 227 | GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs_, | 229 | GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs_, |
| 228 | FileSys::ManualContentProvider* provider_, | 230 | FileSys::ManualContentProvider* provider_, |
| 229 | QVector<UISettings::GameDir>& game_dirs_, | 231 | QVector<UISettings::GameDir>& game_dirs_, |
| 230 | const CompatibilityList& compatibility_list_, Core::System& system_) | 232 | const CompatibilityList& compatibility_list_, |
| 233 | const PlayTime::PlayTimeManager& play_time_manager_, | ||
| 234 | Core::System& system_) | ||
| 231 | : vfs{std::move(vfs_)}, provider{provider_}, game_dirs{game_dirs_}, | 235 | : vfs{std::move(vfs_)}, provider{provider_}, game_dirs{game_dirs_}, |
| 232 | compatibility_list{compatibility_list_}, system{system_} {} | 236 | compatibility_list{compatibility_list_}, |
| 237 | play_time_manager{play_time_manager_}, system{system_} {} | ||
| 233 | 238 | ||
| 234 | GameListWorker::~GameListWorker() = default; | 239 | GameListWorker::~GameListWorker() = default; |
| 235 | 240 | ||
| @@ -280,7 +285,7 @@ void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) { | |||
| 280 | } | 285 | } |
| 281 | 286 | ||
| 282 | emit EntryReady(MakeGameListEntry(file->GetFullPath(), name, file->GetSize(), icon, *loader, | 287 | emit EntryReady(MakeGameListEntry(file->GetFullPath(), name, file->GetSize(), icon, *loader, |
| 283 | program_id, compatibility_list, patch), | 288 | program_id, compatibility_list, play_time_manager, patch), |
| 284 | parent_dir); | 289 | parent_dir); |
| 285 | } | 290 | } |
| 286 | } | 291 | } |
| @@ -357,7 +362,8 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa | |||
| 357 | 362 | ||
| 358 | emit EntryReady(MakeGameListEntry(physical_name, name, | 363 | emit EntryReady(MakeGameListEntry(physical_name, name, |
| 359 | Common::FS::GetSize(physical_name), icon, | 364 | Common::FS::GetSize(physical_name), icon, |
| 360 | *loader, id, compatibility_list, patch), | 365 | *loader, id, compatibility_list, |
| 366 | play_time_manager, patch), | ||
| 361 | parent_dir); | 367 | parent_dir); |
| 362 | } | 368 | } |
| 363 | } else { | 369 | } else { |
| @@ -370,10 +376,11 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa | |||
| 370 | const FileSys::PatchManager patch{program_id, system.GetFileSystemController(), | 376 | const FileSys::PatchManager patch{program_id, system.GetFileSystemController(), |
| 371 | system.GetContentProvider()}; | 377 | system.GetContentProvider()}; |
| 372 | 378 | ||
| 373 | emit EntryReady( | 379 | emit EntryReady(MakeGameListEntry(physical_name, name, |
| 374 | MakeGameListEntry(physical_name, name, Common::FS::GetSize(physical_name), | 380 | Common::FS::GetSize(physical_name), icon, |
| 375 | icon, *loader, program_id, compatibility_list, patch), | 381 | *loader, program_id, compatibility_list, |
| 376 | parent_dir); | 382 | play_time_manager, patch), |
| 383 | parent_dir); | ||
| 377 | } | 384 | } |
| 378 | } | 385 | } |
| 379 | } else if (is_dir) { | 386 | } else if (is_dir) { |
diff --git a/src/yuzu/game_list_worker.h b/src/yuzu/game_list_worker.h index 24a4e92c3..2bb0a0cb6 100644 --- a/src/yuzu/game_list_worker.h +++ b/src/yuzu/game_list_worker.h | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include <QString> | 13 | #include <QString> |
| 14 | 14 | ||
| 15 | #include "yuzu/compatibility_list.h" | 15 | #include "yuzu/compatibility_list.h" |
| 16 | #include "yuzu/play_time_manager.h" | ||
| 16 | 17 | ||
| 17 | namespace Core { | 18 | namespace Core { |
| 18 | class System; | 19 | class System; |
| @@ -36,7 +37,9 @@ public: | |||
| 36 | explicit GameListWorker(std::shared_ptr<FileSys::VfsFilesystem> vfs_, | 37 | explicit GameListWorker(std::shared_ptr<FileSys::VfsFilesystem> vfs_, |
| 37 | FileSys::ManualContentProvider* provider_, | 38 | FileSys::ManualContentProvider* provider_, |
| 38 | QVector<UISettings::GameDir>& game_dirs_, | 39 | QVector<UISettings::GameDir>& game_dirs_, |
| 39 | const CompatibilityList& compatibility_list_, Core::System& system_); | 40 | const CompatibilityList& compatibility_list_, |
| 41 | const PlayTime::PlayTimeManager& play_time_manager_, | ||
| 42 | Core::System& system_); | ||
| 40 | ~GameListWorker() override; | 43 | ~GameListWorker() override; |
| 41 | 44 | ||
| 42 | /// Starts the processing of directory tree information. | 45 | /// Starts the processing of directory tree information. |
| @@ -76,6 +79,7 @@ private: | |||
| 76 | FileSys::ManualContentProvider* provider; | 79 | FileSys::ManualContentProvider* provider; |
| 77 | QVector<UISettings::GameDir>& game_dirs; | 80 | QVector<UISettings::GameDir>& game_dirs; |
| 78 | const CompatibilityList& compatibility_list; | 81 | const CompatibilityList& compatibility_list; |
| 82 | const PlayTime::PlayTimeManager& play_time_manager; | ||
| 79 | 83 | ||
| 80 | QStringList watch_list; | 84 | QStringList watch_list; |
| 81 | std::atomic_bool stop_processing; | 85 | std::atomic_bool stop_processing; |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index adb7b332f..5427758c1 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -18,6 +18,8 @@ | |||
| 18 | #include <sys/socket.h> | 18 | #include <sys/socket.h> |
| 19 | #endif | 19 | #endif |
| 20 | 20 | ||
| 21 | #include <boost/container/flat_set.hpp> | ||
| 22 | |||
| 21 | // VFS includes must be before glad as they will conflict with Windows file api, which uses defines. | 23 | // VFS includes must be before glad as they will conflict with Windows file api, which uses defines. |
| 22 | #include "applets/qt_amiibo_settings.h" | 24 | #include "applets/qt_amiibo_settings.h" |
| 23 | #include "applets/qt_controller.h" | 25 | #include "applets/qt_controller.h" |
| @@ -96,6 +98,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 96 | #include "common/scm_rev.h" | 98 | #include "common/scm_rev.h" |
| 97 | #include "common/scope_exit.h" | 99 | #include "common/scope_exit.h" |
| 98 | #ifdef _WIN32 | 100 | #ifdef _WIN32 |
| 101 | #include <shlobj.h> | ||
| 99 | #include "common/windows/timer_resolution.h" | 102 | #include "common/windows/timer_resolution.h" |
| 100 | #endif | 103 | #endif |
| 101 | #ifdef ARCHITECTURE_x86_64 | 104 | #ifdef ARCHITECTURE_x86_64 |
| @@ -148,6 +151,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 148 | #include "yuzu/install_dialog.h" | 151 | #include "yuzu/install_dialog.h" |
| 149 | #include "yuzu/loading_screen.h" | 152 | #include "yuzu/loading_screen.h" |
| 150 | #include "yuzu/main.h" | 153 | #include "yuzu/main.h" |
| 154 | #include "yuzu/play_time_manager.h" | ||
| 151 | #include "yuzu/startup_checks.h" | 155 | #include "yuzu/startup_checks.h" |
| 152 | #include "yuzu/uisettings.h" | 156 | #include "yuzu/uisettings.h" |
| 153 | #include "yuzu/util/clickable_label.h" | 157 | #include "yuzu/util/clickable_label.h" |
| @@ -336,6 +340,8 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan | |||
| 336 | SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue()); | 340 | SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue()); |
| 337 | discord_rpc->Update(); | 341 | discord_rpc->Update(); |
| 338 | 342 | ||
| 343 | play_time_manager = std::make_unique<PlayTime::PlayTimeManager>(); | ||
| 344 | |||
| 339 | system->GetRoomNetwork().Init(); | 345 | system->GetRoomNetwork().Init(); |
| 340 | 346 | ||
| 341 | RegisterMetaTypes(); | 347 | RegisterMetaTypes(); |
| @@ -984,7 +990,7 @@ void GMainWindow::InitializeWidgets() { | |||
| 984 | render_window = new GRenderWindow(this, emu_thread.get(), input_subsystem, *system); | 990 | render_window = new GRenderWindow(this, emu_thread.get(), input_subsystem, *system); |
| 985 | render_window->hide(); | 991 | render_window->hide(); |
| 986 | 992 | ||
| 987 | game_list = new GameList(vfs, provider.get(), *system, this); | 993 | game_list = new GameList(vfs, provider.get(), *play_time_manager, *system, this); |
| 988 | ui->horizontalLayout->addWidget(game_list); | 994 | ui->horizontalLayout->addWidget(game_list); |
| 989 | 995 | ||
| 990 | game_list_placeholder = new GameListPlaceholder(this); | 996 | game_list_placeholder = new GameListPlaceholder(this); |
| @@ -1445,6 +1451,7 @@ void GMainWindow::OnAppFocusStateChanged(Qt::ApplicationState state) { | |||
| 1445 | Settings::values.audio_muted = false; | 1451 | Settings::values.audio_muted = false; |
| 1446 | auto_muted = false; | 1452 | auto_muted = false; |
| 1447 | } | 1453 | } |
| 1454 | UpdateVolumeUI(); | ||
| 1448 | } | 1455 | } |
| 1449 | } | 1456 | } |
| 1450 | 1457 | ||
| @@ -1458,6 +1465,8 @@ void GMainWindow::ConnectWidgetEvents() { | |||
| 1458 | connect(game_list, &GameList::RemoveInstalledEntryRequested, this, | 1465 | connect(game_list, &GameList::RemoveInstalledEntryRequested, this, |
| 1459 | &GMainWindow::OnGameListRemoveInstalledEntry); | 1466 | &GMainWindow::OnGameListRemoveInstalledEntry); |
| 1460 | connect(game_list, &GameList::RemoveFileRequested, this, &GMainWindow::OnGameListRemoveFile); | 1467 | connect(game_list, &GameList::RemoveFileRequested, this, &GMainWindow::OnGameListRemoveFile); |
| 1468 | connect(game_list, &GameList::RemovePlayTimeRequested, this, | ||
| 1469 | &GMainWindow::OnGameListRemovePlayTimeData); | ||
| 1461 | connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS); | 1470 | connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS); |
| 1462 | connect(game_list, &GameList::VerifyIntegrityRequested, this, | 1471 | connect(game_list, &GameList::VerifyIntegrityRequested, this, |
| 1463 | &GMainWindow::OnGameListVerifyIntegrity); | 1472 | &GMainWindow::OnGameListVerifyIntegrity); |
| @@ -1551,6 +1560,15 @@ void GMainWindow::ConnectMenuEvents() { | |||
| 1551 | // Tools | 1560 | // Tools |
| 1552 | connect_menu(ui->action_Rederive, std::bind(&GMainWindow::OnReinitializeKeys, this, | 1561 | connect_menu(ui->action_Rederive, std::bind(&GMainWindow::OnReinitializeKeys, this, |
| 1553 | ReinitializeKeyBehavior::Warning)); | 1562 | ReinitializeKeyBehavior::Warning)); |
| 1563 | connect_menu(ui->action_Load_Album, &GMainWindow::OnAlbum); | ||
| 1564 | connect_menu(ui->action_Load_Cabinet_Nickname_Owner, | ||
| 1565 | [this]() { OnCabinet(Service::NFP::CabinetMode::StartNicknameAndOwnerSettings); }); | ||
| 1566 | connect_menu(ui->action_Load_Cabinet_Eraser, | ||
| 1567 | [this]() { OnCabinet(Service::NFP::CabinetMode::StartGameDataEraser); }); | ||
| 1568 | connect_menu(ui->action_Load_Cabinet_Restorer, | ||
| 1569 | [this]() { OnCabinet(Service::NFP::CabinetMode::StartRestorer); }); | ||
| 1570 | connect_menu(ui->action_Load_Cabinet_Formatter, | ||
| 1571 | [this]() { OnCabinet(Service::NFP::CabinetMode::StartFormatter); }); | ||
| 1554 | connect_menu(ui->action_Load_Mii_Edit, &GMainWindow::OnMiiEdit); | 1572 | connect_menu(ui->action_Load_Mii_Edit, &GMainWindow::OnMiiEdit); |
| 1555 | connect_menu(ui->action_Capture_Screenshot, &GMainWindow::OnCaptureScreenshot); | 1573 | connect_menu(ui->action_Capture_Screenshot, &GMainWindow::OnCaptureScreenshot); |
| 1556 | 1574 | ||
| @@ -1568,6 +1586,7 @@ void GMainWindow::ConnectMenuEvents() { | |||
| 1568 | 1586 | ||
| 1569 | void GMainWindow::UpdateMenuState() { | 1587 | void GMainWindow::UpdateMenuState() { |
| 1570 | const bool is_paused = emu_thread == nullptr || !emu_thread->IsRunning(); | 1588 | const bool is_paused = emu_thread == nullptr || !emu_thread->IsRunning(); |
| 1589 | const bool is_firmware_available = CheckFirmwarePresence(); | ||
| 1571 | 1590 | ||
| 1572 | const std::array running_actions{ | 1591 | const std::array running_actions{ |
| 1573 | ui->action_Stop, | 1592 | ui->action_Stop, |
| @@ -1578,10 +1597,23 @@ void GMainWindow::UpdateMenuState() { | |||
| 1578 | ui->action_Pause, | 1597 | ui->action_Pause, |
| 1579 | }; | 1598 | }; |
| 1580 | 1599 | ||
| 1600 | const std::array applet_actions{ | ||
| 1601 | ui->action_Load_Album, | ||
| 1602 | ui->action_Load_Cabinet_Nickname_Owner, | ||
| 1603 | ui->action_Load_Cabinet_Eraser, | ||
| 1604 | ui->action_Load_Cabinet_Restorer, | ||
| 1605 | ui->action_Load_Cabinet_Formatter, | ||
| 1606 | ui->action_Load_Mii_Edit, | ||
| 1607 | }; | ||
| 1608 | |||
| 1581 | for (QAction* action : running_actions) { | 1609 | for (QAction* action : running_actions) { |
| 1582 | action->setEnabled(emulation_running); | 1610 | action->setEnabled(emulation_running); |
| 1583 | } | 1611 | } |
| 1584 | 1612 | ||
| 1613 | for (QAction* action : applet_actions) { | ||
| 1614 | action->setEnabled(is_firmware_available && !emulation_running); | ||
| 1615 | } | ||
| 1616 | |||
| 1585 | ui->action_Capture_Screenshot->setEnabled(emulation_running && !is_paused); | 1617 | ui->action_Capture_Screenshot->setEnabled(emulation_running && !is_paused); |
| 1586 | 1618 | ||
| 1587 | if (emulation_running && is_paused) { | 1619 | if (emulation_running && is_paused) { |
| @@ -1591,8 +1623,6 @@ void GMainWindow::UpdateMenuState() { | |||
| 1591 | } | 1623 | } |
| 1592 | 1624 | ||
| 1593 | multiplayer_state->UpdateNotificationStatus(); | 1625 | multiplayer_state->UpdateNotificationStatus(); |
| 1594 | |||
| 1595 | ui->action_Load_Mii_Edit->setEnabled(CheckFirmwarePresence()); | ||
| 1596 | } | 1626 | } |
| 1597 | 1627 | ||
| 1598 | void GMainWindow::OnDisplayTitleBars(bool show) { | 1628 | void GMainWindow::OnDisplayTitleBars(bool show) { |
| @@ -2103,6 +2133,8 @@ void GMainWindow::OnEmulationStopped() { | |||
| 2103 | OnTasStateChanged(); | 2133 | OnTasStateChanged(); |
| 2104 | render_window->FinalizeCamera(); | 2134 | render_window->FinalizeCamera(); |
| 2105 | 2135 | ||
| 2136 | system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::None); | ||
| 2137 | |||
| 2106 | // Enable all controllers | 2138 | // Enable all controllers |
| 2107 | system->HIDCore().SetSupportedStyleTag({Core::HID::NpadStyleSet::All}); | 2139 | system->HIDCore().SetSupportedStyleTag({Core::HID::NpadStyleSet::All}); |
| 2108 | 2140 | ||
| @@ -2511,6 +2543,17 @@ void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget targ | |||
| 2511 | } | 2543 | } |
| 2512 | } | 2544 | } |
| 2513 | 2545 | ||
| 2546 | void GMainWindow::OnGameListRemovePlayTimeData(u64 program_id) { | ||
| 2547 | if (QMessageBox::question(this, tr("Remove Play Time Data"), tr("Reset play time?"), | ||
| 2548 | QMessageBox::Yes | QMessageBox::No, | ||
| 2549 | QMessageBox::No) != QMessageBox::Yes) { | ||
| 2550 | return; | ||
| 2551 | } | ||
| 2552 | |||
| 2553 | play_time_manager->ResetProgramPlayTime(program_id); | ||
| 2554 | game_list->PopulateAsync(UISettings::values.game_dirs); | ||
| 2555 | } | ||
| 2556 | |||
| 2514 | void GMainWindow::RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target) { | 2557 | void GMainWindow::RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target) { |
| 2515 | const auto target_file_name = [target] { | 2558 | const auto target_file_name = [target] { |
| 2516 | switch (target) { | 2559 | switch (target) { |
| @@ -2802,7 +2845,6 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga | |||
| 2802 | const QStringList args = QApplication::arguments(); | 2845 | const QStringList args = QApplication::arguments(); |
| 2803 | std::filesystem::path yuzu_command = args[0].toStdString(); | 2846 | std::filesystem::path yuzu_command = args[0].toStdString(); |
| 2804 | 2847 | ||
| 2805 | #if defined(__linux__) || defined(__FreeBSD__) | ||
| 2806 | // If relative path, make it an absolute path | 2848 | // If relative path, make it an absolute path |
| 2807 | if (yuzu_command.c_str()[0] == '.') { | 2849 | if (yuzu_command.c_str()[0] == '.') { |
| 2808 | yuzu_command = Common::FS::GetCurrentDir() / yuzu_command; | 2850 | yuzu_command = Common::FS::GetCurrentDir() / yuzu_command; |
| @@ -2825,12 +2867,14 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga | |||
| 2825 | UISettings::values.shortcut_already_warned = true; | 2867 | UISettings::values.shortcut_already_warned = true; |
| 2826 | } | 2868 | } |
| 2827 | #endif // __linux__ | 2869 | #endif // __linux__ |
| 2828 | #endif // __linux__ || __FreeBSD__ | ||
| 2829 | 2870 | ||
| 2830 | std::filesystem::path target_directory{}; | 2871 | std::filesystem::path target_directory{}; |
| 2831 | // Determine target directory for shortcut | 2872 | // Determine target directory for shortcut |
| 2832 | #if defined(__linux__) || defined(__FreeBSD__) | 2873 | #if defined(WIN32) |
| 2874 | const char* home = std::getenv("USERPROFILE"); | ||
| 2875 | #else | ||
| 2833 | const char* home = std::getenv("HOME"); | 2876 | const char* home = std::getenv("HOME"); |
| 2877 | #endif | ||
| 2834 | const std::filesystem::path home_path = (home == nullptr ? "~" : home); | 2878 | const std::filesystem::path home_path = (home == nullptr ? "~" : home); |
| 2835 | const char* xdg_data_home = std::getenv("XDG_DATA_HOME"); | 2879 | const char* xdg_data_home = std::getenv("XDG_DATA_HOME"); |
| 2836 | 2880 | ||
| @@ -2840,7 +2884,7 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga | |||
| 2840 | QMessageBox::critical( | 2884 | QMessageBox::critical( |
| 2841 | this, tr("Create Shortcut"), | 2885 | this, tr("Create Shortcut"), |
| 2842 | tr("Cannot create shortcut on desktop. Path \"%1\" does not exist.") | 2886 | tr("Cannot create shortcut on desktop. Path \"%1\" does not exist.") |
| 2843 | .arg(QString::fromStdString(target_directory)), | 2887 | .arg(QString::fromStdString(target_directory.generic_string())), |
| 2844 | QMessageBox::StandardButton::Ok); | 2888 | QMessageBox::StandardButton::Ok); |
| 2845 | return; | 2889 | return; |
| 2846 | } | 2890 | } |
| @@ -2848,15 +2892,15 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga | |||
| 2848 | target_directory = (xdg_data_home == nullptr ? home_path / ".local/share" : xdg_data_home) / | 2892 | target_directory = (xdg_data_home == nullptr ? home_path / ".local/share" : xdg_data_home) / |
| 2849 | "applications"; | 2893 | "applications"; |
| 2850 | if (!Common::FS::CreateDirs(target_directory)) { | 2894 | if (!Common::FS::CreateDirs(target_directory)) { |
| 2851 | QMessageBox::critical(this, tr("Create Shortcut"), | 2895 | QMessageBox::critical( |
| 2852 | tr("Cannot create shortcut in applications menu. Path \"%1\" " | 2896 | this, tr("Create Shortcut"), |
| 2853 | "does not exist and cannot be created.") | 2897 | tr("Cannot create shortcut in applications menu. Path \"%1\" " |
| 2854 | .arg(QString::fromStdString(target_directory)), | 2898 | "does not exist and cannot be created.") |
| 2855 | QMessageBox::StandardButton::Ok); | 2899 | .arg(QString::fromStdString(target_directory.generic_string())), |
| 2900 | QMessageBox::StandardButton::Ok); | ||
| 2856 | return; | 2901 | return; |
| 2857 | } | 2902 | } |
| 2858 | } | 2903 | } |
| 2859 | #endif | ||
| 2860 | 2904 | ||
| 2861 | const std::string game_file_name = std::filesystem::path(game_path).filename().string(); | 2905 | const std::string game_file_name = std::filesystem::path(game_path).filename().string(); |
| 2862 | // Determine full paths for icon and shortcut | 2906 | // Determine full paths for icon and shortcut |
| @@ -2878,9 +2922,14 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga | |||
| 2878 | const std::filesystem::path shortcut_path = | 2922 | const std::filesystem::path shortcut_path = |
| 2879 | target_directory / (program_id == 0 ? fmt::format("yuzu-{}.desktop", game_file_name) | 2923 | target_directory / (program_id == 0 ? fmt::format("yuzu-{}.desktop", game_file_name) |
| 2880 | : fmt::format("yuzu-{:016X}.desktop", program_id)); | 2924 | : fmt::format("yuzu-{:016X}.desktop", program_id)); |
| 2925 | #elif defined(WIN32) | ||
| 2926 | std::filesystem::path icons_path = | ||
| 2927 | Common::FS::GetYuzuPathString(Common::FS::YuzuPath::IconsDir); | ||
| 2928 | std::filesystem::path icon_path = | ||
| 2929 | icons_path / ((program_id == 0 ? fmt::format("yuzu-{}.ico", game_file_name) | ||
| 2930 | : fmt::format("yuzu-{:016X}.ico", program_id))); | ||
| 2881 | #else | 2931 | #else |
| 2882 | const std::filesystem::path icon_path{}; | 2932 | std::string icon_extension; |
| 2883 | const std::filesystem::path shortcut_path{}; | ||
| 2884 | #endif | 2933 | #endif |
| 2885 | 2934 | ||
| 2886 | // Get title from game file | 2935 | // Get title from game file |
| @@ -2905,29 +2954,37 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga | |||
| 2905 | LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path); | 2954 | LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path); |
| 2906 | } | 2955 | } |
| 2907 | 2956 | ||
| 2908 | QImage icon_jpeg = | 2957 | QImage icon_data = |
| 2909 | QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size())); | 2958 | QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size())); |
| 2910 | #if defined(__linux__) || defined(__FreeBSD__) | 2959 | #if defined(__linux__) || defined(__FreeBSD__) |
| 2911 | // Convert and write the icon as a PNG | 2960 | // Convert and write the icon as a PNG |
| 2912 | if (!icon_jpeg.save(QString::fromStdString(icon_path.string()))) { | 2961 | if (!icon_data.save(QString::fromStdString(icon_path.string()))) { |
| 2913 | LOG_ERROR(Frontend, "Could not write icon as PNG to file"); | 2962 | LOG_ERROR(Frontend, "Could not write icon as PNG to file"); |
| 2914 | } else { | 2963 | } else { |
| 2915 | LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string()); | 2964 | LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string()); |
| 2916 | } | 2965 | } |
| 2966 | #elif defined(WIN32) | ||
| 2967 | if (!SaveIconToFile(icon_path.string(), icon_data)) { | ||
| 2968 | LOG_ERROR(Frontend, "Could not write icon to file"); | ||
| 2969 | return; | ||
| 2970 | } | ||
| 2917 | #endif // __linux__ | 2971 | #endif // __linux__ |
| 2918 | 2972 | ||
| 2919 | #if defined(__linux__) || defined(__FreeBSD__) | 2973 | #ifdef _WIN32 |
| 2974 | // Replace characters that are illegal in Windows filenames by a dash | ||
| 2975 | const std::string illegal_chars = "<>:\"/\\|?*"; | ||
| 2976 | for (char c : illegal_chars) { | ||
| 2977 | std::replace(title.begin(), title.end(), c, '_'); | ||
| 2978 | } | ||
| 2979 | const std::filesystem::path shortcut_path = target_directory / (title + ".lnk").c_str(); | ||
| 2980 | #endif | ||
| 2981 | |||
| 2920 | const std::string comment = | 2982 | const std::string comment = |
| 2921 | tr("Start %1 with the yuzu Emulator").arg(QString::fromStdString(title)).toStdString(); | 2983 | tr("Start %1 with the yuzu Emulator").arg(QString::fromStdString(title)).toStdString(); |
| 2922 | const std::string arguments = fmt::format("-g \"{:s}\"", game_path); | 2984 | const std::string arguments = fmt::format("-g \"{:s}\"", game_path); |
| 2923 | const std::string categories = "Game;Emulator;Qt;"; | 2985 | const std::string categories = "Game;Emulator;Qt;"; |
| 2924 | const std::string keywords = "Switch;Nintendo;"; | 2986 | const std::string keywords = "Switch;Nintendo;"; |
| 2925 | #else | 2987 | |
| 2926 | const std::string comment{}; | ||
| 2927 | const std::string arguments{}; | ||
| 2928 | const std::string categories{}; | ||
| 2929 | const std::string keywords{}; | ||
| 2930 | #endif | ||
| 2931 | if (!CreateShortcut(shortcut_path.string(), title, comment, icon_path.string(), | 2988 | if (!CreateShortcut(shortcut_path.string(), title, comment, icon_path.string(), |
| 2932 | yuzu_command.string(), arguments, categories, keywords)) { | 2989 | yuzu_command.string(), arguments, categories, keywords)) { |
| 2933 | QMessageBox::critical(this, tr("Create Shortcut"), | 2990 | QMessageBox::critical(this, tr("Create Shortcut"), |
| @@ -3334,6 +3391,9 @@ void GMainWindow::OnStartGame() { | |||
| 3334 | UpdateMenuState(); | 3391 | UpdateMenuState(); |
| 3335 | OnTasStateChanged(); | 3392 | OnTasStateChanged(); |
| 3336 | 3393 | ||
| 3394 | play_time_manager->SetProgramId(system->GetApplicationProcessProgramID()); | ||
| 3395 | play_time_manager->Start(); | ||
| 3396 | |||
| 3337 | discord_rpc->Update(); | 3397 | discord_rpc->Update(); |
| 3338 | } | 3398 | } |
| 3339 | 3399 | ||
| @@ -3349,6 +3409,7 @@ void GMainWindow::OnRestartGame() { | |||
| 3349 | 3409 | ||
| 3350 | void GMainWindow::OnPauseGame() { | 3410 | void GMainWindow::OnPauseGame() { |
| 3351 | emu_thread->SetRunning(false); | 3411 | emu_thread->SetRunning(false); |
| 3412 | play_time_manager->Stop(); | ||
| 3352 | UpdateMenuState(); | 3413 | UpdateMenuState(); |
| 3353 | AllowOSSleep(); | 3414 | AllowOSSleep(); |
| 3354 | } | 3415 | } |
| @@ -3369,6 +3430,9 @@ void GMainWindow::OnStopGame() { | |||
| 3369 | return; | 3430 | return; |
| 3370 | } | 3431 | } |
| 3371 | 3432 | ||
| 3433 | play_time_manager->Stop(); | ||
| 3434 | // Update game list to show new play time | ||
| 3435 | game_list->PopulateAsync(UISettings::values.game_dirs); | ||
| 3372 | if (OnShutdownBegin()) { | 3436 | if (OnShutdownBegin()) { |
| 3373 | OnShutdownBeginDialog(); | 3437 | OnShutdownBeginDialog(); |
| 3374 | } else { | 3438 | } else { |
| @@ -3942,6 +4006,34 @@ bool GMainWindow::CreateShortcut(const std::string& shortcut_path, const std::st | |||
| 3942 | shortcut_stream.close(); | 4006 | shortcut_stream.close(); |
| 3943 | 4007 | ||
| 3944 | return true; | 4008 | return true; |
| 4009 | #elif defined(WIN32) | ||
| 4010 | IShellLinkW* shell_link; | ||
| 4011 | auto hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW, | ||
| 4012 | (void**)&shell_link); | ||
| 4013 | if (FAILED(hres)) { | ||
| 4014 | return false; | ||
| 4015 | } | ||
| 4016 | shell_link->SetPath( | ||
| 4017 | Common::UTF8ToUTF16W(command).data()); // Path to the object we are referring to | ||
| 4018 | shell_link->SetArguments(Common::UTF8ToUTF16W(arguments).data()); | ||
| 4019 | shell_link->SetDescription(Common::UTF8ToUTF16W(comment).data()); | ||
| 4020 | shell_link->SetIconLocation(Common::UTF8ToUTF16W(icon_path).data(), 0); | ||
| 4021 | |||
| 4022 | IPersistFile* persist_file; | ||
| 4023 | hres = shell_link->QueryInterface(IID_IPersistFile, (void**)&persist_file); | ||
| 4024 | if (FAILED(hres)) { | ||
| 4025 | return false; | ||
| 4026 | } | ||
| 4027 | |||
| 4028 | hres = persist_file->Save(Common::UTF8ToUTF16W(shortcut_path).data(), TRUE); | ||
| 4029 | if (FAILED(hres)) { | ||
| 4030 | return false; | ||
| 4031 | } | ||
| 4032 | |||
| 4033 | persist_file->Release(); | ||
| 4034 | shell_link->Release(); | ||
| 4035 | |||
| 4036 | return true; | ||
| 3945 | #endif | 4037 | #endif |
| 3946 | return false; | 4038 | return false; |
| 3947 | } | 4039 | } |
| @@ -4134,6 +4226,53 @@ void GMainWindow::OnToggleStatusBar() { | |||
| 4134 | statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked()); | 4226 | statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked()); |
| 4135 | } | 4227 | } |
| 4136 | 4228 | ||
| 4229 | void GMainWindow::OnAlbum() { | ||
| 4230 | constexpr u64 AlbumId = 0x010000000000100Dull; | ||
| 4231 | auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); | ||
| 4232 | if (!bis_system) { | ||
| 4233 | QMessageBox::warning(this, tr("No firmware available"), | ||
| 4234 | tr("Please install the firmware to use the Album applet.")); | ||
| 4235 | return; | ||
| 4236 | } | ||
| 4237 | |||
| 4238 | auto album_nca = bis_system->GetEntry(AlbumId, FileSys::ContentRecordType::Program); | ||
| 4239 | if (!album_nca) { | ||
| 4240 | QMessageBox::warning(this, tr("Album Applet"), | ||
| 4241 | tr("Album applet is not available. Please reinstall firmware.")); | ||
| 4242 | return; | ||
| 4243 | } | ||
| 4244 | |||
| 4245 | system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::PhotoViewer); | ||
| 4246 | |||
| 4247 | const auto filename = QString::fromStdString(album_nca->GetFullPath()); | ||
| 4248 | UISettings::values.roms_path = QFileInfo(filename).path(); | ||
| 4249 | BootGame(filename); | ||
| 4250 | } | ||
| 4251 | |||
| 4252 | void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) { | ||
| 4253 | constexpr u64 CabinetId = 0x0100000000001002ull; | ||
| 4254 | auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); | ||
| 4255 | if (!bis_system) { | ||
| 4256 | QMessageBox::warning(this, tr("No firmware available"), | ||
| 4257 | tr("Please install the firmware to use the Cabinet applet.")); | ||
| 4258 | return; | ||
| 4259 | } | ||
| 4260 | |||
| 4261 | auto cabinet_nca = bis_system->GetEntry(CabinetId, FileSys::ContentRecordType::Program); | ||
| 4262 | if (!cabinet_nca) { | ||
| 4263 | QMessageBox::warning(this, tr("Cabinet Applet"), | ||
| 4264 | tr("Cabinet applet is not available. Please reinstall firmware.")); | ||
| 4265 | return; | ||
| 4266 | } | ||
| 4267 | |||
| 4268 | system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::Cabinet); | ||
| 4269 | system->GetAppletManager().SetCabinetMode(mode); | ||
| 4270 | |||
| 4271 | const auto filename = QString::fromStdString(cabinet_nca->GetFullPath()); | ||
| 4272 | UISettings::values.roms_path = QFileInfo(filename).path(); | ||
| 4273 | BootGame(filename); | ||
| 4274 | } | ||
| 4275 | |||
| 4137 | void GMainWindow::OnMiiEdit() { | 4276 | void GMainWindow::OnMiiEdit() { |
| 4138 | constexpr u64 MiiEditId = 0x0100000000001009ull; | 4277 | constexpr u64 MiiEditId = 0x0100000000001009ull; |
| 4139 | auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); | 4278 | auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); |
| @@ -4150,6 +4289,8 @@ void GMainWindow::OnMiiEdit() { | |||
| 4150 | return; | 4289 | return; |
| 4151 | } | 4290 | } |
| 4152 | 4291 | ||
| 4292 | system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::MiiEdit); | ||
| 4293 | |||
| 4153 | const auto filename = QString::fromStdString((mii_applet_nca->GetFullPath())); | 4294 | const auto filename = QString::fromStdString((mii_applet_nca->GetFullPath())); |
| 4154 | UISettings::values.roms_path = QFileInfo(filename).path(); | 4295 | UISettings::values.roms_path = QFileInfo(filename).path(); |
| 4155 | BootGame(filename); | 4296 | BootGame(filename); |
| @@ -4602,8 +4743,8 @@ bool GMainWindow::CheckFirmwarePresence() { | |||
| 4602 | 4743 | ||
| 4603 | bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, u64 program_id, | 4744 | bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, u64 program_id, |
| 4604 | u64* selected_title_id, u8* selected_content_record_type) { | 4745 | u64* selected_title_id, u8* selected_content_record_type) { |
| 4605 | using ContentInfo = std::pair<FileSys::TitleType, FileSys::ContentRecordType>; | 4746 | using ContentInfo = std::tuple<u64, FileSys::TitleType, FileSys::ContentRecordType>; |
| 4606 | boost::container::flat_map<u64, ContentInfo> available_title_ids; | 4747 | boost::container::flat_set<ContentInfo> available_title_ids; |
| 4607 | 4748 | ||
| 4608 | const auto RetrieveEntries = [&](FileSys::TitleType title_type, | 4749 | const auto RetrieveEntries = [&](FileSys::TitleType title_type, |
| 4609 | FileSys::ContentRecordType record_type) { | 4750 | FileSys::ContentRecordType record_type) { |
| @@ -4611,12 +4752,14 @@ bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installe | |||
| 4611 | for (const auto& entry : entries) { | 4752 | for (const auto& entry : entries) { |
| 4612 | if (FileSys::GetBaseTitleID(entry.title_id) == program_id && | 4753 | if (FileSys::GetBaseTitleID(entry.title_id) == program_id && |
| 4613 | installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success) { | 4754 | installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success) { |
| 4614 | available_title_ids[entry.title_id] = {title_type, record_type}; | 4755 | available_title_ids.insert({entry.title_id, title_type, record_type}); |
| 4615 | } | 4756 | } |
| 4616 | } | 4757 | } |
| 4617 | }; | 4758 | }; |
| 4618 | 4759 | ||
| 4619 | RetrieveEntries(FileSys::TitleType::Application, FileSys::ContentRecordType::Program); | 4760 | RetrieveEntries(FileSys::TitleType::Application, FileSys::ContentRecordType::Program); |
| 4761 | RetrieveEntries(FileSys::TitleType::Application, FileSys::ContentRecordType::HtmlDocument); | ||
| 4762 | RetrieveEntries(FileSys::TitleType::Application, FileSys::ContentRecordType::LegalInformation); | ||
| 4620 | RetrieveEntries(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); | 4763 | RetrieveEntries(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); |
| 4621 | 4764 | ||
| 4622 | if (available_title_ids.empty()) { | 4765 | if (available_title_ids.empty()) { |
| @@ -4627,10 +4770,14 @@ bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installe | |||
| 4627 | 4770 | ||
| 4628 | if (available_title_ids.size() > 1) { | 4771 | if (available_title_ids.size() > 1) { |
| 4629 | QStringList list; | 4772 | QStringList list; |
| 4630 | for (auto& [title_id, content_info] : available_title_ids) { | 4773 | for (auto& [title_id, title_type, record_type] : available_title_ids) { |
| 4631 | const auto hex_title_id = QString::fromStdString(fmt::format("{:X}", title_id)); | 4774 | const auto hex_title_id = QString::fromStdString(fmt::format("{:X}", title_id)); |
| 4632 | if (content_info.first == FileSys::TitleType::Application) { | 4775 | if (record_type == FileSys::ContentRecordType::Program) { |
| 4633 | list.push_back(QStringLiteral("Application [%1]").arg(hex_title_id)); | 4776 | list.push_back(QStringLiteral("Program [%1]").arg(hex_title_id)); |
| 4777 | } else if (record_type == FileSys::ContentRecordType::HtmlDocument) { | ||
| 4778 | list.push_back(QStringLiteral("HTML document [%1]").arg(hex_title_id)); | ||
| 4779 | } else if (record_type == FileSys::ContentRecordType::LegalInformation) { | ||
| 4780 | list.push_back(QStringLiteral("Legal information [%1]").arg(hex_title_id)); | ||
| 4634 | } else { | 4781 | } else { |
| 4635 | list.push_back( | 4782 | list.push_back( |
| 4636 | QStringLiteral("DLC %1 [%2]").arg(title_id & 0x7FF).arg(hex_title_id)); | 4783 | QStringLiteral("DLC %1 [%2]").arg(title_id & 0x7FF).arg(hex_title_id)); |
| @@ -4648,9 +4795,9 @@ bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installe | |||
| 4648 | title_index = list.indexOf(res); | 4795 | title_index = list.indexOf(res); |
| 4649 | } | 4796 | } |
| 4650 | 4797 | ||
| 4651 | const auto selected_info = available_title_ids.nth(title_index); | 4798 | const auto& [title_id, title_type, record_type] = *available_title_ids.nth(title_index); |
| 4652 | *selected_title_id = selected_info->first; | 4799 | *selected_title_id = title_id; |
| 4653 | *selected_content_record_type = static_cast<u8>(selected_info->second.second); | 4800 | *selected_content_record_type = static_cast<u8>(record_type); |
| 4654 | return true; | 4801 | return true; |
| 4655 | } | 4802 | } |
| 4656 | 4803 | ||
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index ba318eb11..2346eb3bd 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -81,6 +81,10 @@ namespace DiscordRPC { | |||
| 81 | class DiscordInterface; | 81 | class DiscordInterface; |
| 82 | } | 82 | } |
| 83 | 83 | ||
| 84 | namespace PlayTime { | ||
| 85 | class PlayTimeManager; | ||
| 86 | } | ||
| 87 | |||
| 84 | namespace FileSys { | 88 | namespace FileSys { |
| 85 | class ContentProvider; | 89 | class ContentProvider; |
| 86 | class ManualContentProvider; | 90 | class ManualContentProvider; |
| @@ -102,6 +106,10 @@ namespace Service::NFC { | |||
| 102 | class NfcDevice; | 106 | class NfcDevice; |
| 103 | } // namespace Service::NFC | 107 | } // namespace Service::NFC |
| 104 | 108 | ||
| 109 | namespace Service::NFP { | ||
| 110 | enum class CabinetMode : u8; | ||
| 111 | } // namespace Service::NFP | ||
| 112 | |||
| 105 | namespace Ui { | 113 | namespace Ui { |
| 106 | class MainWindow; | 114 | class MainWindow; |
| 107 | } | 115 | } |
| @@ -319,6 +327,7 @@ private slots: | |||
| 319 | void OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type); | 327 | void OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type); |
| 320 | void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target, | 328 | void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target, |
| 321 | const std::string& game_path); | 329 | const std::string& game_path); |
| 330 | void OnGameListRemovePlayTimeData(u64 program_id); | ||
| 322 | void OnGameListDumpRomFS(u64 program_id, const std::string& game_path, DumpRomFSTarget target); | 331 | void OnGameListDumpRomFS(u64 program_id, const std::string& game_path, DumpRomFSTarget target); |
| 323 | void OnGameListVerifyIntegrity(const std::string& game_path); | 332 | void OnGameListVerifyIntegrity(const std::string& game_path); |
| 324 | void OnGameListCopyTID(u64 program_id); | 333 | void OnGameListCopyTID(u64 program_id); |
| @@ -365,6 +374,8 @@ private slots: | |||
| 365 | void ResetWindowSize720(); | 374 | void ResetWindowSize720(); |
| 366 | void ResetWindowSize900(); | 375 | void ResetWindowSize900(); |
| 367 | void ResetWindowSize1080(); | 376 | void ResetWindowSize1080(); |
| 377 | void OnAlbum(); | ||
| 378 | void OnCabinet(Service::NFP::CabinetMode mode); | ||
| 368 | void OnMiiEdit(); | 379 | void OnMiiEdit(); |
| 369 | void OnCaptureScreenshot(); | 380 | void OnCaptureScreenshot(); |
| 370 | void OnReinitializeKeys(ReinitializeKeyBehavior behavior); | 381 | void OnReinitializeKeys(ReinitializeKeyBehavior behavior); |
| @@ -384,6 +395,7 @@ private: | |||
| 384 | void RemoveVulkanDriverPipelineCache(u64 program_id); | 395 | void RemoveVulkanDriverPipelineCache(u64 program_id); |
| 385 | void RemoveAllTransferableShaderCaches(u64 program_id); | 396 | void RemoveAllTransferableShaderCaches(u64 program_id); |
| 386 | void RemoveCustomConfiguration(u64 program_id, const std::string& game_path); | 397 | void RemoveCustomConfiguration(u64 program_id, const std::string& game_path); |
| 398 | void RemovePlayTimeData(u64 program_id); | ||
| 387 | void RemoveCacheStorage(u64 program_id); | 399 | void RemoveCacheStorage(u64 program_id); |
| 388 | bool SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id, | 400 | bool SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id, |
| 389 | u64* selected_title_id, u8* selected_content_record_type); | 401 | u64* selected_title_id, u8* selected_content_record_type); |
| @@ -423,6 +435,7 @@ private: | |||
| 423 | 435 | ||
| 424 | std::unique_ptr<Core::System> system; | 436 | std::unique_ptr<Core::System> system; |
| 425 | std::unique_ptr<DiscordRPC::DiscordInterface> discord_rpc; | 437 | std::unique_ptr<DiscordRPC::DiscordInterface> discord_rpc; |
| 438 | std::unique_ptr<PlayTime::PlayTimeManager> play_time_manager; | ||
| 426 | std::shared_ptr<InputCommon::InputSubsystem> input_subsystem; | 439 | std::shared_ptr<InputCommon::InputSubsystem> input_subsystem; |
| 427 | 440 | ||
| 428 | MultiplayerState* multiplayer_state = nullptr; | 441 | MultiplayerState* multiplayer_state = nullptr; |
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 91d6c5ef3..88684ffb5 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui | |||
| @@ -137,6 +137,15 @@ | |||
| 137 | <property name="title"> | 137 | <property name="title"> |
| 138 | <string>&Tools</string> | 138 | <string>&Tools</string> |
| 139 | </property> | 139 | </property> |
| 140 | <widget class="QMenu" name="menu_cabinet_applet"> | ||
| 141 | <property name="title"> | ||
| 142 | <string>&Amiibo</string> | ||
| 143 | </property> | ||
| 144 | <addaction name="action_Load_Cabinet_Nickname_Owner"/> | ||
| 145 | <addaction name="action_Load_Cabinet_Eraser"/> | ||
| 146 | <addaction name="action_Load_Cabinet_Restorer"/> | ||
| 147 | <addaction name="action_Load_Cabinet_Formatter"/> | ||
| 148 | </widget> | ||
| 140 | <widget class="QMenu" name="menuTAS"> | 149 | <widget class="QMenu" name="menuTAS"> |
| 141 | <property name="title"> | 150 | <property name="title"> |
| 142 | <string>&TAS</string> | 151 | <string>&TAS</string> |
| @@ -150,6 +159,8 @@ | |||
| 150 | <addaction name="action_Rederive"/> | 159 | <addaction name="action_Rederive"/> |
| 151 | <addaction name="action_Verify_installed_contents"/> | 160 | <addaction name="action_Verify_installed_contents"/> |
| 152 | <addaction name="separator"/> | 161 | <addaction name="separator"/> |
| 162 | <addaction name="menu_cabinet_applet"/> | ||
| 163 | <addaction name="action_Load_Album"/> | ||
| 153 | <addaction name="action_Load_Mii_Edit"/> | 164 | <addaction name="action_Load_Mii_Edit"/> |
| 154 | <addaction name="separator"/> | 165 | <addaction name="separator"/> |
| 155 | <addaction name="action_Capture_Screenshot"/> | 166 | <addaction name="action_Capture_Screenshot"/> |
| @@ -370,6 +381,31 @@ | |||
| 370 | <string>&Capture Screenshot</string> | 381 | <string>&Capture Screenshot</string> |
| 371 | </property> | 382 | </property> |
| 372 | </action> | 383 | </action> |
| 384 | <action name="action_Load_Album"> | ||
| 385 | <property name="text"> | ||
| 386 | <string>Open &Album</string> | ||
| 387 | </property> | ||
| 388 | </action> | ||
| 389 | <action name="action_Load_Cabinet_Nickname_Owner"> | ||
| 390 | <property name="text"> | ||
| 391 | <string>&Set Nickname and Owner</string> | ||
| 392 | </property> | ||
| 393 | </action> | ||
| 394 | <action name="action_Load_Cabinet_Eraser"> | ||
| 395 | <property name="text"> | ||
| 396 | <string>&Delete Game Data</string> | ||
| 397 | </property> | ||
| 398 | </action> | ||
| 399 | <action name="action_Load_Cabinet_Restorer"> | ||
| 400 | <property name="text"> | ||
| 401 | <string>&Restore Amiibo</string> | ||
| 402 | </property> | ||
| 403 | </action> | ||
| 404 | <action name="action_Load_Cabinet_Formatter"> | ||
| 405 | <property name="text"> | ||
| 406 | <string>&Format Amiibo</string> | ||
| 407 | </property> | ||
| 408 | </action> | ||
| 373 | <action name="action_Load_Mii_Edit"> | 409 | <action name="action_Load_Mii_Edit"> |
| 374 | <property name="text"> | 410 | <property name="text"> |
| 375 | <string>Open &Mii Editor</string> | 411 | <string>Open &Mii Editor</string> |
diff --git a/src/yuzu/play_time_manager.cpp b/src/yuzu/play_time_manager.cpp new file mode 100644 index 000000000..155c36b7d --- /dev/null +++ b/src/yuzu/play_time_manager.cpp | |||
| @@ -0,0 +1,179 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/alignment.h" | ||
| 5 | #include "common/fs/file.h" | ||
| 6 | #include "common/fs/fs.h" | ||
| 7 | #include "common/fs/path_util.h" | ||
| 8 | #include "common/logging/log.h" | ||
| 9 | #include "common/settings.h" | ||
| 10 | #include "common/thread.h" | ||
| 11 | #include "core/hle/service/acc/profile_manager.h" | ||
| 12 | #include "yuzu/play_time_manager.h" | ||
| 13 | |||
| 14 | namespace PlayTime { | ||
| 15 | |||
| 16 | namespace { | ||
| 17 | |||
| 18 | struct PlayTimeElement { | ||
| 19 | ProgramId program_id; | ||
| 20 | PlayTime play_time; | ||
| 21 | }; | ||
| 22 | |||
| 23 | std::optional<std::filesystem::path> GetCurrentUserPlayTimePath() { | ||
| 24 | const Service::Account::ProfileManager manager; | ||
| 25 | const auto uuid = manager.GetUser(static_cast<s32>(Settings::values.current_user)); | ||
| 26 | if (!uuid.has_value()) { | ||
| 27 | return std::nullopt; | ||
| 28 | } | ||
| 29 | return Common::FS::GetYuzuPath(Common::FS::YuzuPath::PlayTimeDir) / | ||
| 30 | uuid->RawString().append(".bin"); | ||
| 31 | } | ||
| 32 | |||
| 33 | [[nodiscard]] bool ReadPlayTimeFile(PlayTimeDatabase& out_play_time_db) { | ||
| 34 | const auto filename = GetCurrentUserPlayTimePath(); | ||
| 35 | |||
| 36 | if (!filename.has_value()) { | ||
| 37 | LOG_ERROR(Frontend, "Failed to get current user path"); | ||
| 38 | return false; | ||
| 39 | } | ||
| 40 | |||
| 41 | out_play_time_db.clear(); | ||
| 42 | |||
| 43 | if (Common::FS::Exists(filename.value())) { | ||
| 44 | Common::FS::IOFile file{filename.value(), Common::FS::FileAccessMode::Read, | ||
| 45 | Common::FS::FileType::BinaryFile}; | ||
| 46 | if (!file.IsOpen()) { | ||
| 47 | LOG_ERROR(Frontend, "Failed to open play time file: {}", | ||
| 48 | Common::FS::PathToUTF8String(filename.value())); | ||
| 49 | return false; | ||
| 50 | } | ||
| 51 | |||
| 52 | const size_t num_elements = file.GetSize() / sizeof(PlayTimeElement); | ||
| 53 | std::vector<PlayTimeElement> elements(num_elements); | ||
| 54 | |||
| 55 | if (file.ReadSpan<PlayTimeElement>(elements) != num_elements) { | ||
| 56 | return false; | ||
| 57 | } | ||
| 58 | |||
| 59 | for (const auto& [program_id, play_time] : elements) { | ||
| 60 | if (program_id != 0) { | ||
| 61 | out_play_time_db[program_id] = play_time; | ||
| 62 | } | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | return true; | ||
| 67 | } | ||
| 68 | |||
| 69 | [[nodiscard]] bool WritePlayTimeFile(const PlayTimeDatabase& play_time_db) { | ||
| 70 | const auto filename = GetCurrentUserPlayTimePath(); | ||
| 71 | |||
| 72 | if (!filename.has_value()) { | ||
| 73 | LOG_ERROR(Frontend, "Failed to get current user path"); | ||
| 74 | return false; | ||
| 75 | } | ||
| 76 | |||
| 77 | Common::FS::IOFile file{filename.value(), Common::FS::FileAccessMode::Write, | ||
| 78 | Common::FS::FileType::BinaryFile}; | ||
| 79 | if (!file.IsOpen()) { | ||
| 80 | LOG_ERROR(Frontend, "Failed to open play time file: {}", | ||
| 81 | Common::FS::PathToUTF8String(filename.value())); | ||
| 82 | return false; | ||
| 83 | } | ||
| 84 | |||
| 85 | std::vector<PlayTimeElement> elements; | ||
| 86 | elements.reserve(play_time_db.size()); | ||
| 87 | |||
| 88 | for (auto& [program_id, play_time] : play_time_db) { | ||
| 89 | if (program_id != 0) { | ||
| 90 | elements.push_back(PlayTimeElement{program_id, play_time}); | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | return file.WriteSpan<PlayTimeElement>(elements) == elements.size(); | ||
| 95 | } | ||
| 96 | |||
| 97 | } // namespace | ||
| 98 | |||
| 99 | PlayTimeManager::PlayTimeManager() { | ||
| 100 | if (!ReadPlayTimeFile(database)) { | ||
| 101 | LOG_ERROR(Frontend, "Failed to read play time database! Resetting to default."); | ||
| 102 | } | ||
| 103 | } | ||
| 104 | |||
| 105 | PlayTimeManager::~PlayTimeManager() { | ||
| 106 | Save(); | ||
| 107 | } | ||
| 108 | |||
| 109 | void PlayTimeManager::SetProgramId(u64 program_id) { | ||
| 110 | running_program_id = program_id; | ||
| 111 | } | ||
| 112 | |||
| 113 | void PlayTimeManager::Start() { | ||
| 114 | play_time_thread = std::jthread([&](std::stop_token stop_token) { AutoTimestamp(stop_token); }); | ||
| 115 | } | ||
| 116 | |||
| 117 | void PlayTimeManager::Stop() { | ||
| 118 | play_time_thread = {}; | ||
| 119 | } | ||
| 120 | |||
| 121 | void PlayTimeManager::AutoTimestamp(std::stop_token stop_token) { | ||
| 122 | Common::SetCurrentThreadName("PlayTimeReport"); | ||
| 123 | |||
| 124 | using namespace std::literals::chrono_literals; | ||
| 125 | using std::chrono::seconds; | ||
| 126 | using std::chrono::steady_clock; | ||
| 127 | |||
| 128 | auto timestamp = steady_clock::now(); | ||
| 129 | |||
| 130 | const auto GetDuration = [&]() -> u64 { | ||
| 131 | const auto last_timestamp = std::exchange(timestamp, steady_clock::now()); | ||
| 132 | const auto duration = std::chrono::duration_cast<seconds>(timestamp - last_timestamp); | ||
| 133 | return static_cast<u64>(duration.count()); | ||
| 134 | }; | ||
| 135 | |||
| 136 | while (!stop_token.stop_requested()) { | ||
| 137 | Common::StoppableTimedWait(stop_token, 30s); | ||
| 138 | |||
| 139 | database[running_program_id] += GetDuration(); | ||
| 140 | Save(); | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | void PlayTimeManager::Save() { | ||
| 145 | if (!WritePlayTimeFile(database)) { | ||
| 146 | LOG_ERROR(Frontend, "Failed to update play time database!"); | ||
| 147 | } | ||
| 148 | } | ||
| 149 | |||
| 150 | u64 PlayTimeManager::GetPlayTime(u64 program_id) const { | ||
| 151 | auto it = database.find(program_id); | ||
| 152 | if (it != database.end()) { | ||
| 153 | return it->second; | ||
| 154 | } else { | ||
| 155 | return 0; | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | void PlayTimeManager::ResetProgramPlayTime(u64 program_id) { | ||
| 160 | database.erase(program_id); | ||
| 161 | Save(); | ||
| 162 | } | ||
| 163 | |||
| 164 | QString ReadablePlayTime(qulonglong time_seconds) { | ||
| 165 | if (time_seconds == 0) { | ||
| 166 | return {}; | ||
| 167 | } | ||
| 168 | const auto time_minutes = std::max(static_cast<double>(time_seconds) / 60, 1.0); | ||
| 169 | const auto time_hours = static_cast<double>(time_seconds) / 3600; | ||
| 170 | const bool is_minutes = time_minutes < 60; | ||
| 171 | const char* unit = is_minutes ? "m" : "h"; | ||
| 172 | const auto value = is_minutes ? time_minutes : time_hours; | ||
| 173 | |||
| 174 | return QStringLiteral("%L1 %2") | ||
| 175 | .arg(value, 0, 'f', !is_minutes && time_seconds % 60 != 0) | ||
| 176 | .arg(QString::fromUtf8(unit)); | ||
| 177 | } | ||
| 178 | |||
| 179 | } // namespace PlayTime | ||
diff --git a/src/yuzu/play_time_manager.h b/src/yuzu/play_time_manager.h new file mode 100644 index 000000000..5f96f3447 --- /dev/null +++ b/src/yuzu/play_time_manager.h | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <QString> | ||
| 7 | |||
| 8 | #include <map> | ||
| 9 | |||
| 10 | #include "common/common_funcs.h" | ||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "common/polyfill_thread.h" | ||
| 13 | |||
| 14 | namespace PlayTime { | ||
| 15 | |||
| 16 | using ProgramId = u64; | ||
| 17 | using PlayTime = u64; | ||
| 18 | using PlayTimeDatabase = std::map<ProgramId, PlayTime>; | ||
| 19 | |||
| 20 | class PlayTimeManager { | ||
| 21 | public: | ||
| 22 | explicit PlayTimeManager(); | ||
| 23 | ~PlayTimeManager(); | ||
| 24 | |||
| 25 | YUZU_NON_COPYABLE(PlayTimeManager); | ||
| 26 | YUZU_NON_MOVEABLE(PlayTimeManager); | ||
| 27 | |||
| 28 | u64 GetPlayTime(u64 program_id) const; | ||
| 29 | void ResetProgramPlayTime(u64 program_id); | ||
| 30 | void SetProgramId(u64 program_id); | ||
| 31 | void Start(); | ||
| 32 | void Stop(); | ||
| 33 | |||
| 34 | private: | ||
| 35 | PlayTimeDatabase database; | ||
| 36 | u64 running_program_id; | ||
| 37 | std::jthread play_time_thread; | ||
| 38 | void AutoTimestamp(std::stop_token stop_token); | ||
| 39 | void Save(); | ||
| 40 | }; | ||
| 41 | |||
| 42 | QString ReadablePlayTime(qulonglong time_seconds); | ||
| 43 | |||
| 44 | } // namespace PlayTime | ||
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index 8efd63f31..975008159 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h | |||
| @@ -103,7 +103,7 @@ struct Values { | |||
| 103 | true, | 103 | true, |
| 104 | true}; | 104 | true}; |
| 105 | Setting<bool> mute_when_in_background{ | 105 | Setting<bool> mute_when_in_background{ |
| 106 | linkage, false, "muteWhenInBackground", Category::Ui, Settings::Specialization::Default, | 106 | linkage, false, "muteWhenInBackground", Category::Audio, Settings::Specialization::Default, |
| 107 | true, true}; | 107 | true, true}; |
| 108 | Setting<bool> hide_mouse{ | 108 | Setting<bool> hide_mouse{ |
| 109 | linkage, true, "hideInactiveMouse", Category::UiGeneral, Settings::Specialization::Default, | 109 | linkage, true, "hideInactiveMouse", Category::UiGeneral, Settings::Specialization::Default, |
| @@ -183,6 +183,9 @@ struct Values { | |||
| 183 | Setting<bool> show_size{linkage, true, "show_size", Category::UiGameList}; | 183 | Setting<bool> show_size{linkage, true, "show_size", Category::UiGameList}; |
| 184 | Setting<bool> show_types{linkage, true, "show_types", Category::UiGameList}; | 184 | Setting<bool> show_types{linkage, true, "show_types", Category::UiGameList}; |
| 185 | 185 | ||
| 186 | // Play time | ||
| 187 | Setting<bool> show_play_time{linkage, true, "show_play_time", Category::UiGameList}; | ||
| 188 | |||
| 186 | bool configuration_applied; | 189 | bool configuration_applied; |
| 187 | bool reset_to_defaults; | 190 | bool reset_to_defaults; |
| 188 | bool shortcut_already_warned{false}; | 191 | bool shortcut_already_warned{false}; |
diff --git a/src/yuzu/util/util.cpp b/src/yuzu/util/util.cpp index 5c3e4589e..f2854c8ec 100644 --- a/src/yuzu/util/util.cpp +++ b/src/yuzu/util/util.cpp | |||
| @@ -5,6 +5,10 @@ | |||
| 5 | #include <cmath> | 5 | #include <cmath> |
| 6 | #include <QPainter> | 6 | #include <QPainter> |
| 7 | #include "yuzu/util/util.h" | 7 | #include "yuzu/util/util.h" |
| 8 | #ifdef _WIN32 | ||
| 9 | #include <windows.h> | ||
| 10 | #include "common/fs/file.h" | ||
| 11 | #endif | ||
| 8 | 12 | ||
| 9 | QFont GetMonospaceFont() { | 13 | QFont GetMonospaceFont() { |
| 10 | QFont font(QStringLiteral("monospace")); | 14 | QFont font(QStringLiteral("monospace")); |
| @@ -37,3 +41,101 @@ QPixmap CreateCirclePixmapFromColor(const QColor& color) { | |||
| 37 | painter.drawEllipse({circle_pixmap.width() / 2.0, circle_pixmap.height() / 2.0}, 7.0, 7.0); | 41 | painter.drawEllipse({circle_pixmap.width() / 2.0, circle_pixmap.height() / 2.0}, 7.0, 7.0); |
| 38 | return circle_pixmap; | 42 | return circle_pixmap; |
| 39 | } | 43 | } |
| 44 | |||
| 45 | bool SaveIconToFile(const std::string_view path, const QImage& image) { | ||
| 46 | #if defined(WIN32) | ||
| 47 | #pragma pack(push, 2) | ||
| 48 | struct IconDir { | ||
| 49 | WORD id_reserved; | ||
| 50 | WORD id_type; | ||
| 51 | WORD id_count; | ||
| 52 | }; | ||
| 53 | |||
| 54 | struct IconDirEntry { | ||
| 55 | BYTE width; | ||
| 56 | BYTE height; | ||
| 57 | BYTE color_count; | ||
| 58 | BYTE reserved; | ||
| 59 | WORD planes; | ||
| 60 | WORD bit_count; | ||
| 61 | DWORD bytes_in_res; | ||
| 62 | DWORD image_offset; | ||
| 63 | }; | ||
| 64 | #pragma pack(pop) | ||
| 65 | |||
| 66 | const QImage source_image = image.convertToFormat(QImage::Format_RGB32); | ||
| 67 | constexpr std::array<int, 7> scale_sizes{256, 128, 64, 48, 32, 24, 16}; | ||
| 68 | constexpr int bytes_per_pixel = 4; | ||
| 69 | |||
| 70 | const IconDir icon_dir{ | ||
| 71 | .id_reserved = 0, | ||
| 72 | .id_type = 1, | ||
| 73 | .id_count = static_cast<WORD>(scale_sizes.size()), | ||
| 74 | }; | ||
| 75 | |||
| 76 | Common::FS::IOFile icon_file(path, Common::FS::FileAccessMode::Write, | ||
| 77 | Common::FS::FileType::BinaryFile); | ||
| 78 | if (!icon_file.IsOpen()) { | ||
| 79 | return false; | ||
| 80 | } | ||
| 81 | |||
| 82 | if (!icon_file.Write(icon_dir)) { | ||
| 83 | return false; | ||
| 84 | } | ||
| 85 | |||
| 86 | std::size_t image_offset = sizeof(IconDir) + (sizeof(IconDirEntry) * scale_sizes.size()); | ||
| 87 | for (std::size_t i = 0; i < scale_sizes.size(); i++) { | ||
| 88 | const int image_size = scale_sizes[i] * scale_sizes[i] * bytes_per_pixel; | ||
| 89 | const IconDirEntry icon_entry{ | ||
| 90 | .width = static_cast<BYTE>(scale_sizes[i]), | ||
| 91 | .height = static_cast<BYTE>(scale_sizes[i]), | ||
| 92 | .color_count = 0, | ||
| 93 | .reserved = 0, | ||
| 94 | .planes = 1, | ||
| 95 | .bit_count = bytes_per_pixel * 8, | ||
| 96 | .bytes_in_res = static_cast<DWORD>(sizeof(BITMAPINFOHEADER) + image_size), | ||
| 97 | .image_offset = static_cast<DWORD>(image_offset), | ||
| 98 | }; | ||
| 99 | image_offset += icon_entry.bytes_in_res; | ||
| 100 | if (!icon_file.Write(icon_entry)) { | ||
| 101 | return false; | ||
| 102 | } | ||
| 103 | } | ||
| 104 | |||
| 105 | for (std::size_t i = 0; i < scale_sizes.size(); i++) { | ||
| 106 | const QImage scaled_image = source_image.scaled( | ||
| 107 | scale_sizes[i], scale_sizes[i], Qt::IgnoreAspectRatio, Qt::SmoothTransformation); | ||
| 108 | const BITMAPINFOHEADER info_header{ | ||
| 109 | .biSize = sizeof(BITMAPINFOHEADER), | ||
| 110 | .biWidth = scaled_image.width(), | ||
| 111 | .biHeight = scaled_image.height() * 2, | ||
| 112 | .biPlanes = 1, | ||
| 113 | .biBitCount = bytes_per_pixel * 8, | ||
| 114 | .biCompression = BI_RGB, | ||
| 115 | .biSizeImage{}, | ||
| 116 | .biXPelsPerMeter{}, | ||
| 117 | .biYPelsPerMeter{}, | ||
| 118 | .biClrUsed{}, | ||
| 119 | .biClrImportant{}, | ||
| 120 | }; | ||
| 121 | |||
| 122 | if (!icon_file.Write(info_header)) { | ||
| 123 | return false; | ||
| 124 | } | ||
| 125 | |||
| 126 | for (int y = 0; y < scaled_image.height(); y++) { | ||
| 127 | const auto* line = scaled_image.scanLine(scaled_image.height() - 1 - y); | ||
| 128 | std::vector<u8> line_data(scaled_image.width() * bytes_per_pixel); | ||
| 129 | std::memcpy(line_data.data(), line, line_data.size()); | ||
| 130 | if (!icon_file.Write(line_data)) { | ||
| 131 | return false; | ||
| 132 | } | ||
| 133 | } | ||
| 134 | } | ||
| 135 | icon_file.Close(); | ||
| 136 | |||
| 137 | return true; | ||
| 138 | #else | ||
| 139 | return false; | ||
| 140 | #endif | ||
| 141 | } | ||
diff --git a/src/yuzu/util/util.h b/src/yuzu/util/util.h index 39dd2d895..09c14ce3f 100644 --- a/src/yuzu/util/util.h +++ b/src/yuzu/util/util.h | |||
| @@ -7,14 +7,22 @@ | |||
| 7 | #include <QString> | 7 | #include <QString> |
| 8 | 8 | ||
| 9 | /// Returns a QFont object appropriate to use as a monospace font for debugging widgets, etc. | 9 | /// Returns a QFont object appropriate to use as a monospace font for debugging widgets, etc. |
| 10 | QFont GetMonospaceFont(); | 10 | [[nodiscard]] QFont GetMonospaceFont(); |
| 11 | 11 | ||
| 12 | /// Convert a size in bytes into a readable format (KiB, MiB, etc.) | 12 | /// Convert a size in bytes into a readable format (KiB, MiB, etc.) |
| 13 | QString ReadableByteSize(qulonglong size); | 13 | [[nodiscard]] QString ReadableByteSize(qulonglong size); |
| 14 | 14 | ||
| 15 | /** | 15 | /** |
| 16 | * Creates a circle pixmap from a specified color | 16 | * Creates a circle pixmap from a specified color |
| 17 | * @param color The color the pixmap shall have | 17 | * @param color The color the pixmap shall have |
| 18 | * @return QPixmap circle pixmap | 18 | * @return QPixmap circle pixmap |
| 19 | */ | 19 | */ |
| 20 | QPixmap CreateCirclePixmapFromColor(const QColor& color); | 20 | [[nodiscard]] QPixmap CreateCirclePixmapFromColor(const QColor& color); |
| 21 | |||
| 22 | /** | ||
| 23 | * Saves a windows icon to a file | ||
| 24 | * @param path The icons path | ||
| 25 | * @param image The image to save | ||
| 26 | * @return bool If the operation succeeded | ||
| 27 | */ | ||
| 28 | [[nodiscard]] bool SaveIconToFile(const std::string_view path, const QImage& image); | ||